├── gui
├── core
│ ├── anirem.ico
│ ├── anirem-core.qrc
│ ├── qpmx.json
│ ├── aniremsettingsviewmodel.h
│ ├── aniremapp.h
│ ├── imageloader.h
│ ├── datasyncsettingsviewmodel.h
│ ├── aniremsettingsviewmodel.cpp
│ ├── detailsviewmodel.h
│ ├── entryviewmodel.h
│ ├── core.pro
│ ├── datasyncsettingsviewmodel.cpp
│ ├── loginviewmodel.h
│ ├── proxerentrymodel.h
│ ├── addanimeviewmodel.h
│ ├── entryviewmodel.cpp
│ ├── mainviewmodel.h
│ ├── detailsviewmodel.cpp
│ ├── imageloader.cpp
│ └── loginviewmodel.cpp
├── quick
│ ├── openssl
│ │ ├── .gitignore
│ │ └── get_openssl.sh
│ ├── qt.conf
│ ├── anirem-android.qrc
│ ├── android
│ │ ├── res
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── splash.9.png
│ │ │ │ └── ic_notification.png
│ │ │ ├── drawable-mdpi
│ │ │ │ ├── splash.9.png
│ │ │ │ └── ic_notification.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ ├── splash.9.png
│ │ │ │ └── ic_notification.png
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_foreground.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_foreground.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ ├── splash.9.png
│ │ │ │ └── ic_notification.png
│ │ │ ├── drawable-xxxhdpi
│ │ │ │ ├── splash.9.png
│ │ │ │ └── ic_notification.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_foreground.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_foreground.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_foreground.png
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values
│ │ │ │ ├── colors.xml
│ │ │ │ ├── styles.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── libs.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ └── ic_launcher.xml
│ │ │ └── values-de
│ │ │ │ └── strings.xml
│ │ ├── src
│ │ │ └── de
│ │ │ │ └── skycoder42
│ │ │ │ └── anirem
│ │ │ │ ├── Globals.java
│ │ │ │ ├── BootReceiver.java
│ │ │ │ ├── AniremActivity.java
│ │ │ │ └── AniremService.java
│ │ └── build.gradle
│ ├── App.qml
│ ├── qtquickcontrols2.conf
│ ├── icons
│ │ ├── ic_add_white_24px.svg
│ │ ├── ic_check_white_24px.svg
│ │ ├── ic_filter_list_white_24px.svg
│ │ ├── ic_arrow_back_white_24px.svg
│ │ ├── ic_playlist_add_white_24px.svg
│ │ ├── ic_open_in_browser_white_24px.svg
│ │ ├── ic_content_paste_white_24px.svg
│ │ ├── ic_autorenew_white_24px.svg
│ │ ├── ic_text_fields_white_24px.svg
│ │ └── ic_delete_forever_white_24px.svg
│ ├── qtquickcontrols2_android.conf
│ ├── qtquickcontrols2_win.conf
│ ├── qpmx.json
│ ├── qmltoast.h
│ ├── anirem_de.ts
│ ├── qmltoast.cpp
│ ├── proxerimageprovider.h
│ ├── AnimeHistoryDelegate.qml
│ ├── EntryView.qml
│ ├── anirem-quick.qrc
│ ├── SubButton.qml
│ ├── AnimeInfoDelegate.qml
│ ├── proxerimageprovider.cpp
│ ├── DetailsView.qml
│ ├── AddAnimeDialog.qml
│ ├── LoginDialog.qml
│ ├── quick.pro
│ └── main.cpp
├── widgets
│ ├── installer
│ │ ├── main.png
│ │ ├── meta
│ │ │ └── package.xml
│ │ ├── anirem_de.ts
│ │ └── config.xml
│ ├── resources
│ │ ├── bell.ico
│ │ ├── cross.ico
│ │ ├── plus.ico
│ │ ├── world_go.ico
│ │ ├── bell_valid.ico
│ │ ├── ajax-loader.gif
│ │ ├── arrow_refresh.ico
│ │ ├── paste_plain.ico
│ │ ├── textfield_add.ico
│ │ ├── anirem_inverted.ico
│ │ ├── page_white_copy.ico
│ │ └── scroll_pane_list_add.ico
│ ├── icons
│ │ └── hicolor
│ │ │ ├── 128x128
│ │ │ └── apps
│ │ │ │ └── anirem.png
│ │ │ ├── 16x16
│ │ │ └── apps
│ │ │ │ └── anirem.png
│ │ │ ├── 192x192
│ │ │ └── apps
│ │ │ │ └── anirem.png
│ │ │ ├── 22x22
│ │ │ └── apps
│ │ │ │ └── anirem.png
│ │ │ ├── 24x24
│ │ │ └── apps
│ │ │ │ └── anirem.png
│ │ │ ├── 256x256
│ │ │ └── apps
│ │ │ │ └── anirem.png
│ │ │ ├── 32x32
│ │ │ └── apps
│ │ │ │ └── anirem.png
│ │ │ ├── 48x48
│ │ │ └── apps
│ │ │ │ └── anirem.png
│ │ │ ├── 512x512
│ │ │ └── apps
│ │ │ │ └── anirem.png
│ │ │ ├── 64x64
│ │ │ └── apps
│ │ │ │ └── anirem.png
│ │ │ ├── 72x72
│ │ │ └── apps
│ │ │ │ └── anirem.png
│ │ │ └── 96x96
│ │ │ └── apps
│ │ │ └── anirem.png
│ ├── instancesetup.h
│ ├── animemodel.h
│ ├── logindialog.h
│ ├── entrydialog.h
│ ├── anirem.desktop
│ ├── anirem_de.ts
│ ├── detailsdockwidget.h
│ ├── qpmx.json
│ ├── anirem-widgets.qrc
│ ├── widgetsupdatenotifier.h
│ ├── addanimedialog.h
│ ├── entrydialog.cpp
│ ├── logindialog.cpp
│ ├── mainwindow.h
│ ├── animemodel.cpp
│ ├── main.cpp
│ ├── detailsdockwidget.cpp
│ ├── widgetsupdatenotifier.cpp
│ ├── entrydialog.ui
│ ├── addanimedialog.cpp
│ └── detailsdockwidget.ui
└── gui.pro
├── ci
├── anirem.aks
├── proxer.data
├── appveyor_init.bat
├── appveyor_postbuild.bat
├── travis_init.sh
├── travis_deploy.sh
├── pkgbuild.sh
└── travis_postbuild.sh
├── icn
├── anirem.ico
├── anirem.icns
├── store_fn.png
├── store_ic.png
├── anirem_white.svg
└── anirem.svg
├── img
├── desktop_add.png
├── mobile_add.png
├── mobile_main.png
├── desktop_main.png
└── mobile_detail.png
├── droid-service
├── android
│ ├── res
│ │ ├── drawable-hdpi
│ │ │ ├── splash.9.png
│ │ │ └── ic_notification.png
│ │ ├── drawable-mdpi
│ │ │ ├── splash.9.png
│ │ │ └── ic_notification.png
│ │ ├── drawable-xhdpi
│ │ │ ├── splash.9.png
│ │ │ └── ic_notification.png
│ │ ├── drawable-xxhdpi
│ │ │ ├── splash.9.png
│ │ │ └── ic_notification.png
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_foreground.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_foreground.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_foreground.png
│ │ ├── drawable-xxxhdpi
│ │ │ ├── splash.9.png
│ │ │ └── ic_notification.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_foreground.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_foreground.png
│ │ ├── values
│ │ │ ├── colors.xml
│ │ │ ├── styles.xml
│ │ │ ├── strings.xml
│ │ │ └── libs.xml
│ │ ├── mipmap-anydpi-v26
│ │ │ └── ic_launcher.xml
│ │ └── values-de
│ │ │ └── strings.xml
│ ├── src
│ │ └── de
│ │ │ └── skycoder42
│ │ │ └── anirem
│ │ │ ├── Globals.java
│ │ │ ├── AniremService.java
│ │ │ ├── BootReceiver.java
│ │ │ └── AniremActivity.java
│ └── build.gradle
├── droid-service.pro
├── starthelper.h
├── androidupdatenotifier.h
├── androidupdatenotifier.cpp
├── main.cpp
└── starthelper.cpp
├── lib
├── syncedsettings.xml
├── api
│ ├── proxerloginvalue.xml
│ ├── proxernamevalue.xml
│ ├── proxerstatus.xml
│ ├── proxerentry.xml
│ ├── proxerlogin.xml
│ ├── proxerlist.xml
│ ├── proxernames.xml
│ ├── proxerrelations.xml
│ ├── proxerentryvalue.xml
│ ├── proxerlistvalue.xml
│ ├── proxerapi.xml
│ ├── infoclass.xml
│ └── userclass.xml
├── lib_anirem_global.h
├── qpmx.json
├── apihelper.h
├── libanirem.h
├── jsonseasondataconverter.h
├── storedcookiejar.h
├── iupdatenotifier.h
├── localsettings.xml
├── passiveupdater.h
├── seasonstatusloader.h
├── apihelper.cpp
├── jsonseasondataconverter.cpp
├── storedcookiejar.cpp
├── passiveupdater.cpp
├── animeinfo.h
├── libanirem.cpp
└── lib.pro
├── lib.pri
├── AniRem.pro
├── appveyor.yml
├── .gitignore
├── LICENSE
├── .qmake.conf
└── .travis.yml
/gui/core/anirem.ico:
--------------------------------------------------------------------------------
1 | ../../icn/anirem.ico
--------------------------------------------------------------------------------
/gui/quick/openssl/.gitignore:
--------------------------------------------------------------------------------
1 | openssl*
2 |
--------------------------------------------------------------------------------
/gui/quick/qt.conf:
--------------------------------------------------------------------------------
1 | [Paths]
2 | Translations=assets:/translations
3 |
--------------------------------------------------------------------------------
/ci/anirem.aks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/ci/anirem.aks
--------------------------------------------------------------------------------
/ci/proxer.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/ci/proxer.data
--------------------------------------------------------------------------------
/icn/anirem.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/icn/anirem.ico
--------------------------------------------------------------------------------
/icn/anirem.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/icn/anirem.icns
--------------------------------------------------------------------------------
/icn/store_fn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/icn/store_fn.png
--------------------------------------------------------------------------------
/icn/store_ic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/icn/store_ic.png
--------------------------------------------------------------------------------
/img/desktop_add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/img/desktop_add.png
--------------------------------------------------------------------------------
/img/mobile_add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/img/mobile_add.png
--------------------------------------------------------------------------------
/img/mobile_main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/img/mobile_main.png
--------------------------------------------------------------------------------
/img/desktop_main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/img/desktop_main.png
--------------------------------------------------------------------------------
/img/mobile_detail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/img/mobile_detail.png
--------------------------------------------------------------------------------
/gui/widgets/installer/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/installer/main.png
--------------------------------------------------------------------------------
/gui/widgets/resources/bell.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/resources/bell.ico
--------------------------------------------------------------------------------
/gui/widgets/resources/cross.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/resources/cross.ico
--------------------------------------------------------------------------------
/gui/widgets/resources/plus.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/resources/plus.ico
--------------------------------------------------------------------------------
/gui/widgets/resources/world_go.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/resources/world_go.ico
--------------------------------------------------------------------------------
/ci/appveyor_init.bat:
--------------------------------------------------------------------------------
1 | 7z x "-p%PROXER_DATA%" -o. ".\ci\proxer.data" > nul
2 | echo CONFIG += create_installer >> install.pri
3 |
--------------------------------------------------------------------------------
/gui/widgets/resources/bell_valid.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/resources/bell_valid.ico
--------------------------------------------------------------------------------
/gui/widgets/resources/ajax-loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/resources/ajax-loader.gif
--------------------------------------------------------------------------------
/gui/widgets/resources/arrow_refresh.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/resources/arrow_refresh.ico
--------------------------------------------------------------------------------
/gui/widgets/resources/paste_plain.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/resources/paste_plain.ico
--------------------------------------------------------------------------------
/gui/widgets/resources/textfield_add.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/resources/textfield_add.ico
--------------------------------------------------------------------------------
/gui/widgets/resources/anirem_inverted.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/resources/anirem_inverted.ico
--------------------------------------------------------------------------------
/gui/widgets/resources/page_white_copy.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/resources/page_white_copy.ico
--------------------------------------------------------------------------------
/gui/quick/anirem-android.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | qt.conf
4 |
5 |
6 |
--------------------------------------------------------------------------------
/gui/widgets/resources/scroll_pane_list_add.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/resources/scroll_pane_list_add.ico
--------------------------------------------------------------------------------
/gui/quick/android/res/drawable-hdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/drawable-hdpi/splash.9.png
--------------------------------------------------------------------------------
/gui/quick/android/res/drawable-mdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/drawable-mdpi/splash.9.png
--------------------------------------------------------------------------------
/gui/quick/android/res/drawable-xhdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/drawable-xhdpi/splash.9.png
--------------------------------------------------------------------------------
/gui/quick/android/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/gui/quick/android/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/gui/widgets/icons/hicolor/128x128/apps/anirem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/icons/hicolor/128x128/apps/anirem.png
--------------------------------------------------------------------------------
/gui/widgets/icons/hicolor/16x16/apps/anirem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/icons/hicolor/16x16/apps/anirem.png
--------------------------------------------------------------------------------
/gui/widgets/icons/hicolor/192x192/apps/anirem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/icons/hicolor/192x192/apps/anirem.png
--------------------------------------------------------------------------------
/gui/widgets/icons/hicolor/22x22/apps/anirem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/icons/hicolor/22x22/apps/anirem.png
--------------------------------------------------------------------------------
/gui/widgets/icons/hicolor/24x24/apps/anirem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/icons/hicolor/24x24/apps/anirem.png
--------------------------------------------------------------------------------
/gui/widgets/icons/hicolor/256x256/apps/anirem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/icons/hicolor/256x256/apps/anirem.png
--------------------------------------------------------------------------------
/gui/widgets/icons/hicolor/32x32/apps/anirem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/icons/hicolor/32x32/apps/anirem.png
--------------------------------------------------------------------------------
/gui/widgets/icons/hicolor/48x48/apps/anirem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/icons/hicolor/48x48/apps/anirem.png
--------------------------------------------------------------------------------
/gui/widgets/icons/hicolor/512x512/apps/anirem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/icons/hicolor/512x512/apps/anirem.png
--------------------------------------------------------------------------------
/gui/widgets/icons/hicolor/64x64/apps/anirem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/icons/hicolor/64x64/apps/anirem.png
--------------------------------------------------------------------------------
/gui/widgets/icons/hicolor/72x72/apps/anirem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/icons/hicolor/72x72/apps/anirem.png
--------------------------------------------------------------------------------
/gui/widgets/icons/hicolor/96x96/apps/anirem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/widgets/icons/hicolor/96x96/apps/anirem.png
--------------------------------------------------------------------------------
/gui/quick/android/res/drawable-xxhdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/drawable-xxhdpi/splash.9.png
--------------------------------------------------------------------------------
/gui/quick/android/res/drawable-xxxhdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/drawable-xxxhdpi/splash.9.png
--------------------------------------------------------------------------------
/gui/quick/android/res/mipmap-hdpi/ic_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/mipmap-hdpi/ic_foreground.png
--------------------------------------------------------------------------------
/gui/quick/android/res/mipmap-mdpi/ic_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/mipmap-mdpi/ic_foreground.png
--------------------------------------------------------------------------------
/gui/quick/android/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/gui/quick/android/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/droid-service/android/res/drawable-hdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/drawable-hdpi/splash.9.png
--------------------------------------------------------------------------------
/droid-service/android/res/drawable-mdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/drawable-mdpi/splash.9.png
--------------------------------------------------------------------------------
/droid-service/android/res/drawable-xhdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/drawable-xhdpi/splash.9.png
--------------------------------------------------------------------------------
/droid-service/android/res/drawable-xxhdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/drawable-xxhdpi/splash.9.png
--------------------------------------------------------------------------------
/droid-service/android/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/droid-service/android/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/droid-service/android/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/gui/quick/android/res/mipmap-xhdpi/ic_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/mipmap-xhdpi/ic_foreground.png
--------------------------------------------------------------------------------
/gui/quick/android/res/mipmap-xxhdpi/ic_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/mipmap-xxhdpi/ic_foreground.png
--------------------------------------------------------------------------------
/gui/quick/android/res/mipmap-xxxhdpi/ic_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/mipmap-xxxhdpi/ic_foreground.png
--------------------------------------------------------------------------------
/gui/quick/android/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/droid-service/android/res/drawable-xxxhdpi/splash.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/drawable-xxxhdpi/splash.9.png
--------------------------------------------------------------------------------
/droid-service/android/res/mipmap-hdpi/ic_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/mipmap-hdpi/ic_foreground.png
--------------------------------------------------------------------------------
/droid-service/android/res/mipmap-mdpi/ic_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/mipmap-mdpi/ic_foreground.png
--------------------------------------------------------------------------------
/droid-service/android/res/mipmap-xhdpi/ic_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/mipmap-xhdpi/ic_foreground.png
--------------------------------------------------------------------------------
/droid-service/android/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/droid-service/android/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/droid-service/android/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #4E4E4E
4 |
5 |
--------------------------------------------------------------------------------
/gui/quick/android/res/drawable-hdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/drawable-hdpi/ic_notification.png
--------------------------------------------------------------------------------
/gui/quick/android/res/drawable-mdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/drawable-mdpi/ic_notification.png
--------------------------------------------------------------------------------
/gui/quick/android/res/drawable-xhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/drawable-xhdpi/ic_notification.png
--------------------------------------------------------------------------------
/gui/quick/android/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #4E4E4E
4 |
5 |
--------------------------------------------------------------------------------
/droid-service/android/res/drawable-hdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/drawable-hdpi/ic_notification.png
--------------------------------------------------------------------------------
/droid-service/android/res/drawable-mdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/drawable-mdpi/ic_notification.png
--------------------------------------------------------------------------------
/droid-service/android/res/mipmap-xxhdpi/ic_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/mipmap-xxhdpi/ic_foreground.png
--------------------------------------------------------------------------------
/droid-service/android/res/mipmap-xxxhdpi/ic_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/mipmap-xxxhdpi/ic_foreground.png
--------------------------------------------------------------------------------
/gui/quick/android/res/drawable-xxhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/drawable-xxhdpi/ic_notification.png
--------------------------------------------------------------------------------
/gui/quick/android/res/drawable-xxxhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/gui/quick/android/res/drawable-xxxhdpi/ic_notification.png
--------------------------------------------------------------------------------
/droid-service/android/res/drawable-xhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/drawable-xhdpi/ic_notification.png
--------------------------------------------------------------------------------
/droid-service/android/res/drawable-xxhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/drawable-xxhdpi/ic_notification.png
--------------------------------------------------------------------------------
/droid-service/android/res/drawable-xxxhdpi/ic_notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Skycoder42/AniRem/master/droid-service/android/res/drawable-xxxhdpi/ic_notification.png
--------------------------------------------------------------------------------
/gui/quick/App.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.10
2 | import de.skycoder42.QtMvvm.Quick 1.0
3 | import de.skycoder42.QtMvvm.DataSync.Quick 1.0
4 |
5 | QtMvvmApp {
6 | title: qsTr("Ani-Rem")
7 | }
8 |
--------------------------------------------------------------------------------
/gui/quick/qtquickcontrols2.conf:
--------------------------------------------------------------------------------
1 | [Material]
2 | Theme=Dark
3 | Primary=#777777
4 | Accent=#8A0E0E
5 | Background=#4E4E4E
6 |
7 | [Universal]
8 | Theme=Dark
9 | Accent=#8A0E0E
10 | Background=#4E4E4E
11 |
--------------------------------------------------------------------------------
/gui/core/anirem-core.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | settings.xml
4 |
5 |
6 | anirem.ico
7 |
8 |
9 |
--------------------------------------------------------------------------------
/gui/quick/icons/ic_add_white_24px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gui/widgets/instancesetup.h:
--------------------------------------------------------------------------------
1 | #ifndef INSTANCESETUP_H
2 | #define INSTANCESETUP_H
3 |
4 | #include
5 |
6 | void setMainWindow(QMainWindow *window);
7 | QMainWindow *getMainWindow();
8 |
9 | #endif // INSTANCESETUP_H
10 |
--------------------------------------------------------------------------------
/gui/quick/qtquickcontrols2_android.conf:
--------------------------------------------------------------------------------
1 | [Material]
2 | Theme=Dark
3 | Primary=#777777
4 | Accent=#8A0E0E
5 | Background=#4E4E4E
6 |
7 | [Universal]
8 | Theme=Dark
9 | Accent=#8A0E0E
10 | Background=#4E4E4E
11 |
12 | [Controls]
13 | Style=Material
14 |
--------------------------------------------------------------------------------
/gui/quick/qtquickcontrols2_win.conf:
--------------------------------------------------------------------------------
1 | [Material]
2 | Theme=Dark
3 | Primary=#777777
4 | Accent=#8A0E0E
5 | Background=#4E4E4E
6 |
7 | [Universal]
8 | Theme=Dark
9 | Accent=#8A0E0E
10 | Background=#4E4E4E
11 |
12 | [Controls]
13 | Style=Universal
14 |
--------------------------------------------------------------------------------
/gui/quick/icons/ic_check_white_24px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gui/quick/icons/ic_filter_list_white_24px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gui/quick/openssl/get_openssl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | curl -Lo "openssl.tar.xz" "https://github.com/Skycoder42/ci-builds/releases/download/${OPENSSL_VERSION}/openssl_${OPENSSL_VERSION}_${PLATFORM}.tar.xz"
5 | tar -xf openssl.tar.xz
6 | rm -f openssl.tar.xz
7 |
--------------------------------------------------------------------------------
/gui/quick/icons/ic_arrow_back_white_24px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gui/core/qpmx.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": [
3 | ],
4 | "license": {
5 | "file": "",
6 | "name": ""
7 | },
8 | "prcFile": "",
9 | "priFile": "",
10 | "priIncludes": [
11 | ],
12 | "publishers": {
13 | },
14 | "source": false
15 | }
16 |
--------------------------------------------------------------------------------
/gui/quick/icons/ic_playlist_add_white_24px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gui/quick/qpmx.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": [
3 | ],
4 | "license": {
5 | "file": "",
6 | "name": ""
7 | },
8 | "prcFile": "",
9 | "priFile": "",
10 | "priIncludes": [
11 | ],
12 | "publishers": {
13 | },
14 | "source": false
15 | }
16 |
--------------------------------------------------------------------------------
/gui/quick/android/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/gui/quick/android/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Ani-Rem
4 | Überprüfe auf neue Seasons…
5 | Animes werden geladen…
6 |
7 |
--------------------------------------------------------------------------------
/droid-service/android/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/droid-service/android/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Ani-Rem
4 | Überprüfe auf neue Seasons…
5 | Animes werden geladen…
6 |
7 |
--------------------------------------------------------------------------------
/lib/syncedsettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lib_anirem_global.h
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/gui/quick/icons/ic_open_in_browser_white_24px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gui/gui.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 |
3 | SUBDIRS += \
4 | core \
5 | widgets \
6 | quick
7 |
8 | widgets.depends += core
9 | quick.depends += core
10 |
11 | android: SUBDIRS -= widgets
12 | no_quick: SUBDIRS -= quick
13 |
14 | prepareRecursiveTarget(lrelease)
15 | prepareRecursiveTarget(qtifw)
16 | QMAKE_EXTRA_TARGETS += lrelease qtifw
17 |
--------------------------------------------------------------------------------
/lib/api/proxerloginvalue.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lib_anirem_global.h
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/lib/api/proxernamevalue.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lib_anirem_global.h
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ci/appveyor_postbuild.bat:
--------------------------------------------------------------------------------
1 | setlocal
2 |
3 | set qtplatform=%PLATFORM%
4 | for %%* in (.) do set CurrDirName=%%~nx*
5 |
6 | call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 || exit /B 1
7 |
8 | cd build-%qtplatform%
9 | nmake INSTALL_ROOT=\projects\%CurrDirName%\install qtifw || exit /B 1
10 |
--------------------------------------------------------------------------------
/lib/api/proxerstatus.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lib_anirem_global.h
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/droid-service/droid-service.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 |
3 | QT += androidextras mvvmcore
4 | QT -= gui
5 |
6 | TARGET = anirem-service
7 |
8 | HEADERS += \
9 | starthelper.h \
10 | androidupdatenotifier.h
11 |
12 | SOURCES += \
13 | main.cpp \
14 | starthelper.cpp \
15 | androidupdatenotifier.cpp
16 |
17 | QMAKE_EXTRA_TARGETS += lrelease qtifw
18 |
19 | include(../lib.pri)
20 |
--------------------------------------------------------------------------------
/gui/quick/icons/ic_content_paste_white_24px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gui/widgets/installer/meta/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Ani-Rem Season Reminder
4 | Ani-Rem Season Reminder
5 | 2.0.0
6 | 2018-04-15
7 | true
8 | true
9 | de.skycoder42.seasonproxer
10 |
11 |
--------------------------------------------------------------------------------
/lib/api/proxerentry.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lib_anirem_global.h
4 | proxerstatus.h
5 | proxerentryvalue.h
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/api/proxerlogin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lib_anirem_global.h
4 | proxerstatus.h
5 | proxerloginvalue.h
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/gui/quick/icons/ic_autorenew_white_24px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/lib_anirem_global.h:
--------------------------------------------------------------------------------
1 | #ifndef LIB_ANIREM_GLOBAL_H
2 | #define LIB_ANIREM_GLOBAL_H
3 |
4 | #include
5 |
6 | #if defined(BUILD_LIB_ANIREM)
7 | # define LIB_ANIREM_EXPORT Q_DECL_EXPORT
8 | #else
9 | # define LIB_ANIREM_EXPORT Q_DECL_IMPORT
10 | #endif
11 |
12 | #ifdef BUILD_LIB_ANIREM
13 | #ifndef PROXER_API_KEY
14 | #include "../proxer-api-key.h"
15 | #endif
16 | #endif
17 |
18 | #endif // LIB_ANIREM_GLOBAL_H
19 |
--------------------------------------------------------------------------------
/lib/api/proxerlist.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lib_anirem_global.h
4 | proxerstatus.h
5 | proxerlistvalue.h
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/api/proxernames.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lib_anirem_global.h
4 | proxerstatus.h
5 | proxernamevalue.h
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/gui/quick/icons/ic_text_fields_white_24px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/api/proxerrelations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lib_anirem_global.h
4 | proxerstatus.h
5 | proxerentryvalue.h
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/qpmx.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": [
3 | {
4 | "package": "de.skycoder42.qsettingsgenerator",
5 | "provider": "qpm",
6 | "version": "1.4.1"
7 | }
8 | ],
9 | "license": {
10 | "file": "",
11 | "name": ""
12 | },
13 | "prcFile": "",
14 | "priFile": "",
15 | "priIncludes": [
16 | ],
17 | "publishers": {
18 | },
19 | "source": false
20 | }
21 |
--------------------------------------------------------------------------------
/gui/quick/icons/ic_delete_forever_white_24px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gui/quick/qmltoast.h:
--------------------------------------------------------------------------------
1 | #ifndef QMLTOAST_H
2 | #define QMLTOAST_H
3 |
4 | #include
5 |
6 | class QmlToast : public QObject
7 | {
8 | Q_OBJECT
9 |
10 | public:
11 | enum Duration {
12 | Short = 0x00000000,
13 | Long = 0x00000001
14 | };
15 | Q_ENUM(Duration)
16 |
17 | explicit QmlToast(QObject *parent = nullptr);
18 |
19 | public slots:
20 | void toast(const QString &text, Duration duration = Short);
21 | };
22 |
23 | #endif // QMLTOAST_H
24 |
--------------------------------------------------------------------------------
/lib/api/proxerentryvalue.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lib_anirem_global.h
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/gui/quick/android/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
--------------------------------------------------------------------------------
/lib/api/proxerlistvalue.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lib_anirem_global.h
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/droid-service/android/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
--------------------------------------------------------------------------------
/gui/widgets/animemodel.h:
--------------------------------------------------------------------------------
1 | #ifndef ANIMEMODEL_H
2 | #define ANIMEMODEL_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | class AnimeModel : public QObjectProxyModel
10 | {
11 | Q_OBJECT
12 |
13 | public:
14 | explicit AnimeModel(QtDataSync::DataStoreModel *srcModel, QObject *parent = nullptr);
15 |
16 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
17 | };
18 |
19 | #endif // ANIMEMODEL_H
20 |
--------------------------------------------------------------------------------
/gui/widgets/logindialog.h:
--------------------------------------------------------------------------------
1 | #ifndef LOGINDIALOG_H
2 | #define LOGINDIALOG_H
3 |
4 | #include
5 | #include
6 |
7 | namespace Ui {
8 | class LoginDialog;
9 | }
10 |
11 | class LoginDialog : public QDialog
12 | {
13 | Q_OBJECT
14 |
15 | public:
16 | Q_INVOKABLE explicit LoginDialog(QtMvvm::ViewModel *viewModel, QWidget *parent = nullptr);
17 | ~LoginDialog() override;
18 |
19 | void accept() override;
20 |
21 | private:
22 | LoginViewModel *_viewModel;
23 | Ui::LoginDialog *_ui;
24 | };
25 |
26 | #endif // LOGINDIALOG_H
27 |
--------------------------------------------------------------------------------
/droid-service/starthelper.h:
--------------------------------------------------------------------------------
1 | #ifndef STARTHELPER_H
2 | #define STARTHELPER_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class StartHelper : public QObject
9 | {
10 | Q_OBJECT
11 |
12 | public:
13 | explicit StartHelper(QObject *parent = nullptr);
14 |
15 | static void handleStart();
16 |
17 | private slots:
18 | void startUpdater();
19 | void done();
20 |
21 | private:
22 | static QMutex _runMutex;
23 | static QPointer _runInstance;
24 | static bool _wasStarted;
25 | };
26 |
27 | #endif // STARTHELPER_H
28 |
--------------------------------------------------------------------------------
/lib.pri:
--------------------------------------------------------------------------------
1 | # link against main lib
2 | QT += datasync restclient mvvmcore
3 |
4 | ARLIB_DIR = $$shadowed($$PWD/lib)
5 | win32:CONFIG(release, debug|release): LIBS += -L$$ARLIB_DIR/release/ -lanirem
6 | else:win32:CONFIG(debug, debug|release): LIBS += -L$$ARLIB_DIR/debug/ -lanirem
7 | else:mac: LIBS += -F$$ARLIB_DIR/ -framework anirem
8 | else: LIBS += -L$$ARLIB_DIR/ -lanirem
9 |
10 | INCLUDEPATH += $$PWD/lib $$ARLIB_DIR
11 | win32:CONFIG(release, debug|release): INCLUDEPATH += $$ARLIB_DIR/release
12 | else:win32:CONFIG(debug, debug|release): INCLUDEPATH += $$ARLIB_DIR/debug
13 | DEPENDPATH += $$PWD/lib
14 |
--------------------------------------------------------------------------------
/gui/widgets/entrydialog.h:
--------------------------------------------------------------------------------
1 | #ifndef ENTRYDIALOG_H
2 | #define ENTRYDIALOG_H
3 |
4 | #include
5 | #include
6 |
7 | namespace Ui {
8 | class EntryDialog;
9 | }
10 |
11 | class EntryDialog : public QDialog
12 | {
13 | Q_OBJECT
14 |
15 | public:
16 | Q_INVOKABLE explicit EntryDialog(QtMvvm::ViewModel *viewModel, QWidget *parent = nullptr);
17 | ~EntryDialog() override;
18 |
19 | private slots:
20 | void on_treeView_activated(const QModelIndex &index);
21 |
22 | private:
23 | EntryViewModel *_viewModel;
24 | Ui::EntryDialog *_ui;
25 | };
26 |
27 | #endif // ENTRYDIALOG_H
28 |
--------------------------------------------------------------------------------
/lib/api/proxerapi.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lib_anirem_global.h
4 | infoclass.h
5 | userclass.h
6 |
7 | https://proxer.me/api
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/lib/apihelper.h:
--------------------------------------------------------------------------------
1 | #ifndef APIHELPER_H
2 | #define APIHELPER_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include "lib_anirem_global.h"
9 | #include "proxerstatus.h"
10 |
11 | class LIB_ANIREM_EXPORT ApiHelper
12 | {
13 | Q_DECLARE_TR_FUNCTIONS(ApiHelper)
14 |
15 | public:
16 | static QString transformError(ProxerStatus status, int);
17 | static QString formatError(const QString &error, int errorCode, QtRestClient::RestReply::ErrorType type);
18 | static bool testValid(int code, ProxerStatus status);
19 | };
20 |
21 | #endif // APIHELPER_H
22 |
--------------------------------------------------------------------------------
/lib/libanirem.h:
--------------------------------------------------------------------------------
1 | #ifndef LIBANIREM_H
2 | #define LIBANIREM_H
3 |
4 | #include
5 | #include
6 |
7 | #include "lib_anirem_global.h"
8 | #include "animeinfo.h"
9 | #include "localsettings.h"
10 |
11 | using AniremStore = QtDataSync::DataTypeStore;
12 |
13 | namespace AniRem {
14 |
15 | LIB_ANIREM_EXPORT void prepareTranslations();
16 | LIB_ANIREM_EXPORT void setup(QtDataSync::Setup &setup, bool passive = false);
17 | LIB_ANIREM_EXPORT void setProxerToken(const QString &token, LocalSettings *settings);
18 |
19 | }
20 |
21 | #endif // LIBANIREM_H
22 |
--------------------------------------------------------------------------------
/gui/widgets/anirem.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Type=Application
3 | Version=1.1
4 | Name=Ani-Rem — Anime Season Reminder
5 | Name[de]=Ani-Rem — Anime Season Erinnerungen
6 | Comment=A tool to passivly check for updates on seasons, for proxer.me
7 | Comment[de]=Ein Tool zum passiven überprüfen von Updates für Anime Seasons, basierend auf proxer.me
8 | Exec=anirem
9 | Icon=anirem
10 | Terminal=false
11 | Categories=Utility;Qt;
12 | Actions=UpdateCheck;
13 |
14 | [Desktop Action UpdateCheck]
15 | Exec=anirem --update
16 | Name=Check for updated seasons
17 | Name[de]=Auf neue Seasons überprüfen
18 | Icon=view-refresh
19 |
--------------------------------------------------------------------------------
/droid-service/android/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Ani-Rem
4 | Checking for new seasons…
5 | Loading Animes…
6 |
7 | Anime Informations
8 | Notifications about new relations of your saved animes
9 | Service Status
10 | Status notifications that Ani-Rem is updating relations
11 |
12 |
--------------------------------------------------------------------------------
/lib/jsonseasondataconverter.h:
--------------------------------------------------------------------------------
1 | #ifndef JSONSEASONDATACONVERTER_H
2 | #define JSONSEASONDATACONVERTER_H
3 |
4 | #include
5 |
6 | class JsonSeasonDataConverter : public QJsonTypeConverter
7 | {
8 | public:
9 | bool canConvert(int metaTypeId) const override;
10 | QList jsonTypes() const override;
11 | QJsonValue serialize(int propertyType, const QVariant &value, const SerializationHelper *helper) const override;
12 | QVariant deserialize(int propertyType, const QJsonValue &value, QObject *parent, const SerializationHelper *helper) const override;
13 | };
14 |
15 | #endif // JSONSEASONDATACONVERTER_H
16 |
--------------------------------------------------------------------------------
/gui/widgets/anirem_de.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/lib/storedcookiejar.h:
--------------------------------------------------------------------------------
1 | #ifndef STOREDCOOKIEJAR_H
2 | #define STOREDCOOKIEJAR_H
3 |
4 | #include
5 | #include
6 |
7 | class StoredCookieJar : public QNetworkCookieJar
8 | {
9 | Q_OBJECT
10 |
11 | public:
12 | explicit StoredCookieJar(QObject *parent = nullptr);
13 | ~StoredCookieJar();
14 |
15 | bool insertCookie(const QNetworkCookie &cookie) override;
16 | bool deleteCookie(const QNetworkCookie &cookie) override;
17 |
18 | private slots:
19 | void store();
20 |
21 | private:
22 | static const QString CookieKey;
23 | static const QString CookieDataKey;
24 |
25 | QSettings *_cookieStore;
26 | bool _scheduled;
27 | };
28 |
29 | #endif // STOREDCOOKIEJAR_H
30 |
--------------------------------------------------------------------------------
/gui/widgets/installer/anirem_de.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/lib/iupdatenotifier.h:
--------------------------------------------------------------------------------
1 | #ifndef IUPDATENOTIFIER_H
2 | #define IUPDATENOTIFIER_H
3 |
4 | #include
5 |
6 | class IUpdateNotifier
7 | {
8 | Q_DISABLE_COPY(IUpdateNotifier)
9 |
10 | public:
11 | inline IUpdateNotifier() = default;
12 | virtual inline ~IUpdateNotifier() = default;
13 |
14 | public slots:
15 | virtual void showNotification(bool isPositive, const QString &title, const QString &description) = 0;
16 | virtual void updateProgress(int value, int max) = 0;
17 | };
18 |
19 | #define IUpdateNotifierIid "de.skycoder42.anirem.IUpdateNotifier"
20 | Q_DECLARE_INTERFACE(IUpdateNotifier, IUpdateNotifierIid)
21 | Q_DECLARE_METATYPE(IUpdateNotifier*)
22 |
23 | #endif // IUPDATENOTIFIER_H
24 |
--------------------------------------------------------------------------------
/gui/quick/android/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Ani-Rem
4 | Checking for new seasons…
5 | Loading Animes…
6 | Checking Anime for updates:
7 |
8 | Anime Informations
9 | Notifications about new relations of your saved animes
10 | Service Status
11 | Status notifications that Ani-Rem is updating relations
12 |
13 |
--------------------------------------------------------------------------------
/droid-service/androidupdatenotifier.h:
--------------------------------------------------------------------------------
1 | #ifndef ANDROIDUPDATENOTIFIER_H
2 | #define ANDROIDUPDATENOTIFIER_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class AndroidUpdateNotifier : public QObject, public IUpdateNotifier
9 | {
10 | Q_OBJECT
11 | Q_INTERFACES(IUpdateNotifier)
12 |
13 | public:
14 | Q_INVOKABLE explicit AndroidUpdateNotifier(QObject *parent = nullptr);
15 |
16 | public slots:
17 | void showNotification(bool isPositive, const QString &title, const QString &description) override;
18 | void updateProgress(int current, int max) override;
19 |
20 | private:
21 | QAndroidJniObject _service;
22 | };
23 |
24 | #endif // ANDROIDUPDATENOTIFIER_H
25 |
--------------------------------------------------------------------------------
/gui/quick/anirem_de.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/lib/localsettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lib_anirem_global.h
4 | QSize
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/ci/travis_init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | currDir=$(dirname $0)
5 |
6 | echo 'CONFIG += create_installer' >> install.pri
7 |
8 | # install 7z
9 | if [[ $TRAVIS_OS_NAME == "linux" ]]; then
10 | sudo apt-get -qq update
11 | sudo apt-get -qq install --no-install-recommends p7zip-full
12 |
13 | # append post build script
14 | echo "$currDir/travis_postbuild.sh" >> qtmodules-travis/ci/linux/build-docker.sh
15 | fi
16 |
17 | if [[ $TRAVIS_OS_NAME == "osx" ]]; then
18 | brew install p7zip
19 | fi
20 |
21 | # unpack data
22 | mkdir ../__private
23 | 7z x "-p$PROXER_DATA" -o. "./ci/proxer.data" > /dev/null
24 |
25 | # install openssl for android
26 | if [[ $PLATFORM == "android_"* ]]; then
27 | pushd gui/quick/openssl
28 | ./get_openssl.sh
29 | popd
30 | fi
31 |
--------------------------------------------------------------------------------
/gui/core/aniremsettingsviewmodel.h:
--------------------------------------------------------------------------------
1 | #ifndef ANIREMSETTINGSVIEWMODEL_H
2 | #define ANIREMSETTINGSVIEWMODEL_H
3 |
4 | #include "datasyncsettingsviewmodel.h"
5 | #include "imageloader.h"
6 |
7 | class AniremSettingsViewModel : public DataSyncSettingsViewModel
8 | {
9 | Q_OBJECT
10 |
11 | QTMVVM_INJECT_PROP(ImageLoader*, imageLoader, _loader)
12 |
13 | public:
14 | Q_INVOKABLE explicit AniremSettingsViewModel(QObject *parent = nullptr);
15 |
16 | void saveValue(const QString &key, const QVariant &value) override;
17 | void resetValue(const QString &key) override;
18 |
19 | public slots:
20 | void callAction(const QString &key, const QVariantMap ¶meters) override;
21 |
22 | private:
23 | ImageLoader *_loader;
24 | };
25 |
26 | #endif // ANIREMSETTINGSVIEWMODEL_H
27 |
--------------------------------------------------------------------------------
/gui/quick/android/res/values/libs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - https://download.qt.io/ministro/android/qt5/qt-5.9
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/droid-service/android/res/values/libs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - https://download.qt.io/ministro/android/qt5/qt-5.9
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/AniRem.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 |
3 | SUBDIRS += \
4 | gui \
5 | lib
6 |
7 | gui.depends += lib
8 |
9 | android {
10 | SUBDIRS += droid-service
11 | droid-service.depends += lib
12 | }
13 |
14 | prepareRecursiveTarget(lrelease)
15 | prepareRecursiveTarget(qtifw)
16 | QMAKE_EXTRA_TARGETS += lrelease qtifw
17 |
18 | DISTFILES += .qmake.conf \
19 | README.md
20 |
21 | mac {
22 | install_join.commands += cp -pRv "$(INSTALL_ROOT)$$INSTALL_APPS/" "$(INSTALL_ROOT)$${INSTALL_PREFIX}/" \
23 | $$escape_expand(\n\t)rm -rf "$(INSTALL_ROOT)$$INSTALL_APPS" \
24 | $$escape_expand(\n\t)install_name_tool -change anirem.framework/Versions/2/anirem @rpath/anirem.framework/Versions/2/anirem "$(INSTALL_ROOT)$${INSTALL_BINS}/Ani-Rem"
25 | install_join.target = install
26 | QMAKE_EXTRA_TARGETS += install_join
27 | }
28 |
--------------------------------------------------------------------------------
/gui/widgets/detailsdockwidget.h:
--------------------------------------------------------------------------------
1 | #ifndef DETAILSDOCKWIDGET_H
2 | #define DETAILSDOCKWIDGET_H
3 |
4 | #include
5 | #include
6 |
7 | namespace Ui {
8 | class DetailsDockWidget;
9 | }
10 |
11 | class DetailsDockWidget : public QDockWidget
12 | {
13 | Q_OBJECT
14 |
15 | public:
16 | Q_INVOKABLE DetailsDockWidget(QtMvvm::ViewModel *viewModel, QWidget *parent = nullptr);
17 | ~DetailsDockWidget() override;
18 |
19 | protected:
20 | void closeEvent(QCloseEvent *event) override;
21 |
22 | private slots:
23 | void updateInfo();
24 |
25 | void imageLoaded(int id, const QImage &image);
26 | void imageLoadFailed(int id, const QString &error);
27 |
28 | private:
29 | DetailsViewModel *_viewModel;
30 | Ui::DetailsDockWidget *_ui;
31 | };
32 |
33 | #endif // DETAILSDOCKWIDGET_H
34 |
--------------------------------------------------------------------------------
/gui/core/aniremapp.h:
--------------------------------------------------------------------------------
1 | #ifndef GUIAPP_H
2 | #define GUIAPP_H
3 |
4 | #include
5 |
6 | class AniRemApp : public QtMvvm::CoreApp
7 | {
8 | Q_OBJECT
9 |
10 | public:
11 | explicit AniRemApp(QObject *parent = nullptr);
12 |
13 | void updateAutoStartState();
14 | void updateAutoStartState(int interval);
15 |
16 | signals:
17 | void updateMigrationProgressMax(int max);
18 | void updateMigrationProgressValue(int value);
19 |
20 | protected:
21 | void performRegistrations() override;
22 | int startApp(const QStringList &arguments) override;
23 |
24 | private slots:
25 | void migrate();
26 |
27 | private:
28 | bool setAutoStart(bool autoStart);
29 |
30 | void updateCheck();
31 | };
32 |
33 | #undef coreApp
34 | #define coreApp static_cast(QtMvvm::CoreApp::instance())
35 |
36 | #endif // GUIAPP_H
37 |
--------------------------------------------------------------------------------
/gui/widgets/installer/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Ani-Rem Season Reminder
4 | 2.0.0
5 | Ani-Rem Season Reminder Installer
6 | Sykcoder42
7 | https://github.com/Skycoder42/AniRem
8 | Ani-Rem
9 | Ani-Rem
10 | Ani-Rem
11 | true
12 | true
13 |
14 |
15 | https://install.skycoder42.de/installers/anirem
16 | 1
17 | Ani-Rem Season Reminder Repository
18 |
19 |
20 | controller.js
21 |
22 |
--------------------------------------------------------------------------------
/droid-service/androidupdatenotifier.cpp:
--------------------------------------------------------------------------------
1 | #include "androidupdatenotifier.h"
2 | #include
3 |
4 | AndroidUpdateNotifier::AndroidUpdateNotifier(QObject *parent) :
5 | QObject(parent),
6 | _service(QtAndroid::androidService())
7 | {}
8 |
9 | void AndroidUpdateNotifier::showNotification(bool isPositive, const QString &title, const QString &description)
10 | {
11 | _service.callMethod("showUpdateNotification",
12 | "(ZLjava/lang/String;Ljava/lang/String;)V",
13 | static_cast(isPositive),
14 | QAndroidJniObject::fromString(title).object(),
15 | QAndroidJniObject::fromString(description).object());
16 | }
17 |
18 | void AndroidUpdateNotifier::updateProgress(int current, int max)
19 | {
20 | _service.callMethod("updateProgress", "(II)V",
21 | static_cast(current),
22 | static_cast(max));
23 | }
24 |
--------------------------------------------------------------------------------
/lib/api/infoclass.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lib_anirem_global.h
4 | proxerentry.h
5 | proxerrelations.h
6 | proxernames.h
7 |
8 | info
9 |
10 |
11 | entry
12 |
13 |
14 |
15 | names
16 |
17 |
18 |
19 | relations
20 |
21 | false
22 |
23 |
24 |
--------------------------------------------------------------------------------
/gui/core/imageloader.h:
--------------------------------------------------------------------------------
1 | #ifndef IMAGELOADER_H
2 | #define IMAGELOADER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | class ImageLoader : public QObject
10 | {
11 | Q_OBJECT
12 |
13 | public:
14 | Q_INVOKABLE explicit ImageLoader(QObject *parent = nullptr);
15 |
16 | public slots:
17 | void loadImage(int id);
18 |
19 | void clearCache();
20 |
21 | signals:
22 | void imageLoaded(int id, const QImage &image);
23 | void imageLoadFailed(int id, const QString &errorString);
24 |
25 | private slots:
26 | void loadImageImpl(int id);
27 | void clearCacheImpl();
28 |
29 | private:
30 | static const QString CacheDirName;
31 | static const QString ImageNameTemplate;
32 |
33 | QNetworkAccessManager *_nam;
34 | QCache _cache;
35 |
36 | void imageNetworkReply(int id, QNetworkReply *reply);
37 | };
38 |
39 | #endif // IMAGELOADER_H
40 |
--------------------------------------------------------------------------------
/gui/quick/qmltoast.cpp:
--------------------------------------------------------------------------------
1 | #include "qmltoast.h"
2 | #ifdef Q_OS_ANDROID
3 | #include
4 | #else
5 | #include
6 | #endif
7 |
8 | QmlToast::QmlToast(QObject *parent) :
9 | QObject(parent)
10 | {}
11 |
12 | void QmlToast::toast(const QString &text, QmlToast::Duration duration)
13 | {
14 | #ifdef Q_OS_ANDROID
15 | QtAndroid::runOnAndroidThread([text, duration](){
16 | auto toast = QAndroidJniObject::callStaticObjectMethod("android/widget/Toast", "makeText",
17 | "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;",
18 | QtAndroid::androidContext().object(),
19 | QAndroidJniObject::fromString(text).object(),
20 | static_cast(duration));
21 | if(toast.isValid())
22 | toast.callMethod("show");
23 | });
24 | #else
25 | Q_UNUSED(duration)
26 | qInfo().noquote() << "TOAST:" << text;
27 | #endif
28 | }
29 |
--------------------------------------------------------------------------------
/gui/widgets/qpmx.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": [
3 | {
4 | "package": "de.skycoder42.qobjectlistmodel",
5 | "provider": "qpm",
6 | "version": "1.2.1"
7 | },
8 | {
9 | "package": "de.skycoder42.dialog-master",
10 | "provider": "qpm",
11 | "version": "1.3.2"
12 | },
13 | {
14 | "package": "de.skycoder42.qsingleinstance",
15 | "provider": "qpm",
16 | "version": "1.2.2"
17 | },
18 | {
19 | "package": "de.skycoder42.qtifw-advanced-setup",
20 | "provider": "qpm",
21 | "version": "2.1.1"
22 | }
23 | ],
24 | "license": {
25 | "file": "",
26 | "name": ""
27 | },
28 | "prcFile": "",
29 | "priFile": "",
30 | "priIncludes": [
31 | ],
32 | "publishers": {
33 | },
34 | "source": false
35 | }
36 |
--------------------------------------------------------------------------------
/gui/widgets/anirem-widgets.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | resources/cross.ico
4 | resources/plus.ico
5 | resources/paste_plain.ico
6 | resources/page_white_copy.ico
7 | resources/arrow_refresh.ico
8 | resources/world_go.ico
9 | resources/bell_valid.ico
10 | resources/anirem_inverted.ico
11 | resources/textfield_add.ico
12 | resources/scroll_pane_list_add.ico
13 |
14 |
15 | resources/ajax-loader.gif
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ci/travis_deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | if [[ $PLATFORM == "android_"* ]]; then
5 | # install android deps for signing
6 | sudo apt-get -qq update
7 | sudo apt-get -qq install --no-install-recommends openjdk-8-jdk unzip
8 |
9 | # android skd
10 | curl -Lo /tmp/android-sdk.zip https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
11 | mkdir $HOME/android
12 | unzip -qq /tmp/android-sdk.zip -d $HOME/android/sdk/
13 | rm -f /tmp/android-sdk.zip
14 | echo y | $HOME/android/sdk/tools/bin/sdkmanager --update > /dev/null
15 | echo y | $HOME/android/sdk/tools/bin/sdkmanager "build-tools;27.0.3" > /dev/null
16 |
17 | # sign
18 | mkdir -p ./install
19 | $(find "$HOME/android" -name apksigner) sign --ks "$(dirname $0)/anirem.aks" --ks-pass env:AKS_DATA --key-pass env:AKS_DATA --out "./install/anirem-${ANIREM_VER}_${PLATFORM}.apk" "build-$PLATFORM/android-build/build/outputs/apk/android-build-release-unsigned.apk"
20 | fi
21 |
--------------------------------------------------------------------------------
/ci/pkgbuild.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # run with git root as pwd
3 | # $1 out-dir
4 | # $2 version
5 | set -e
6 |
7 | currdir=$(pwd)
8 | pkgname=anirem
9 | pkgver=${1:?Invalid commands. Syntax: arch.sh }
10 | outdir=$(readlink -f ${2:?Invalid commands. Syntax: arch.sh })
11 | pkgdir=$(mktemp -d)
12 |
13 | echo 'CONFIG += no_mobile no_updater' >> .qmake.conf
14 | ./qpminit.sh
15 |
16 | pushd $(mktemp -d)
17 |
18 | qmake -r "$currdir"
19 | make
20 | make lrelease
21 | make INSTALL_ROOT="$pkgdir" install
22 |
23 | popd
24 |
25 | install -D -m 644 -p Desktop/${pkgname}.desktop $pkgdir/usr/share/applications/${pkgname}.desktop
26 | for res in 16 22 24 32 48 64 72 96 192 256 512; do
27 | install -D -m 644 -p "icn/${pkgname}_${res}.png" "$pkgdir/usr/share/icons/hicolor/${res}x${res}/apps/${pkgname}.png"
28 | done
29 |
30 | mkdir -p "$outdir"
31 | cd "$pkgdir"
32 | export XZ_OPT=-9
33 | tar cJf "$outdir/${pkgname}-${pkgver}.tar.xz" ./*
34 |
--------------------------------------------------------------------------------
/droid-service/android/src/de/skycoder42/anirem/Globals.java:
--------------------------------------------------------------------------------
1 | package de.skycoder42.anirem;
2 |
3 | import android.os.Build;
4 |
5 | public class Globals {
6 | // alarms
7 | public static final int AlarmIntentId = 11;
8 |
9 | // preferences
10 | public static final String AutoStartPrefs = "de.skycoder42.anirem.preferences";
11 | public static final String AutStartKey = "de.skycoder42.anirem.autostart";
12 |
13 | // notifications channels
14 | public static final String NormalChannelId = "de.skycoder42.anirem.channel.normal";
15 | public static final String ForegroundChannelId = "de.skycoder42.anirem.channel.foreground";
16 |
17 | // notifications IDs
18 | public static final int ForegroundId = 21;
19 | public static final int NotifyId = 22;
20 |
21 | // colors
22 | public static final int NormalColor = 0xFF8A0E0E; // proxer red
23 |
24 | public static boolean isOreo() {
25 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/gui/quick/android/src/de/skycoder42/anirem/Globals.java:
--------------------------------------------------------------------------------
1 | package de.skycoder42.anirem;
2 |
3 | import android.os.Build;
4 |
5 | public class Globals {
6 | // alarms
7 | public static final int AlarmIntentId = 11;
8 | public static final int OpenIntentId = 12;
9 |
10 | // preferences
11 | public static final String AutoStartPrefs = "de.skycoder42.anirem.preferences";
12 | public static final String AutStartKey = "de.skycoder42.anirem.autostart";
13 |
14 | // notifications channels
15 | public static final String NormalChannelId = "de.skycoder42.anirem.channel.normal";
16 | public static final String ForegroundChannelId = "de.skycoder42.anirem.channel.foreground";
17 |
18 | // notifications IDs
19 | public static final int ForegroundId = 21;
20 | public static final int NotifyId = 22;
21 |
22 | // colors
23 | public static final int NormalColor = 0xFF8A0E0E; // proxer red
24 |
25 | public static boolean isOreo() {
26 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ci/travis_postbuild.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | if [[ $PLATFORM == "android_"* ]]; then
5 | if [[ "$PLATFORM" == "android_armv7" ]]; then
6 | PCODE=0
7 | fi
8 | if [[ "$PLATFORM" == "android_x86" ]]; then
9 | PCODE=1
10 | fi
11 |
12 | sed -i "s/android:versionCode=\"\([[:digit:]]*\)\"/android:versionCode=\"\1%{pcode}\"/g" gui/quick/android/AndroidManifest.xml
13 | sed -i "s/%{pcode}/$PCODE/g" gui/quick/android/AndroidManifest.xml
14 |
15 | mv install build-$PLATFORM/android-build
16 | /opt/qt/$QT_VER/$PLATFORM/bin/androiddeployqt --input "build-$PLATFORM/gui/quick/android-libanirem-activity.so-deployment-settings.json" --output "build-$PLATFORM/android-build" --deployment bundled --gradle --no-gdbserver --release
17 | else
18 | if [[ $TRAVIS_OS_NAME == "linux" ]]; then
19 | apt-get -qq update
20 | apt-get -qq install libxrender1 libsecret-1-0
21 | fi
22 |
23 | rootdir=$(pwd)
24 | pushd build-$PLATFORM
25 | make INSTALL_ROOT="$rootdir/install" qtifw
26 |
27 | find "$rootdir/install"
28 | fi
29 |
--------------------------------------------------------------------------------
/droid-service/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include "starthelper.h"
7 | #include "androidupdatenotifier.h"
8 |
9 | int main(int argc, char *argv[])
10 | {
11 | QAndroidService service(argc, argv);
12 | //WORKAROUND until fixed in upstream
13 | qputenv("PLUGIN_KEYSTORES_PATH", QCoreApplication::applicationDirPath().toUtf8());
14 | qInfo() << "Overwriting keystore path to:" << qgetenv("PLUGIN_KEYSTORES_PATH");
15 |
16 | //load translations
17 | AniRem::prepareTranslations();
18 |
19 | QtMvvm::ServiceRegistry::instance()->registerInterface();
20 |
21 | try {
22 | QtDataSync::Setup setup;
23 | AniRem::setup(setup, true);
24 | setup.create();
25 |
26 | StartHelper starter;
27 |
28 | qInfo() << "service successfully started";
29 | return service.exec();
30 | } catch(QException &e) {
31 | qCritical() << e.what();
32 | return EXIT_FAILURE;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/gui/core/datasyncsettingsviewmodel.h:
--------------------------------------------------------------------------------
1 | #ifndef DATASYNCSETTINGSVIEWMODEL_H
2 | #define DATASYNCSETTINGSVIEWMODEL_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | struct DataSyncSettingsEntry //copied from qsettingsgenerator
9 | {
10 | Q_GADGET
11 |
12 | public:
13 | Q_PROPERTY(QString key MEMBER key USER true)
14 | Q_PROPERTY(QByteArray value MEMBER value)
15 |
16 | QString key;
17 | QByteArray value;
18 | };
19 |
20 | class DataSyncSettingsViewModel : public QtMvvm::SettingsViewModel
21 | {
22 | Q_OBJECT
23 |
24 | public:
25 | Q_INVOKABLE explicit DataSyncSettingsViewModel(QObject *parent = nullptr);
26 |
27 | QVariant loadValue(const QString &key, const QVariant &defaultValue) const override;
28 | void saveValue(const QString &key, const QVariant &value) override;
29 | void resetValue(const QString &key) override;
30 |
31 | private:
32 | QtDataSync::DataTypeStore *_store;
33 | };
34 |
35 | #endif // DATASYNCSETTINGSVIEWMODEL_H
36 |
--------------------------------------------------------------------------------
/lib/passiveupdater.h:
--------------------------------------------------------------------------------
1 | #ifndef PASSIVEUPDATER_H
2 | #define PASSIVEUPDATER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "lib_anirem_global.h"
8 | #include "libanirem.h"
9 | #include "seasonstatusloader.h"
10 | #include "iupdatenotifier.h"
11 |
12 | class LIB_ANIREM_EXPORT PassiveUpdater : public QObject
13 | {
14 | Q_OBJECT
15 |
16 | QTMVVM_INJECT_PROP(SeasonStatusLoader*, loader, _loader)
17 | QTMVVM_INJECT_PROP(IUpdateNotifier*, notifier, _notifier)
18 |
19 | public:
20 | Q_INVOKABLE explicit PassiveUpdater(QObject *parent = nullptr);
21 |
22 | public slots:
23 | void performUpdateCheck();
24 |
25 | signals:
26 | void done(bool hasNotification);
27 |
28 | private slots:
29 | void qtmvvm_init();
30 |
31 | void completed(bool hasUpdates, const QString &errorString);
32 |
33 | private:
34 | SeasonStatusLoader *_loader;
35 | IUpdateNotifier *_notifier;
36 | QtDataSync::SyncManager *_manager;
37 | AniremStore *_store;
38 | };
39 |
40 | #endif // PASSIVEUPDATER_H
41 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | image:
2 | - Visual Studio 2017
3 |
4 | version: build-{build}
5 |
6 | environment:
7 | QT_VER: 5.10.1
8 | PLATFORM: msvc2017_64
9 | EXTRA_MODULES: .qtremoteobjects;.skycoder42
10 | NO_TESTS: true
11 | PROXER_DATA:
12 | secure: fktz4h6RVTRjrTLC7mJCpyFsaGDk5SfT/Qu+7/1U+DOjHprCUclci8+Ph/Cx1xnHzFdekP81JSmZ3H9DBhzmFUdPWRTL/XWKshOOq7Qso+g=
13 |
14 | install:
15 | - git clone https://github.com/Skycoder42/QtModules.git .\qtmodules-travis
16 | - .\qtmodules-travis\ci\win\setup.bat
17 |
18 | build_script:
19 | - .\ci\appveyor_init.bat
20 | - .\qtmodules-travis\ci\win\build.bat
21 | - .\ci\appveyor_postbuild.bat
22 |
23 | artifacts:
24 | - path: install\repository_win.zip
25 | - path: install\Ani-Rem Installer.exe
26 |
27 | deploy:
28 | provider: GitHub
29 | auth_token:
30 | secure: Cp5GRQku2ZWnKPE12NB5q11ZO0Fr5mlzdUTjnLpYJr/dki4LPVqm231edFggogy8
31 | artifact: /.*/
32 | force_update: false
33 | on:
34 | appveyor_repo_tag: true
35 |
36 | cache:
37 | - 'C:\Users\appveyor\AppData\Local\Skycoder42\qpmx\cache -> appveyor.yml'
38 |
--------------------------------------------------------------------------------
/gui/widgets/widgetsupdatenotifier.h:
--------------------------------------------------------------------------------
1 | #ifndef WIDGETSUPDATENOTIFIER_H
2 | #define WIDGETSUPDATENOTIFIER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | class WidgetsUpdateNotifier : public QObject, public IUpdateNotifier
12 | {
13 | Q_OBJECT
14 | Q_INTERFACES(IUpdateNotifier)
15 |
16 | public:
17 | Q_INVOKABLE explicit WidgetsUpdateNotifier(QObject *parent = nullptr);
18 |
19 | public slots:
20 | void showNotification(bool isPositive, const QString &title, const QString &description) override;
21 | void updateProgress(int value, int max) override;
22 |
23 | private slots:
24 | void timerTriggered();
25 | void trayActivated(QSystemTrayIcon::ActivationReason reason);
26 | void showMainViewModel();
27 | void quit();
28 |
29 | private:
30 | QSystemTrayIcon *_trayIcon;
31 | QScopedPointer _trayMenu;
32 |
33 | QTimer *_blinkTimer;
34 | QIcon _normalIcon;
35 | QIcon _invertIcon;
36 | bool _currentNormal;
37 | };
38 |
39 | #endif // WIDGETSUPDATENOTIFIER_H
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # This file is used to ignore files which are generated
2 | # ----------------------------------------------------------------------------
3 |
4 | *~
5 | *.autosave
6 | *.a
7 | *.core
8 | *.moc
9 | *.o
10 | *.obj
11 | *.orig
12 | *.rej
13 | *.so
14 | *.so.*
15 | *_pch.h.cpp
16 | *_resource.rc
17 | *.qm
18 | .#*
19 | *.*#
20 | core
21 | !core/
22 | tags
23 | .DS_Store
24 | .directory
25 | *.debug
26 | Makefile*
27 | *.prl
28 | *.app
29 | moc_*.cpp
30 | ui_*.h
31 | qrc_*.cpp
32 | Thumbs.db
33 | *.res
34 | *.rc
35 | /.qmake.cache
36 | /.qmake.stash
37 |
38 | # qtcreator generated files
39 | *.pro.user*
40 |
41 | # xemacs temporary files
42 | *.flc
43 |
44 | # Vim temporary files
45 | .*.swp
46 |
47 | # Visual Studio generated files
48 | *.ib_pdb_index
49 | *.idb
50 | *.ilk
51 | *.pdb
52 | *.sln
53 | *.suo
54 | *.vcproj
55 | *vcproj.*.*.user
56 | *.ncb
57 | *.sdf
58 | *.opensdf
59 | *.vcxproj
60 | *vcxproj.*
61 |
62 | # MinGW generated files
63 | *.Debug
64 | *.Release
65 |
66 | # Python byte code
67 | *.pyc
68 |
69 | # Binaries
70 | # --------
71 | *.dll
72 | *.exe
73 |
74 | # qpmx
75 | .qpmx-dev-cache
76 | *.tsdummy.cpp
77 | *.json.user
78 |
79 | .old
80 | /proxer-api-key.h
81 |
--------------------------------------------------------------------------------
/gui/quick/proxerimageprovider.h:
--------------------------------------------------------------------------------
1 | #ifndef PROXERIMAGEPROVIDER_H
2 | #define PROXERIMAGEPROVIDER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | class ProxerImageResponse : public QQuickImageResponse
10 | {
11 | Q_OBJECT
12 |
13 | public:
14 | ProxerImageResponse(int _id, const QSize &_requestedSize);
15 |
16 | QQuickTextureFactory *textureFactory() const override;
17 | QString errorString() const override;
18 |
19 | public slots:
20 | void cancel() override;
21 |
22 | void imageLoaded(int _id, const QImage &image);
23 | void imageLoadFailed(int _id, const QString &errorString);
24 |
25 | private:
26 | int _id;
27 | QSize _requestedSize;
28 | QImage _result;
29 | QString _error;
30 | };
31 |
32 | class ProxerImageProvider : public QQuickAsyncImageProvider
33 | {
34 | public:
35 | ProxerImageProvider();
36 |
37 | ImageType imageType() const override;
38 | Flags flags() const override;
39 |
40 | QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
41 |
42 | private:
43 | ImageLoader *_loader;
44 | };
45 |
46 | #endif // PROXERIMAGEPROVIDER_H
47 |
--------------------------------------------------------------------------------
/gui/widgets/addanimedialog.h:
--------------------------------------------------------------------------------
1 | #ifndef ADDANIMEDIALOG_H
2 | #define ADDANIMEDIALOG_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | namespace Ui {
10 | class AddAnimeDialog;
11 | }
12 |
13 | class AddAnimeDialog : public QDialog
14 | {
15 | Q_OBJECT
16 |
17 | Q_PROPERTY(int uiId READ uiId WRITE setUiId NOTIFY uiIdChanged)
18 |
19 | public:
20 | Q_INVOKABLE AddAnimeDialog(QtMvvm::ViewModel *viewModel, QWidget *parent = nullptr);
21 | ~AddAnimeDialog() override;
22 |
23 | void accept() override;
24 |
25 | signals:
26 | void uiIdChanged();
27 |
28 | private slots:
29 | void setUiId(int id);
30 | void loadingChanged(bool loading);
31 |
32 | void imageLoaded(int id, const QImage &image);
33 | void imageLoadFailed(int id, const QString &error);
34 |
35 | private:
36 | enum PmState {
37 | PmCleared,
38 | PmLoading,
39 | PmLoaded,
40 | PmError
41 | };
42 |
43 | AddAnimeViewModel *_viewModel;
44 | Ui::AddAnimeDialog *_ui;
45 |
46 | QMovie *_loadingMovie;
47 | QPixmap _currentPixmap;
48 | PmState _pmState;
49 |
50 | void updatePm();
51 |
52 | int uiId() const;
53 | };
54 |
55 | #endif // ADDANIMEDIALOG_H
56 |
--------------------------------------------------------------------------------
/gui/quick/AnimeHistoryDelegate.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.10
2 | import QtQuick.Controls 2.3
3 | import QtQuick.Controls.Material 2.3
4 | import QtQuick.Controls.Universal 2.3
5 | import QtQuick.Layouts 1.3
6 | import de.skycoder42.QtMvvm.Quick 1.0
7 |
8 | ItemDelegate {
9 | id: delegate
10 | width: parent.width
11 | text: name
12 |
13 | contentItem: RowLayout {
14 | Image {
15 | id: previewImage
16 | asynchronous: true
17 | fillMode: Image.PreserveAspectFit
18 | horizontalAlignment: Image.AlignHCenter
19 | verticalAlignment: Image.AlignVCenter
20 | Layout.minimumWidth: 42
21 | Layout.maximumWidth: 42
22 | Layout.minimumHeight: 42
23 | Layout.maximumHeight: 42
24 | source: "image://proxer/%1".arg(id)
25 | mipmap: true
26 | }
27 |
28 | Label {
29 | id: titleLabel
30 | elide: Label.ElideRight
31 | horizontalAlignment: Qt.AlignLeft
32 | verticalAlignment: Qt.AlignVCenter
33 | Layout.fillWidth: true
34 | Layout.fillHeight: true
35 | text: delegate.text
36 | }
37 |
38 | Label {
39 | id: ratingLabel
40 | Layout.fillHeight: true
41 | horizontalAlignment: Qt.AlignRight
42 | verticalAlignment: Qt.AlignVCenter
43 | text: qsTr("%1/10 ☆").arg(rating)
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/lib/api/userclass.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lib_anirem_global.h
4 | proxerlist.h
5 | proxerlogin.h
6 |
7 | user
8 |
9 |
10 | list
11 |
12 | false
13 | 0
14 | 100
15 | changeDateDESC
16 | stateFilter0
17 |
18 |
19 | login
20 |
21 |
22 |
23 |
24 | login
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/gui/core/aniremsettingsviewmodel.cpp:
--------------------------------------------------------------------------------
1 | #include "aniremsettingsviewmodel.h"
2 | #include "aniremapp.h"
3 | #include
4 |
5 | AniremSettingsViewModel::AniremSettingsViewModel(QObject *parent) :
6 | DataSyncSettingsViewModel(parent),
7 | _loader(nullptr)
8 | {}
9 |
10 | void AniremSettingsViewModel::saveValue(const QString &key, const QVariant &value)
11 | {
12 | DataSyncSettingsViewModel::saveValue(key, value);
13 | if(key == QStringLiteral("updates/autoCheck"))
14 | coreApp->updateAutoStartState(value.toInt());
15 | }
16 |
17 | void AniremSettingsViewModel::resetValue(const QString &key)
18 | {
19 | DataSyncSettingsViewModel::resetValue(key);
20 | if(key == QStringLiteral("updates/autoCheck"))
21 | coreApp->updateAutoStartState(7);
22 | }
23 |
24 | void AniremSettingsViewModel::callAction(const QString &key, const QVariantMap ¶meters)
25 | {
26 | Q_UNUSED(parameters)
27 | if(key == QStringLiteral("clearcache")) {
28 | QtMvvm::question(tr("Clear Cache?"),
29 | tr("Do you want to remove all cached preview images? This will free some storage, "
30 | "but the app will have to download previews of you animes again as soon as they are needed."),
31 | this, [this](bool ok){
32 | if(ok)
33 | _loader->clearCache();
34 | });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/gui/widgets/entrydialog.cpp:
--------------------------------------------------------------------------------
1 | #include "entrydialog.h"
2 | #include "ui_entrydialog.h"
3 | #include
4 | #include
5 |
6 | EntryDialog::EntryDialog(QtMvvm::ViewModel *viewModel, QWidget *parent) :
7 | QDialog(parent),
8 | _viewModel(static_cast(viewModel)),
9 | _ui(new Ui::EntryDialog)
10 | {
11 | _ui->setupUi(this);
12 | DialogMaster::masterDialog(this);
13 |
14 | _ui->typeComboBox->addItems(_viewModel->listTypes());
15 | QtMvvm::bind(_viewModel, "typeIndex",
16 | _ui->typeComboBox, "currentIndex");
17 |
18 | connect(_viewModel, &EntryViewModel::modelChanged,
19 | this, [this](ProxerEntryModel *model){
20 | _ui->treeView->setModel(model);
21 |
22 | _ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
23 | _ui->treeView->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
24 | _ui->treeView->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
25 | _ui->treeView->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
26 | });
27 | }
28 |
29 | EntryDialog::~EntryDialog()
30 | {
31 | delete _ui;
32 | }
33 |
34 | void EntryDialog::on_treeView_activated(const QModelIndex &index)
35 | {
36 | if(index.isValid())
37 | _viewModel->addAnime(_viewModel->model()->getId(index));
38 | }
39 |
--------------------------------------------------------------------------------
/lib/seasonstatusloader.h:
--------------------------------------------------------------------------------
1 | #ifndef SEASONSTATUSLOADER_H
2 | #define SEASONSTATUSLOADER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "lib_anirem_global.h"
8 | #include "libanirem.h"
9 | #include "infoclass.h"
10 | #include "syncedsettings.h"
11 |
12 | class LIB_ANIREM_EXPORT SeasonStatusLoader : public QObject
13 | {
14 | Q_OBJECT
15 |
16 | QTMVVM_INJECT_PROP(SyncedSettings*, settings, _settings)
17 |
18 | public:
19 | Q_INVOKABLE explicit SeasonStatusLoader(QObject *parent = nullptr);
20 |
21 | bool isUpdating() const;
22 | void preClean();
23 |
24 | public slots:
25 | void checkForUpdates(bool useInterval = false);
26 | void checkForUpdates(const AnimeInfo &info);
27 |
28 | signals:
29 | void started();
30 | void updated(int value, int max);
31 | void completed(bool hasUpdates, const QString &errorString = {});
32 |
33 | private slots:
34 | void checkNext();
35 | void error(const QString &errorString, int errorCode, QtRestClient::RestReply::ErrorType errorType);
36 |
37 | private:
38 | AniremStore *_store;
39 | InfoClass *_infoClass;
40 | SyncedSettings *_settings;
41 |
42 | QQueue _updateQueue;
43 | int _lastMax;
44 | bool _anyUpdated;
45 |
46 | void addInfos(const QList &infos);
47 | };
48 |
49 | #endif // SEASONSTATUSLOADER_H
50 |
--------------------------------------------------------------------------------
/lib/apihelper.cpp:
--------------------------------------------------------------------------------
1 | #include "apihelper.h"
2 | #include
3 | #include
4 | #include "libanirem.h"
5 | using namespace QtRestClient;
6 |
7 | QString ApiHelper::transformError(ProxerStatus status, int)
8 | {
9 | return status.message();
10 | }
11 |
12 | QString ApiHelper::formatError(const QString &error, int errorCode, RestReply::ErrorType type)
13 | {
14 | qWarning() << "API-Error type"
15 | << type
16 | << "code"
17 | << errorCode
18 | << "with error string"
19 | << error;
20 |
21 | switch (type) {
22 | case RestReply::NetworkError:
23 | return tr("Network Error: %1").arg(error);
24 | case RestReply::JsonParseError:
25 | return tr("Data Error: %1").arg(error);
26 | case RestReply::FailureError:
27 | if(errorCode == 1005) {
28 | AniRem::setProxerToken({}, LocalSettings::instance());
29 | return tr("Invalid Login-Token! The old token has been removed. Please try again.");
30 | } else
31 | return tr("Proxer-API Error: %1").arg(error);
32 | case RestReply::DeserializationError:
33 | return tr("Data Error: %1").arg(error);
34 | default:
35 | Q_UNREACHABLE();
36 | return QString();
37 | }
38 | }
39 |
40 | bool ApiHelper::testValid(int code, ProxerStatus status)
41 | {
42 | return status.error() == 0 && (code / 100) == 2;
43 | }
44 |
--------------------------------------------------------------------------------
/gui/core/detailsviewmodel.h:
--------------------------------------------------------------------------------
1 | #ifndef DETAILSVIEWMODEL_H
2 | #define DETAILSVIEWMODEL_H
3 |
4 | #include
5 |
6 | #include
7 | #include "imageloader.h"
8 |
9 | class DetailsViewModel : public QtMvvm::ViewModel
10 | {
11 | Q_OBJECT
12 |
13 | Q_PROPERTY(AnimeInfo animeInfo READ animeInfo WRITE setAnimeInfo NOTIFY animeInfoChanged)
14 | Q_PROPERTY(QString detailsText READ detailsText NOTIFY animeInfoChanged)
15 |
16 | QTMVVM_INJECT_PROP(ImageLoader*, imageLoader, _loader)
17 |
18 | public:
19 | static const QString ParamInfo;
20 | static const QString ParamOwner;
21 |
22 | static QVariantHash params(const AnimeInfo &info, QtMvvm::ViewModel *owner = nullptr);
23 |
24 | Q_INVOKABLE explicit DetailsViewModel(QObject *parent = nullptr);
25 |
26 | AnimeInfo animeInfo() const;
27 | QString detailsText() const;
28 |
29 | ImageLoader* imageLoader() const;
30 |
31 | public slots:
32 | void setAnimeInfo(const AnimeInfo &animeInfo);
33 | void clear();
34 |
35 | void uncheckAnime();
36 |
37 | signals:
38 | void animeInfoChanged();
39 |
40 | private slots:
41 | void dataChanged(const QString &key, const QVariant &value);
42 |
43 | protected:
44 | void onInit(const QVariantHash ¶ms) override;
45 |
46 | private:
47 | AniremStore *_store;
48 | ImageLoader *_loader;
49 |
50 | AnimeInfo _animeInfo;
51 | };
52 |
53 | #endif // DETAILSVIEWMODEL_H
54 |
--------------------------------------------------------------------------------
/gui/widgets/logindialog.cpp:
--------------------------------------------------------------------------------
1 | #include "logindialog.h"
2 | #include "ui_logindialog.h"
3 | #include
4 | #include
5 |
6 | LoginDialog::LoginDialog(QtMvvm::ViewModel *viewModel, QWidget *parent) :
7 | QDialog(parent),
8 | _viewModel(static_cast(viewModel)),
9 | _ui(new Ui::LoginDialog)
10 | {
11 | _ui->setupUi(this);
12 | DialogMaster::masterDialog(this, true);
13 |
14 | QtMvvm::bind(_viewModel, "userName",
15 | _ui->usernameLineEdit, "text",
16 | QtMvvm::Binding::TwoWay,
17 | nullptr,
18 | "editingFinished()");
19 | QtMvvm::bind(_viewModel, "password",
20 | _ui->passwordLineEdit, "text",
21 | QtMvvm::Binding::TwoWay,
22 | nullptr,
23 | "editingFinished()");
24 | QtMvvm::bind(_viewModel, "authCode",
25 | _ui->faCodeLineEdit, "text",
26 | QtMvvm::Binding::TwoWay,
27 | nullptr,
28 | "editingFinished()");
29 | QtMvvm::bind(_viewModel, "storePassword",
30 | _ui->rememberPasswordCheckBox, "checked");
31 |
32 | connect(_viewModel, &LoginViewModel::loadingChanged,
33 | this, &LoginDialog::setDisabled);
34 | connect(_viewModel, &LoginViewModel::loginSuccessful,
35 | this, &LoginDialog::close);
36 |
37 | //FEATURE disable for now
38 | _ui->rememberPasswordCheckBox->hide();
39 | }
40 |
41 | LoginDialog::~LoginDialog()
42 | {
43 | delete _ui;
44 | }
45 |
46 | void LoginDialog::accept()
47 | {
48 | _viewModel->login();
49 | }
50 |
--------------------------------------------------------------------------------
/gui/widgets/mainwindow.h:
--------------------------------------------------------------------------------
1 | #ifndef MAINWINDOW_H
2 | #define MAINWINDOW_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 |
11 | #include "animemodel.h"
12 |
13 | namespace Ui {
14 | class MainWindow;
15 | }
16 |
17 | class MainWindow : public QMainWindow
18 | {
19 | Q_OBJECT
20 |
21 | public:
22 | Q_INVOKABLE MainWindow(QtMvvm::ViewModel *viewModel, QWidget *parent = nullptr);
23 | ~MainWindow() override;
24 |
25 | public slots:
26 | void showStatus(const QString &message);
27 | void setProgress(int value, int max);
28 |
29 | void updateLoadStatus(bool isLoading);
30 |
31 | void addDockAction(QAction *action);
32 |
33 | private slots:
34 | void updatePreview(const QModelIndex &index);
35 | void updateCount();
36 |
37 | void on_actionRemove_Anime_triggered();
38 | void on_actionCopy_selected_Info_triggered();
39 | void on_action_Open_Anime_in_browser_triggered();
40 | void on_action_Unmark_new_seasons_triggered();
41 | void on_seasonTreeView_activated(const QModelIndex &index);
42 |
43 | private:
44 | MainViewModel *_viewModel;
45 | Ui::MainWindow *_ui;
46 |
47 | QLabel *_statusLabel;
48 | QProgressBar *_statusProgress;
49 |
50 | AnimeModel *_animeModel;
51 | QSortFilterProxyModel *_proxyModel;
52 |
53 | QModelIndex mapToModel(const QModelIndex &uiIndex) const;
54 | int infoId(const QModelIndex &uiIndex) const;
55 | };
56 |
57 | #endif // MAINWINDOW_H
58 |
--------------------------------------------------------------------------------
/gui/core/entryviewmodel.h:
--------------------------------------------------------------------------------
1 | #ifndef ENTRYVIEWMODEL_H
2 | #define ENTRYVIEWMODEL_H
3 |
4 | #include
5 | #include
6 |
7 | class EntryViewModel : public QtMvvm::ViewModel
8 | {
9 | Q_OBJECT
10 |
11 | Q_PROPERTY(ProxerEntryModel* model READ model NOTIFY modelChanged)
12 | Q_PROPERTY(int typeIndex READ typeIndex WRITE setTypeIndex NOTIFY typeIndexChanged)
13 | Q_PROPERTY(QStringList listTypes READ listTypes CONSTANT)
14 |
15 | QTMVVM_INJECT_PROP(SyncedSettings*, settings, _settings)
16 |
17 | public:
18 |
19 | Q_INVOKABLE explicit EntryViewModel(QObject *parent = nullptr);
20 |
21 | ProxerEntryModel *model() const;
22 | int typeIndex() const;
23 | QStringList listTypes() const;
24 |
25 | public slots:
26 | void addAnime(int id);
27 |
28 | void setTypeIndex(int typeIndex);
29 |
30 | signals:
31 | void modelChanged(ProxerEntryModel *model);
32 | void typeIndexChanged(int typeIndex);
33 |
34 | protected:
35 | void onInit(const QVariantHash ¶ms) override;
36 | void onResult(quint32 requestCode, const QVariant &result) override;
37 |
38 | private slots:
39 | void loginNeeded();
40 | void apiError(const QString &errorString, int errorCode, QtRestClient::RestReply::ErrorType errorType);
41 |
42 | private:
43 | static const int LoginRequestCode = 0x4201;
44 | static const int AddRequestCode = 0x4202;
45 |
46 | SyncedSettings *_settings;
47 | ProxerEntryModel *_model;
48 | bool _showingChild;
49 | };
50 |
51 | #endif // ENTRYVIEWMODEL_H
52 |
--------------------------------------------------------------------------------
/droid-service/starthelper.cpp:
--------------------------------------------------------------------------------
1 | #include "starthelper.h"
2 | #include
3 | #include
4 | #include
5 |
6 | QMutex StartHelper::_runMutex;
7 | QPointer StartHelper::_runInstance = nullptr;
8 | bool StartHelper::_wasStarted = false;
9 |
10 | StartHelper::StartHelper(QObject *parent) :
11 | QObject(parent)
12 | {
13 | QMutexLocker locker(&_runMutex);
14 | if(_wasStarted)
15 | QMetaObject::invokeMethod(_runInstance, "startUpdater", Qt::QueuedConnection);
16 | else
17 | _runInstance = this;
18 | }
19 |
20 | void StartHelper::handleStart()
21 | {
22 | QMutexLocker locker(&_runMutex);
23 | _wasStarted = true;
24 | if(_runInstance) {
25 | QMetaObject::invokeMethod(_runInstance, "startUpdater", Qt::QueuedConnection);
26 | _runInstance = nullptr;//set to null again, this way only 1 queued invokation is done
27 | }
28 | }
29 |
30 | void StartHelper::startUpdater()
31 | {
32 | auto updater = QtMvvm::ServiceRegistry::instance()->constructInjected();
33 | connect(updater, &PassiveUpdater::done,
34 | this, &StartHelper::done);
35 | updater->performUpdateCheck();
36 | }
37 |
38 | void StartHelper::done()
39 | {
40 | auto service = QtAndroid::androidService();
41 | service.callMethod("completeAction");
42 | }
43 |
44 | extern "C" {
45 |
46 | JNIEXPORT void JNICALL Java_de_skycoder42_anirem_AniremService_handleStart(JNIEnv */*env*/, jobject /*obj*/)
47 | {
48 | StartHelper::handleStart();
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/gui/core/core.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 |
3 | QT += mvvmcore mvvmdatasynccore concurrent
4 | android: QT += androidextras
5 | CONFIG += static
6 | win32: CONFIG += skip_target_version_ext
7 |
8 | TARGET = anirem-core
9 |
10 | HEADERS += \
11 | mainviewmodel.h \
12 | aniremapp.h \
13 | datasyncsettingsviewmodel.h \
14 | addanimeviewmodel.h \
15 | imageloader.h \
16 | detailsviewmodel.h \
17 | aniremsettingsviewmodel.h \
18 | entryviewmodel.h \
19 | proxerentrymodel.h \
20 | loginviewmodel.h
21 |
22 | SOURCES += \
23 | mainviewmodel.cpp \
24 | aniremapp.cpp \
25 | datasyncsettingsviewmodel.cpp \
26 | addanimeviewmodel.cpp \
27 | imageloader.cpp \
28 | detailsviewmodel.cpp \
29 | aniremsettingsviewmodel.cpp \
30 | entryviewmodel.cpp \
31 | proxerentrymodel.cpp \
32 | loginviewmodel.cpp
33 |
34 | RESOURCES += \
35 | anirem-core.qrc
36 |
37 | TRANSLATIONS += anirem_core_de.ts
38 |
39 | DISTFILES += $$TRANSLATIONS
40 | QTMVVM_TS_SETTINGS = settings.xml
41 | _never_true_condition: SOURCES += $$files($$PWD/.ts-dummy/*)
42 | PRE_TARGETDEPS += qtmvvm-tsgen
43 |
44 | # install
45 | qpmx_ts_target.path = $$INSTALL_TRANSLATIONS
46 | INSTALLS += qpmx_ts_target
47 |
48 | include(../../lib.pri)
49 |
50 | !ReleaseBuild:!DebugBuild:!system(qpmx -d $$shell_quote($$_PRO_FILE_PWD_) --qmake-run init $$QPMX_EXTRA_OPTIONS $$shell_quote($$QMAKE_QMAKE) $$shell_quote($$OUT_PWD)): error(qpmx initialization failed. Check the compilation log for details.)
51 | else: include($$OUT_PWD/qpmx_generated.pri)
52 |
53 | QMAKE_EXTRA_TARGETS += qtifw
54 |
--------------------------------------------------------------------------------
/gui/quick/EntryView.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.10
2 | import QtQuick.Controls 2.3
3 | import QtQuick.Layouts 1.3
4 | import de.skycoder42.QtMvvm.Core 1.0
5 | import de.skycoder42.QtMvvm.Quick 1.0
6 | import de.skycoder42.anirem 1.0
7 | import "../../qml"
8 |
9 | Page {
10 | id: mainView
11 | property EntryViewModel viewModel: null
12 |
13 | header: ContrastToolBar {
14 | id: toolbar
15 |
16 | RowLayout {
17 | anchors.fill: parent
18 | spacing: 0
19 |
20 | ToolBarLabel {
21 | id: titleLabel
22 | Layout.fillWidth: true
23 | text: qsTr("%1 History").arg(viewModel.listTypes[viewModel.typeIndex])
24 | }
25 |
26 | MenuButton {
27 | id: menuButton
28 | icon.name: "view-filter"
29 | icon.source: "qrc:/icons/ic_filter_list.svg"
30 | text: qsTr("Select Mode")
31 |
32 | ButtonGroup {
33 | id: indexGroup
34 | exclusive: true
35 | }
36 |
37 | Repeater {
38 | id: typeRep
39 | model: viewModel.listTypes
40 | delegate: MenuItem {
41 | id: mItem
42 | text: modelData
43 | checkable: true
44 | checked: viewModel.typeIndex == index
45 | onClicked: viewModel.typeIndex = index
46 |
47 | Component.onCompleted: indexGroup.addButton(mItem)
48 | }
49 | }
50 | }
51 | }
52 | }
53 |
54 | PresenterProgress {}
55 |
56 | ScrollView {
57 | anchors.fill: parent
58 | clip: true
59 |
60 | ListView {
61 | model: viewModel.model
62 |
63 | delegate: AnimeHistoryDelegate {
64 | onClicked: viewModel.addAnime(id)
65 | }
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/gui/core/datasyncsettingsviewmodel.cpp:
--------------------------------------------------------------------------------
1 | #include "datasyncsettingsviewmodel.h"
2 | #include
3 | #include
4 |
5 | DataSyncSettingsViewModel::DataSyncSettingsViewModel(QObject *parent) :
6 | SettingsViewModel(parent),
7 | _store(new QtDataSync::DataTypeStore(this))
8 | {}
9 |
10 | QVariant DataSyncSettingsViewModel::loadValue(const QString &key, const QVariant &defaultValue) const
11 | {
12 | try {
13 | QVariant value;
14 | {
15 | auto data = _store->load(key).value;
16 | QDataStream stream(data);
17 | stream >> value;
18 | }
19 | return value;
20 | } catch (QtDataSync::NoDataException &e) {
21 | return defaultValue;
22 | } catch (QException &e) {
23 | qCritical() << "Failed to load entry" << key << "from datasync settings with error:"
24 | << e.what();
25 | return defaultValue;
26 | }
27 | }
28 |
29 | void DataSyncSettingsViewModel::saveValue(const QString &key, const QVariant &value)
30 | {
31 | try {
32 | QByteArray data;
33 | {
34 | QDataStream stream(&data, QIODevice::WriteOnly);
35 | stream << value;
36 | }
37 | _store->save({key, data});
38 | } catch (QException &e) {
39 | qCritical() << "Failed to save entry" << key << "to datasync settings with error:"
40 | << e.what();
41 | }
42 | }
43 |
44 | void DataSyncSettingsViewModel::resetValue(const QString &key)
45 | {
46 | try {
47 | _store->remove(key);
48 | } catch (QException &e) {
49 | qCritical() << "Failed to remove entry" << key << "from datasync settings with error:"
50 | << e.what();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2018, Felix Barz
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/droid-service/android/src/de/skycoder42/anirem/AniremService.java:
--------------------------------------------------------------------------------
1 | package de.skycoder42.anirem;
2 |
3 | import android.content.Intent;
4 |
5 | import android.graphics.BitmapFactory;
6 |
7 | import android.support.v4.app.NotificationCompat;
8 |
9 | import org.qtproject.qt5.android.bindings.QtService;
10 |
11 | public class AniremService extends QtService {
12 | @Override
13 | public void onDestroy() {
14 | super.onDestroy();
15 | // explicitly exit to prevent the process from beeing cached
16 | System.exit(0);
17 | }
18 |
19 | @Override
20 | public int onStartCommand(Intent intent, int flags, int startId) {
21 | int result = super.onStartCommand(intent, flags, startId);
22 |
23 | NotificationCompat.Builder builder = new NotificationCompat.Builder(this, Globals.ForegroundChannelId)
24 | .setContentTitle(getString(R.string.update_progress_title))
25 | .setContentText(getString(R.string.update_progress_text))
26 | .setContentInfo(getString(R.string.app_name))
27 | .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
28 | .setSmallIcon(R.drawable.ic_notification)
29 | .setColor(Globals.NormalColor)
30 | .setOngoing(true)
31 | .setProgress(0, 0, true)
32 | .setCategory(NotificationCompat.CATEGORY_PROGRESS);
33 |
34 | if (!Globals.isOreo())
35 | builder.setPriority(NotificationCompat.PRIORITY_MIN);
36 |
37 | startForeground(Globals.ForegroundId, builder.build());
38 | return result;
39 | }
40 |
41 | public void completeAction() {
42 | stopForeground(true);
43 | stopService(new Intent(this, AniremService.class));//Stop myself
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/droid-service/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | jcenter()
4 | }
5 |
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:2.2.3'
8 | }
9 | }
10 |
11 | allprojects {
12 | repositories {
13 | jcenter()
14 | maven {
15 | url "https://maven.google.com"
16 | }
17 | }
18 | }
19 |
20 | apply plugin: 'com.android.application'
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | compile "com.android.support:support-compat:27.1.0"
25 | }
26 |
27 | android {
28 | /*******************************************************
29 | * The following variables:
30 | * - androidBuildToolsVersion,
31 | * - androidCompileSdkVersion
32 | * - qt5AndroidDir - holds the path to qt android files
33 | * needed to build any Qt application
34 | * on Android.
35 | *
36 | * are defined in gradle.properties file. This file is
37 | * updated by QtCreator and androiddeployqt tools.
38 | * Changing them manually might break the compilation!
39 | *******************************************************/
40 |
41 | compileSdkVersion androidCompileSdkVersion.toInteger()
42 |
43 | buildToolsVersion androidBuildToolsVersion
44 |
45 | sourceSets {
46 | main {
47 | manifest.srcFile 'AndroidManifest.xml'
48 | java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
49 | aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
50 | res.srcDirs = [qt5AndroidDir + '/res', 'res']
51 | resources.srcDirs = ['src']
52 | renderscript.srcDirs = ['src']
53 | assets.srcDirs = ['assets']
54 | jniLibs.srcDirs = ['libs']
55 | }
56 | }
57 |
58 | lintOptions {
59 | abortOnError false
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/gui/quick/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | jcenter()
4 | }
5 |
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:2.2.3'
8 | }
9 | }
10 |
11 | allprojects {
12 | repositories {
13 | jcenter()
14 | maven {
15 | url "https://maven.google.com"
16 | }
17 | }
18 | }
19 |
20 | apply plugin: 'com.android.application'
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | compile "com.android.support:support-compat:27.1.0"
25 | }
26 |
27 | android {
28 | /*******************************************************
29 | * The following variables:
30 | * - androidBuildToolsVersion,
31 | * - androidCompileSdkVersion
32 | * - qt5AndroidDir - holds the path to qt android files
33 | * needed to build any Qt application
34 | * on Android.
35 | *
36 | * are defined in gradle.properties file. This file is
37 | * updated by QtCreator and androiddeployqt tools.
38 | * Changing them manually might break the compilation!
39 | *******************************************************/
40 |
41 | compileSdkVersion androidCompileSdkVersion.toInteger()
42 |
43 | buildToolsVersion androidBuildToolsVersion
44 |
45 | sourceSets {
46 | main {
47 | manifest.srcFile 'AndroidManifest.xml'
48 | java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
49 | aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
50 | res.srcDirs = [qt5AndroidDir + '/res', 'res']
51 | resources.srcDirs = ['src']
52 | renderscript.srcDirs = ['src']
53 | assets.srcDirs = ['assets']
54 | jniLibs.srcDirs = ['libs']
55 | }
56 | }
57 |
58 | lintOptions {
59 | abortOnError false
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/gui/quick/anirem-quick.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | qtquickcontrols2.conf
4 | qtquickcontrols2_android.conf
5 | qtquickcontrols2_win.conf
6 |
7 |
8 | App.qml
9 | AnimeInfoDelegate.qml
10 | AnimeHistoryDelegate.qml
11 | SubButton.qml
12 |
13 |
14 | MainView.qml
15 | AddAnimeDialog.qml
16 | DetailsView.qml
17 | EntryView.qml
18 | LoginDialog.qml
19 |
20 |
21 | icons/ic_add_white_24px.svg
22 | icons/ic_delete_forever_white_24px.svg
23 | icons/ic_autorenew_white_24px.svg
24 | icons/ic_check_white_24px.svg
25 | icons/ic_open_in_browser_white_24px.svg
26 | icons/ic_arrow_back_white_24px.svg
27 | icons/ic_text_fields_white_24px.svg
28 | icons/ic_playlist_add_white_24px.svg
29 | icons/ic_content_paste_white_24px.svg
30 | icons/ic_filter_list_white_24px.svg
31 |
32 |
33 |
--------------------------------------------------------------------------------
/gui/quick/SubButton.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.10
2 | import QtQuick.Controls 2.3
3 | import de.skycoder42.QtMvvm.Quick 1.0
4 |
5 | RoundActionButton {
6 | id: _subButton
7 |
8 | property Item reference: parent
9 | property real btnSpacing: 16
10 | property bool expanded: false
11 |
12 | highlighted: false
13 | anchors.horizontalCenter: reference.horizontalCenter
14 | anchors.verticalCenter: reference.verticalCenter
15 | implicitHeight: 40 + padding
16 | implicitWidth: 40 + padding
17 | state: expanded ? "expanded" : "collapsed"
18 |
19 | states: [
20 | State {
21 | name: "collapsed"
22 | PropertyChanges {
23 | target: _subButton
24 | anchors.verticalCenterOffset: 0
25 | visible: false
26 | }
27 | },
28 | State {
29 | name: "expanded"
30 | PropertyChanges {
31 | target: _subButton
32 | anchors.verticalCenterOffset: -1 * (reference.height/2 + _subButton.height/2 + btnSpacing)
33 | visible: true
34 | }
35 | }
36 | ]
37 |
38 | transitions: [
39 | Transition {
40 | from: "collapsed"
41 | to: "expanded"
42 | SequentialAnimation {
43 | PropertyAnimation {
44 | target: _subButton
45 | property: "visible"
46 | duration: 0
47 | }
48 | PropertyAnimation {
49 | target: _subButton
50 | property: "anchors.verticalCenterOffset"
51 | duration: 250
52 | easing.type: Easing.OutCubic
53 | }
54 | }
55 | },
56 | Transition {
57 | from: "expanded"
58 | to: "collapsed"
59 | SequentialAnimation {
60 | PropertyAnimation {
61 | target: _subButton
62 | property: "anchors.verticalCenterOffset"
63 | duration: 250
64 | easing.type: Easing.InCubic
65 | }
66 | PropertyAnimation {
67 | target: _subButton
68 | property: "visible"
69 | duration: 0
70 | }
71 | }
72 | }
73 | ]
74 | }
75 |
--------------------------------------------------------------------------------
/gui/widgets/animemodel.cpp:
--------------------------------------------------------------------------------
1 | #include "animemodel.h"
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | AnimeModel::AnimeModel(QtDataSync::DataStoreModel *srcModel, QObject *parent) :
8 | QObjectProxyModel({tr("Id"), tr("Name"), tr("Season Count"), tr("Last updated"), tr("Season Overview")}, parent)
9 | {
10 | setSourceModel(srcModel);
11 |
12 | addMapping(0, Qt::DisplayRole, "id");
13 | addMapping(0, Qt::ToolTipRole, "id");
14 |
15 | addMapping(1, Qt::DisplayRole, "title");
16 | addMapping(1, Qt::ToolTipRole, "title");
17 |
18 | addMapping(2, Qt::DisplayRole, "totalSeasonCount");
19 | addMapping(2, Qt::ToolTipRole, "totalSeasonCount");
20 |
21 | addMapping(3, Qt::DisplayRole, "lastUpdateCheck");
22 | addMapping(3, Qt::ToolTipRole, "lastUpdateCheck");
23 |
24 | addMapping(4, Qt::DisplayRole, "relationsUrl");
25 | addMapping(4, Qt::ToolTipRole, "relationsUrl");
26 | }
27 |
28 | QVariant AnimeModel::data(const QModelIndex &index, int role) const
29 | {
30 | auto src = mapToSource(index);
31 | if(!src.isValid())
32 | return {};
33 |
34 | auto info = static_cast(sourceModel())->object(src);
35 | switch (role) {
36 | case Qt::DisplayRole:
37 | case Qt::ToolTipRole:
38 | switch(index.column()) {
39 | case 2:
40 | return QLocale().toString(info.totalSeasonCount());
41 | case 3:
42 | return info.lastUpdateCheck().toString(Qt::DefaultLocaleShortDate);
43 | default:
44 | break;
45 | }
46 | break;
47 | case Qt::FontRole:
48 | if(info.hasNewSeasons()) {
49 | QFont font;
50 | font.setBold(true);
51 | return font;
52 | } else
53 | break;
54 | case Qt::ForegroundRole:
55 | if(info.hasNewSeasons())
56 | return QColor(0x8A, 0x0E, 0x0E);
57 | else
58 | break;
59 | default:
60 | break;
61 | }
62 |
63 | return QObjectProxyModel::data(index, role);
64 | }
65 |
--------------------------------------------------------------------------------
/lib/jsonseasondataconverter.cpp:
--------------------------------------------------------------------------------
1 | #include "animeinfo.h"
2 | #include "jsonseasondataconverter.h"
3 |
4 | #include
5 |
6 | bool JsonSeasonDataConverter::canConvert(int metaTypeId) const
7 | {
8 | return metaTypeId == qMetaTypeId();
9 | }
10 |
11 | QList JsonSeasonDataConverter::jsonTypes() const
12 | {
13 | return {QJsonValue::Object};
14 | }
15 |
16 | QJsonValue JsonSeasonDataConverter::serialize(int propertyType, const QVariant &value, const QJsonTypeConverter::SerializationHelper *helper) const
17 | {
18 | Q_UNUSED(propertyType)
19 | Q_UNUSED(helper)
20 |
21 | auto v = value.value();
22 | QJsonObject o;
23 | auto metaEnum = QMetaEnum::fromType();
24 | for(const auto &key : v.keys()) {
25 | QJsonObject pair;
26 | pair[QStringLiteral("count")] = v.value(key).first;
27 | pair[QStringLiteral("changed")] = v.value(key).second;
28 | o.insert(QString::fromUtf8(metaEnum.valueToKey(key)), pair);
29 | }
30 | return o;
31 | }
32 |
33 | QVariant JsonSeasonDataConverter::deserialize(int propertyType, const QJsonValue &value, QObject *parent, const QJsonTypeConverter::SerializationHelper *helper) const
34 | {
35 | Q_UNUSED(propertyType)
36 | Q_UNUSED(parent)
37 | Q_UNUSED(helper)
38 |
39 | auto o = value.toObject();
40 | AnimeInfo::SeasonMap v;
41 | auto metaEnum = QMetaEnum::fromType();
42 | for(const auto &key : o.keys()) {
43 | auto pair = o.value(key).toObject();
44 | AnimeInfo::SeasonInfo info;
45 | if(pair.contains(QStringLiteral("first"))) {
46 | info.first = pair[QStringLiteral("first")].toInt();
47 | info.second = pair[QStringLiteral("second")].toBool();
48 | } else {
49 | info.first = pair[QStringLiteral("count")].toInt();
50 | info.second = pair[QStringLiteral("changed")].toBool();
51 | }
52 | v.insert(static_cast(metaEnum.keyToValue(key.toLatin1().constData())), info);
53 | }
54 | return QVariant::fromValue(v);
55 | }
56 |
--------------------------------------------------------------------------------
/gui/core/loginviewmodel.h:
--------------------------------------------------------------------------------
1 | #ifndef LOGINVIEWMODEL_H
2 | #define LOGINVIEWMODEL_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class LoginViewModel : public QtMvvm::ViewModel
9 | {
10 | Q_OBJECT
11 |
12 | Q_PROPERTY(QString userName READ userName WRITE setUserName NOTIFY userNameChanged)
13 | Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
14 | Q_PROPERTY(QString authCode READ authCode WRITE setAuthCode NOTIFY authCodeChanged)
15 | Q_PROPERTY(bool storePassword READ storePassword WRITE setStorePassword NOTIFY storePasswordChanged)
16 |
17 | Q_PROPERTY(bool loading READ isLoading NOTIFY loadingChanged)
18 |
19 | QTMVVM_INJECT_PROP(LocalSettings*, settings, _settings)
20 |
21 | public:
22 | Q_INVOKABLE explicit LoginViewModel(QObject *parent = nullptr);
23 |
24 | QString userName() const;
25 | QString password() const;
26 | QString authCode() const;
27 | bool storePassword() const;
28 | bool isLoading() const;
29 |
30 | public slots:
31 | void setUserName(const QString &userName);
32 | void setPassword(const QString &password);
33 | void setAuthCode(const QString &authCode);
34 | void setStorePassword(bool storePassword);
35 |
36 | void login();
37 |
38 | signals:
39 | void userNameChanged(const QString &userName);
40 | void passwordChanged(const QString &password);
41 | void authCodeChanged(const QString &authCode);
42 | void storePasswordChanged(bool storePassword);
43 | void loadingChanged(bool loading);
44 |
45 | void loginSuccessful();
46 |
47 | protected:
48 | void onInit(const QVariantHash ¶ms) override;
49 |
50 | private slots:
51 | void apiError(const QString &errorString, int errorCode, QtRestClient::RestReply::ErrorType errorType);
52 |
53 | private:
54 | LocalSettings *_settings;
55 | UserClass *_user;
56 |
57 | QString _userName;
58 | QString _password;
59 | QString _authCode;
60 | bool _storePassword;
61 | bool _loading;
62 | };
63 |
64 | #endif // LOGINVIEWMODEL_H
65 |
--------------------------------------------------------------------------------
/gui/widgets/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include "mainwindow.h"
9 | #include "addanimedialog.h"
10 | #include "detailsdockwidget.h"
11 | #include "entrydialog.h"
12 | #include "logindialog.h"
13 | #include "widgetsupdatenotifier.h"
14 | #include "instancesetup.h"
15 |
16 | // Register the core app
17 | QTMVVM_REGISTER_CORE_APP(AniRemApp)
18 |
19 | namespace {
20 |
21 | QPointer currentMainWindow;
22 | QPointer currentInstance;
23 |
24 | }
25 |
26 | int main(int argc, char *argv[])
27 | {
28 | QtMvvm::CoreApp::disableAutoBoot();
29 |
30 | QApplication a(argc, argv);
31 |
32 | QSingleInstance instance;
33 | currentInstance = &instance;
34 | instance.setStartupFunction([&](){
35 | QtMvvm::ServiceRegistry::instance()->registerInterface();
36 |
37 | QtMvvm::registerDataSyncWidgets();
38 | QtMvvm::WidgetsPresenter::registerView();
39 | QtMvvm::WidgetsPresenter::registerView();
40 | QtMvvm::WidgetsPresenter::registerView();
41 | QtMvvm::WidgetsPresenter::registerView();
42 | QtMvvm::WidgetsPresenter::registerView();
43 |
44 | QObject::connect(&instance, &QSingleInstance::instanceMessage,
45 | coreApp, [](){
46 | if(!currentMainWindow)
47 | coreApp->show();
48 | });
49 |
50 | //workaround to force svg deployment
51 | QSvgRenderer _unused;
52 |
53 | coreApp->bootApp();
54 | return EXIT_SUCCESS;
55 | });
56 |
57 | return instance.singleExec();
58 | }
59 |
60 | void setMainWindow(QMainWindow *window)
61 | {
62 | currentMainWindow = window;
63 | if(currentInstance)
64 | currentInstance->setNotifyWindow(window);
65 | }
66 |
67 | QMainWindow *getMainWindow()
68 | {
69 | return currentMainWindow;
70 | }
71 |
--------------------------------------------------------------------------------
/gui/core/proxerentrymodel.h:
--------------------------------------------------------------------------------
1 | #ifndef PROXERENTRYMODEL_H
2 | #define PROXERENTRYMODEL_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class ProxerEntryModel : public QAbstractTableModel
9 | {
10 | Q_OBJECT
11 |
12 | Q_PROPERTY(ListType listType READ listType WRITE setListType NOTIFY listTypeChanged)
13 |
14 | public:
15 | enum ListType {
16 | ListAnimes = 0,
17 | ListMangas = 1
18 | };
19 | Q_ENUM(ListType)
20 |
21 | enum Roles {
22 | NameRole = Qt::DisplayRole,
23 | IdRole = Qt::UserRole + 1,
24 | TypeRole,
25 | DateRole,
26 | RatingRole,
27 |
28 | InvalidRole = -1
29 | };
30 | Q_ENUM(Roles)
31 |
32 | static const int PageSize;
33 |
34 | explicit ProxerEntryModel(SyncedSettings *settings, QObject *parent = nullptr);
35 |
36 | int getId(const QModelIndex &index) const;
37 |
38 | int rowCount(const QModelIndex &parent = {}) const override;
39 | int columnCount(const QModelIndex &parent = {}) const override;
40 | QHash roleNames() const override;
41 |
42 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
43 |
44 | bool canFetchMore(const QModelIndex &parent = {}) const override;
45 | void fetchMore(const QModelIndex &parent = {}) override;
46 |
47 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
48 |
49 | ListType listType() const;
50 |
51 | public slots:
52 | void setListType(ListType listType);
53 |
54 | signals:
55 | void loginNeeded();
56 | void apiError(const QString &errorString, int errorCode, QtRestClient::RestReply::ErrorType errorType);
57 |
58 | void listTypeChanged(ListType listType);
59 |
60 | private:
61 | SyncedSettings *_settings;
62 | UserClass *_user;
63 | ListType _listType;
64 | bool _isFetching;
65 | bool _skipNext;
66 |
67 | QList _data;
68 |
69 | Roles indexRole(const QModelIndex &index, int role) const;
70 | QString typeName(ListType listType) const;
71 | };
72 |
73 | #endif // PROXERENTRYMODEL_H
74 |
--------------------------------------------------------------------------------
/droid-service/android/src/de/skycoder42/anirem/BootReceiver.java:
--------------------------------------------------------------------------------
1 | package de.skycoder42.anirem;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.content.BroadcastReceiver;
6 | import android.content.SharedPreferences;
7 |
8 | import android.app.PendingIntent;
9 | import android.app.AlarmManager;
10 |
11 | public class BootReceiver extends BroadcastReceiver {
12 |
13 | @Override
14 | public void onReceive(Context context, Intent intent) {
15 | scheduleAutoCheck(context);
16 | }
17 |
18 | static public void scheduleAutoCheck(Context context) {
19 | SharedPreferences prefs = context.getSharedPreferences(Globals.AutoStartPrefs, Context.MODE_PRIVATE);
20 | scheduleAutoCheckImp(context, prefs.getBoolean(Globals.AutStartKey, true));
21 | }
22 |
23 | static public void scheduleAutoCheck(Context context, boolean autoCheck) {
24 | scheduleAutoCheckImp(context, autoCheck);
25 |
26 | SharedPreferences prefs = context.getSharedPreferences(Globals.AutoStartPrefs, Context.MODE_PRIVATE);
27 | SharedPreferences.Editor editor = prefs.edit();
28 | editor.putBoolean(Globals.AutStartKey, autoCheck);
29 | editor.apply();
30 | }
31 |
32 | static private void scheduleAutoCheckImp(Context context, boolean autoCheck) {
33 | Intent intent = new Intent(context, AniremService.class);
34 | PendingIntent pending;
35 | if(Globals.isOreo())
36 | pending = PendingIntent.getForegroundService(context, Globals.AlarmIntentId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
37 | else
38 | pending = PendingIntent.getService(context, Globals.AlarmIntentId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
39 |
40 | AlarmManager alarm = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
41 | if(autoCheck) {
42 | alarm.setRepeating(AlarmManager.RTC_WAKEUP,
43 | System.currentTimeMillis() + AlarmManager.INTERVAL_DAY,
44 | AlarmManager.INTERVAL_DAY - 3600000, //every 23 hours, to rotate over time
45 | pending);
46 | } else {
47 | alarm.cancel(pending);
48 | pending.cancel();
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/gui/quick/android/src/de/skycoder42/anirem/BootReceiver.java:
--------------------------------------------------------------------------------
1 | package de.skycoder42.anirem;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.content.BroadcastReceiver;
6 | import android.content.SharedPreferences;
7 |
8 | import android.app.PendingIntent;
9 | import android.app.AlarmManager;
10 |
11 | public class BootReceiver extends BroadcastReceiver {
12 |
13 | @Override
14 | public void onReceive(Context context, Intent intent) {
15 | scheduleAutoCheck(context);
16 | }
17 |
18 | static public void scheduleAutoCheck(Context context) {
19 | SharedPreferences prefs = context.getSharedPreferences(Globals.AutoStartPrefs, Context.MODE_PRIVATE);
20 | scheduleAutoCheckImp(context, prefs.getBoolean(Globals.AutStartKey, true));
21 | }
22 |
23 | static public void scheduleAutoCheck(Context context, boolean autoCheck) {
24 | scheduleAutoCheckImp(context, autoCheck);
25 |
26 | SharedPreferences prefs = context.getSharedPreferences(Globals.AutoStartPrefs, Context.MODE_PRIVATE);
27 | SharedPreferences.Editor editor = prefs.edit();
28 | editor.putBoolean(Globals.AutStartKey, autoCheck);
29 | editor.apply();
30 | }
31 |
32 | static private void scheduleAutoCheckImp(Context context, boolean autoCheck) {
33 | Intent intent = new Intent(context, AniremService.class);
34 | PendingIntent pending;
35 | if(Globals.isOreo())
36 | pending = PendingIntent.getForegroundService(context, Globals.AlarmIntentId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
37 | else
38 | pending = PendingIntent.getService(context, Globals.AlarmIntentId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
39 |
40 | AlarmManager alarm = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
41 | if(autoCheck) {
42 | alarm.setInexactRepeating(AlarmManager.RTC_WAKEUP,
43 | System.currentTimeMillis() + AlarmManager.INTERVAL_DAY,
44 | AlarmManager.INTERVAL_DAY - 3600000, //every 23 hours, to rotate over time
45 | pending);
46 | } else {
47 | alarm.cancel(pending);
48 | pending.cancel();
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/lib/storedcookiejar.cpp:
--------------------------------------------------------------------------------
1 | #include "storedcookiejar.h"
2 | #include
3 | #include
4 | #include
5 |
6 | const QString StoredCookieJar::CookieKey = QStringLiteral("cookies");
7 | const QString StoredCookieJar::CookieDataKey = QStringLiteral("raw");
8 |
9 | StoredCookieJar::StoredCookieJar(QObject *parent) :
10 | QNetworkCookieJar(parent),
11 | _cookieStore(nullptr),
12 | _scheduled(false)
13 | {
14 | QDir cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
15 | cacheDir.mkpath(QStringLiteral("."));
16 | _cookieStore = new QSettings(cacheDir.absoluteFilePath(QStringLiteral("api-cookie-jar.ini")),
17 | QSettings::IniFormat,
18 | this);
19 | auto cnt = _cookieStore->beginReadArray(CookieKey);
20 | QList cookies;
21 | cookies.reserve(cnt);
22 | for(auto i = 0; i < cnt; i++) {
23 | _cookieStore->setArrayIndex(i);
24 | cookies.append(QNetworkCookie::parseCookies(_cookieStore->value(CookieDataKey).toByteArray()));
25 | }
26 | _cookieStore->endArray();
27 | setAllCookies(cookies);
28 | }
29 |
30 | StoredCookieJar::~StoredCookieJar()
31 | {
32 | if(_scheduled)
33 | store();
34 | }
35 |
36 | bool StoredCookieJar::insertCookie(const QNetworkCookie &cookie)
37 | {
38 | auto ok = QNetworkCookieJar::insertCookie(cookie);
39 | if(ok && !_scheduled)
40 | _scheduled = QMetaObject::invokeMethod(this, "store", Qt::QueuedConnection);
41 | return ok;
42 | }
43 |
44 | bool StoredCookieJar::deleteCookie(const QNetworkCookie &cookie)
45 | {
46 | auto ok = QNetworkCookieJar::deleteCookie(cookie);
47 | if(ok && !_scheduled)
48 | _scheduled = QMetaObject::invokeMethod(this, "store", Qt::QueuedConnection);
49 | return ok;
50 | }
51 |
52 | void StoredCookieJar::store()
53 | {
54 | auto cookies = allCookies();
55 | _cookieStore->remove(CookieKey);
56 | _cookieStore->beginWriteArray(CookieKey, cookies.size());
57 | auto index = 0;
58 | for(auto cookie : cookies) {
59 | _cookieStore->setArrayIndex(index++);
60 | _cookieStore->setValue(CookieDataKey, cookie.toRawForm(QNetworkCookie::Full));
61 | }
62 | _cookieStore->endArray();
63 | _scheduled = false;
64 | }
65 |
--------------------------------------------------------------------------------
/gui/widgets/detailsdockwidget.cpp:
--------------------------------------------------------------------------------
1 | #include "detailsdockwidget.h"
2 | #include "ui_detailsdockwidget.h"
3 |
4 | #include
5 | #include
6 |
7 | DetailsDockWidget::DetailsDockWidget(QtMvvm::ViewModel *viewModel, QWidget *parent) :
8 | QDockWidget(parent),
9 | _viewModel(static_cast(viewModel)),
10 | _ui(new Ui::DetailsDockWidget)
11 | {
12 | _ui->setupUi(this);
13 | setMinimumWidth(_ui->previewLabel->minimumWidth() +
14 | 8 +
15 | style()->pixelMetric(QStyle::PM_ScrollBarExtent));
16 |
17 | connect(_viewModel->imageLoader(), &ImageLoader::imageLoaded,
18 | this, &DetailsDockWidget::imageLoaded);
19 | connect(_viewModel->imageLoader(), &ImageLoader::imageLoadFailed,
20 | this, &DetailsDockWidget::imageLoadFailed);
21 |
22 | connect(_viewModel, &DetailsViewModel::animeInfoChanged,
23 | this, &DetailsDockWidget::updateInfo);
24 | updateInfo();
25 |
26 | QMetaObject::invokeMethod(parent, "addDockAction",
27 | Q_ARG(QAction*, toggleViewAction()));
28 | }
29 |
30 | DetailsDockWidget::~DetailsDockWidget()
31 | {
32 | delete _ui;
33 | }
34 |
35 | void DetailsDockWidget::closeEvent(QCloseEvent *event)
36 | {
37 | event->ignore();
38 | hide();
39 | }
40 |
41 | void DetailsDockWidget::updateInfo()
42 | {
43 | auto info = _viewModel->animeInfo();
44 | if(info) {
45 | setWindowTitle(tr("%1 Details").arg(info.title()));
46 |
47 | _ui->previewLabel->setText(tr("Loading preview image…"));
48 | _viewModel->imageLoader()->loadImage(info.id());
49 | _ui->detailsLabel->setText(_viewModel->detailsText());
50 | } else {
51 | setWindowTitle(tr("Anime Details"));
52 | _ui->previewLabel->clear();
53 | _ui->detailsLabel->clear();
54 | }
55 | }
56 |
57 | void DetailsDockWidget::imageLoaded(int id, const QImage &image)
58 | {
59 | if(_viewModel->animeInfo() && _viewModel->animeInfo().id() == id)
60 | _ui->previewLabel->setPixmap(QPixmap::fromImage(image));
61 | }
62 |
63 | void DetailsDockWidget::imageLoadFailed(int id, const QString &error)
64 | {
65 | if(_viewModel->animeInfo() && _viewModel->animeInfo().id() == id) {
66 | qWarning().noquote() << error;
67 | _ui->previewLabel->setText(tr("No image"));
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/gui/core/addanimeviewmodel.h:
--------------------------------------------------------------------------------
1 | #ifndef ADDANIMEVIEWMODEL_H
2 | #define ADDANIMEVIEWMODEL_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include "imageloader.h"
11 |
12 | class AddAnimeViewModel : public QtMvvm::ViewModel
13 | {
14 | Q_OBJECT
15 |
16 | Q_PROPERTY(int id READ id WRITE setId NOTIFY idChanged)
17 | Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
18 | Q_PROPERTY(QStringListModel* nameModel READ nameModel CONSTANT)
19 | Q_PROPERTY(bool loading READ isLoading NOTIFY loadingChanged)
20 | Q_PROPERTY(bool acceptable READ isAcceptable NOTIFY acceptableChanged)
21 |
22 | QTMVVM_INJECT_PROP(ImageLoader*, imageLoader, _loader)
23 | QTMVVM_INJECT_PROP(SeasonStatusLoader*, updater, _updater)
24 |
25 | public:
26 | static const QString ParamId;
27 |
28 | static QVariantHash params(int id);
29 |
30 | Q_INVOKABLE explicit AddAnimeViewModel(QObject *parent = nullptr);
31 |
32 | int id() const;
33 | QString title() const;
34 | QStringListModel* nameModel() const;
35 | bool isLoading() const;
36 | bool isAcceptable() const;
37 |
38 | ImageLoader* imageLoader() const;
39 |
40 | public slots:
41 | bool accept(bool allowInvalid = false);
42 |
43 | void setId(int id);
44 | void setTitle(QString title);
45 |
46 | signals:
47 | void close();
48 |
49 | void idChanged(int id);
50 | void titleChanged(QString title);
51 | void allNamesChanged(QStringList allNames);
52 | void loadingChanged(bool loading);
53 | void acceptableChanged(bool acceptable);
54 |
55 | protected:
56 | void onInit(const QVariantHash ¶ms) override;
57 |
58 | private slots:
59 | void error(const QString &errorString, int errorCode, QtRestClient::RestReply::ErrorType errorType);
60 |
61 | private:
62 | AniremStore *_store;
63 | InfoClass *_infoClass;
64 | ImageLoader *_loader;
65 | SeasonStatusLoader *_updater;
66 | QStringListModel *_nameModel;
67 |
68 | int _id;
69 | QString _title;
70 | bool _loading;
71 | bool _acceptable;
72 |
73 | void updateLoading(bool loading);
74 | void setAcceptable(bool acceptable);
75 | };
76 |
77 | #endif // ADDANIMEVIEWMODEL_H
78 |
--------------------------------------------------------------------------------
/gui/quick/AnimeInfoDelegate.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.10
2 | import QtQuick.Controls 2.3
3 | import QtQuick.Controls.Material 2.3
4 | import QtQuick.Controls.Universal 2.3
5 | import QtQuick.Layouts 1.3
6 | import de.skycoder42.QtMvvm.Quick 1.0
7 |
8 | SwipeDelegate {
9 | id: delegate
10 | width: parent.width
11 | text: title
12 | highlighted: hasNewSeasons === "true"
13 |
14 | signal animeDeleted
15 |
16 | function redColor() {
17 | if(QuickPresenter.currentStyle === "Material")
18 | return Material.color(Material.Red);
19 | else if(QuickPresenter.currentStyle === "Universal")
20 | return Universal.color(Universal.Red);
21 | else
22 | return "#FF0000";
23 | }
24 |
25 | contentItem: RowLayout {
26 | Image {
27 | id: previewImage
28 | asynchronous: true
29 | fillMode: Image.PreserveAspectFit
30 | horizontalAlignment: Image.AlignHCenter
31 | verticalAlignment: Image.AlignVCenter
32 | Layout.minimumWidth: 42
33 | Layout.maximumWidth: 42
34 | Layout.minimumHeight: 42
35 | Layout.maximumHeight: 42
36 | source: "image://proxer/%1".arg(id)
37 | mipmap: true
38 | }
39 |
40 | Label {
41 | id: titleLabel
42 | elide: Label.ElideRight
43 | horizontalAlignment: Qt.AlignLeft
44 | verticalAlignment: Qt.AlignVCenter
45 | Layout.fillWidth: true
46 | Layout.fillHeight: true
47 | font.bold: delegate.highlighted
48 | Material.foreground: delegate.highlighted ? Material.accent : delegate.Material.foreground
49 | text: delegate.text
50 | }
51 |
52 | Label {
53 | id: countLabel
54 | Layout.fillHeight: true
55 | horizontalAlignment: Qt.AlignRight
56 | verticalAlignment: Qt.AlignVCenter
57 | Material.foreground: delegate.highlighted ? Material.accent : delegate.Material.foreground
58 | text: totalSeasonCount
59 | }
60 | }
61 |
62 | swipe.right: Rectangle {
63 | anchors.fill: parent
64 | color: redColor()
65 |
66 | MouseArea {
67 | anchors.fill: parent
68 |
69 | ActionButton {
70 | anchors.centerIn: parent
71 | icon.name: "user-trash"
72 | icon.source: "qrc:/icons/ic_delete_forever.svg"
73 | text: qsTr("Delete Anime")
74 |
75 | Material.foreground: "white"
76 | Universal.foreground: "white"
77 |
78 | onClicked: {
79 | delegate.swipe.close();
80 | animeDeleted()
81 | }
82 | }
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/gui/quick/proxerimageprovider.cpp:
--------------------------------------------------------------------------------
1 | #include "proxerimageprovider.h"
2 | #include
3 |
4 | ProxerImageProvider::ProxerImageProvider() :
5 | QQuickAsyncImageProvider(),
6 | _loader(QtMvvm::ServiceRegistry::instance()->service())
7 | {}
8 |
9 | QQmlImageProviderBase::ImageType ProxerImageProvider::imageType() const
10 | {
11 | return QQmlImageProviderBase::ImageResponse;
12 | }
13 |
14 | QQmlImageProviderBase::Flags ProxerImageProvider::flags() const
15 | {
16 | return QQmlImageProviderBase::ForceAsynchronousImageLoading;
17 | }
18 |
19 | QQuickImageResponse *ProxerImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
20 | {
21 | auto ok = false;
22 | auto rId = id.toInt(&ok);
23 | if(ok) {
24 | auto response = new ProxerImageResponse(rId, requestedSize);
25 | QObject::connect(_loader, &ImageLoader::imageLoaded,
26 | response, &ProxerImageResponse::imageLoaded,
27 | Qt::QueuedConnection);
28 | QObject::connect(_loader, &ImageLoader::imageLoadFailed,
29 | response, &ProxerImageResponse::imageLoadFailed,
30 | Qt::QueuedConnection);
31 | _loader->loadImage(rId);
32 | return response;
33 | } else
34 | return nullptr;
35 | }
36 |
37 |
38 |
39 | ProxerImageResponse::ProxerImageResponse(int id, const QSize &requestedSize) :
40 | _id(id),
41 | _requestedSize(requestedSize),
42 | _result(),
43 | _error()
44 | {}
45 |
46 | QQuickTextureFactory *ProxerImageResponse::textureFactory() const
47 | {
48 | if(_result.isNull())
49 | return nullptr;
50 | else
51 | return QQuickTextureFactory::textureFactoryForImage(_result);
52 | }
53 |
54 | QString ProxerImageResponse::errorString() const
55 | {
56 | return _error;
57 | }
58 |
59 | void ProxerImageResponse::cancel()
60 | {
61 | _id = -1;
62 | _result = QImage();
63 | _error.clear();
64 | emit finished();
65 | }
66 |
67 | void ProxerImageResponse::imageLoaded(int id, const QImage &image)
68 | {
69 | if(id == _id) {
70 | if(_requestedSize.isValid())
71 | _result = image.scaled(_requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
72 | else
73 | _result = image;
74 | emit finished();
75 | }
76 | }
77 |
78 | void ProxerImageResponse::imageLoadFailed(int id, const QString &errorString)
79 | {
80 | if(id == _id) {
81 | _error = errorString;
82 | emit finished();
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/gui/core/entryviewmodel.cpp:
--------------------------------------------------------------------------------
1 | #include "entryviewmodel.h"
2 | #include
3 | #include
4 | #include
5 | #include "loginviewmodel.h"
6 | #include "addanimeviewmodel.h"
7 |
8 | EntryViewModel::EntryViewModel(QObject *parent) :
9 | ViewModel(parent),
10 | _settings(nullptr),
11 | _model(nullptr),
12 | _showingChild(false)
13 | {}
14 |
15 | ProxerEntryModel *EntryViewModel::model() const
16 | {
17 | return _model;
18 | }
19 |
20 | int EntryViewModel::typeIndex() const
21 | {
22 | return _model ? _model->listType() : ProxerEntryModel::ListAnimes;
23 | }
24 |
25 | QStringList EntryViewModel::listTypes() const
26 | {
27 | return {
28 | tr("Animes"),
29 | tr("Mangas")
30 | };
31 | }
32 |
33 | void EntryViewModel::addAnime(int id)
34 | {
35 | if(id == -1)
36 | return;
37 |
38 | if(!_showingChild) {
39 | _showingChild = true;
40 | showForResult(AddRequestCode, AddAnimeViewModel::params(id));
41 | }
42 | }
43 |
44 | void EntryViewModel::setTypeIndex(int typeIndex)
45 | {
46 | if(_model)
47 | _model->setListType(static_cast(typeIndex));
48 | }
49 |
50 | void EntryViewModel::onInit(const QVariantHash ¶ms)
51 | {
52 | Q_UNUSED(params)
53 |
54 | _model = new ProxerEntryModel(_settings, this);
55 | connect(_model, &ProxerEntryModel::listTypeChanged,
56 | this, &EntryViewModel::typeIndexChanged);
57 | connect(_model, &ProxerEntryModel::loginNeeded,
58 | this, &EntryViewModel::loginNeeded);
59 | connect(_model, &ProxerEntryModel::apiError,
60 | this, &EntryViewModel::apiError);
61 | emit modelChanged(_model);
62 | }
63 |
64 | void EntryViewModel::onResult(quint32 requestCode, const QVariant &result)
65 | {
66 | switch(requestCode) {
67 | case LoginRequestCode:
68 | _showingChild = false;
69 | if(result.toBool())
70 | _model->fetchMore();
71 | break;
72 | case AddRequestCode:
73 | _showingChild = false;
74 | break;
75 | default:
76 | break;
77 | }
78 | }
79 |
80 | void EntryViewModel::loginNeeded()
81 | {
82 | if(!_showingChild) {
83 | _showingChild = true;
84 | showForResult(LoginRequestCode);
85 | }
86 | }
87 |
88 | void EntryViewModel::apiError(const QString &errorString, int errorCode, QtRestClient::RestReply::ErrorType errorType)
89 | {
90 | QtMvvm::critical(tr("Failed to get entries"),
91 | ApiHelper::formatError(errorString, errorCode, errorType));
92 | }
93 |
--------------------------------------------------------------------------------
/lib/passiveupdater.cpp:
--------------------------------------------------------------------------------
1 | #include "passiveupdater.h"
2 | #include
3 |
4 | PassiveUpdater::PassiveUpdater(QObject *parent) :
5 | QObject(parent),
6 | _loader(nullptr),
7 | _notifier(nullptr),
8 | _manager(new QtDataSync::SyncManager(this)),
9 | _store(new AniremStore(this))
10 | {}
11 |
12 | void PassiveUpdater::performUpdateCheck()
13 | {
14 | //WORKAROUND remove once patched
15 | qtmvvm_init();
16 |
17 | auto startFn = [this]() {
18 | _manager->runOnSynchronized([this](QtDataSync::SyncManager::SyncState state){
19 | Q_UNUSED(state)
20 | _loader->checkForUpdates(true);
21 | }, false);
22 | };
23 |
24 | if(_manager->replica()->isInitialized())
25 | startFn();
26 | else {
27 | connect(_manager->replica(), &QRemoteObjectReplica::initialized,
28 | this, startFn);
29 | }
30 | }
31 |
32 | void PassiveUpdater::qtmvvm_init()
33 | {
34 | connect(_loader, &SeasonStatusLoader::completed,
35 | this, &PassiveUpdater::completed);
36 | connect(_loader, SIGNAL(updated(int,int)),
37 | dynamic_cast(_notifier), SLOT(updateProgress(int,int)));
38 | }
39 |
40 | void PassiveUpdater::completed(bool hasUpdates, const QString &errorString)
41 | {
42 | if(!errorString.isEmpty()) {
43 | _notifier->showNotification(false, tr("Season check failed!"), errorString);
44 | emit done(true);
45 | } else {
46 | try {
47 | // show the notification
48 | auto allInfos = _store->loadAll();
49 | QStringList updatesList;
50 | for(const auto &anime : allInfos) {
51 | if(anime.hasNewSeasons()) {
52 | updatesList.append(tr("• %1 has now %n relation(s)", "", anime.totalSeasonCount())
53 | .arg(anime.title()));
54 | }
55 | }
56 |
57 | if(!updatesList.isEmpty()) {
58 | _notifier->showNotification(true,
59 | tr("%n new relation(s) detected!", "", updatesList.size()),
60 | updatesList.join(QLatin1Char('\n')));
61 | }
62 |
63 | // if updated: sync then done, otherwise done
64 | if(hasUpdates) {
65 | _manager->runOnSynchronized([this](QtDataSync::SyncManager::SyncState state){
66 | Q_UNUSED(state)
67 | emit done(true);
68 | });
69 | } else
70 | emit done(!updatesList.isEmpty());
71 | } catch(QException &e) {
72 | qCritical() << "Failed to present update result with error" << e.what();
73 | _notifier->showNotification(false, tr("Season check failed!"), tr("Failed to load info of updated Animes"));
74 | emit done(true);
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/droid-service/android/src/de/skycoder42/anirem/AniremActivity.java:
--------------------------------------------------------------------------------
1 | package de.skycoder42.anirem;
2 |
3 | import android.os.Bundle;
4 | import android.os.IBinder;
5 |
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.ServiceConnection;
9 | import android.content.ComponentName;
10 |
11 | import android.app.NotificationManager;
12 | import android.app.NotificationChannel;
13 |
14 | import org.qtproject.qt5.android.bindings.QtActivity;
15 |
16 | public class AniremActivity extends QtActivity {
17 | private boolean _isBound;
18 | private ServiceConnection _connection = new ServiceConnection() {
19 | @Override
20 | public void onServiceConnected(ComponentName className, IBinder service) {
21 | _isBound = true;
22 | }
23 |
24 | @Override
25 | public void onServiceDisconnected(ComponentName className) {
26 | _isBound = false;
27 | }
28 | };
29 |
30 | @Override
31 | public void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | createChannels();
34 |
35 | Intent intent = new Intent(this, AniremService.class);
36 | bindService(intent, _connection, Context.BIND_AUTO_CREATE);
37 | }
38 |
39 | @Override
40 | public void onDestroy() {
41 | super.onDestroy();
42 | if (_isBound) {
43 | unbindService(_connection);
44 | _isBound = false;
45 | }
46 | }
47 |
48 | private void createChannels() {
49 | if(!Globals.isOreo())
50 | return;
51 |
52 | NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
53 |
54 | //create normal channel
55 | NotificationChannel normal = new NotificationChannel(Globals.NormalChannelId,
56 | getString(R.string.channel_nrm_name),
57 | NotificationManager.IMPORTANCE_DEFAULT);
58 | normal.setDescription(getString(R.string.channel_nrm_desc));
59 | normal.enableLights(true);
60 | normal.setLightColor(Globals.NormalColor);
61 | normal.enableVibration(true);
62 | normal.setShowBadge(true);
63 | manager.createNotificationChannel(normal);
64 |
65 | //create foreground channel
66 | NotificationChannel foreground = new NotificationChannel(Globals.ForegroundChannelId,
67 | getString(R.string.channel_svc_name),
68 | NotificationManager.IMPORTANCE_MIN);
69 | foreground.setDescription(getString(R.string.channel_svc_desc));
70 | foreground.enableLights(false);
71 | foreground.enableVibration(false);
72 | foreground.setShowBadge(false);
73 | manager.createNotificationChannel(foreground);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/gui/quick/android/src/de/skycoder42/anirem/AniremActivity.java:
--------------------------------------------------------------------------------
1 | package de.skycoder42.anirem;
2 |
3 | import android.os.Bundle;
4 | import android.os.IBinder;
5 |
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.ServiceConnection;
9 | import android.content.ComponentName;
10 |
11 | import android.app.NotificationManager;
12 | import android.app.NotificationChannel;
13 |
14 | import org.qtproject.qt5.android.bindings.QtActivity;
15 |
16 | public class AniremActivity extends QtActivity {
17 | private boolean _isBound;
18 | private ServiceConnection _connection = new ServiceConnection() {
19 | @Override
20 | public void onServiceConnected(ComponentName className, IBinder service) {
21 | _isBound = true;
22 | }
23 |
24 | @Override
25 | public void onServiceDisconnected(ComponentName className) {
26 | _isBound = false;
27 | }
28 | };
29 |
30 | @Override
31 | public void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | createChannels();
34 |
35 | Intent intent = new Intent(this, AniremService.class);
36 | bindService(intent, _connection, Context.BIND_AUTO_CREATE);
37 | }
38 |
39 | @Override
40 | public void onDestroy() {
41 | super.onDestroy();
42 | if (_isBound) {
43 | unbindService(_connection);
44 | _isBound = false;
45 | }
46 | }
47 |
48 | private void createChannels() {
49 | if(!Globals.isOreo())
50 | return;
51 |
52 | NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
53 |
54 | //create normal channel
55 | NotificationChannel normal = new NotificationChannel(Globals.NormalChannelId,
56 | getString(R.string.channel_nrm_name),
57 | NotificationManager.IMPORTANCE_DEFAULT);
58 | normal.setDescription(getString(R.string.channel_nrm_desc));
59 | normal.enableLights(true);
60 | normal.setLightColor(Globals.NormalColor);
61 | normal.enableVibration(true);
62 | normal.setShowBadge(true);
63 | manager.createNotificationChannel(normal);
64 |
65 | //create foreground channel
66 | NotificationChannel foreground = new NotificationChannel(Globals.ForegroundChannelId,
67 | getString(R.string.channel_svc_name),
68 | NotificationManager.IMPORTANCE_MIN);
69 | foreground.setDescription(getString(R.string.channel_svc_desc));
70 | foreground.enableLights(false);
71 | foreground.enableVibration(false);
72 | foreground.setShowBadge(false);
73 | manager.createNotificationChannel(foreground);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/.qmake.conf:
--------------------------------------------------------------------------------
1 | VERSION = 2.0.1
2 |
3 | CONFIG += warning_clean exceptions
4 | android|clang: CONFIG += c++14
5 | else: CONFIG += c++17
6 | DEFINES += QT_DEPRECATED_WARNINGS QT_ASCII_CAST_WARNINGS QT_USE_QSTRINGBUILDER
7 |
8 | QMAKE_TARGET_COMPANY = "Skycoder42"
9 | QMAKE_TARGET_DESCRIPTION = "Anime season reminder"
10 | QMAKE_TARGET_COPYRIGHT = "Felix Barz"
11 | QMAKE_TARGET_BUNDLE_PREFIX = de.skycoder42
12 |
13 | DEFINES += "VERSION=\\\"$$VERSION\\\""
14 | DEFINES += "COMPANY=\"\\\"$$QMAKE_TARGET_COMPANY\\\"\""
15 | DEFINES += "BUNDLE=\"\\\"$$QMAKE_TARGET_BUNDLE_PREFIX\\\"\""
16 |
17 | # installing
18 | exists($$PWD/install.pri):include($$PWD/install.pri)
19 |
20 | android: INSTALL_TRANSLATIONS = /assets/translations
21 | else:create_installer {
22 | CONFIG += no_quick
23 | QTIFW_PKG_ROOT = /packages/de.skycoder42.anirem
24 | INSTALL_PREFIX = $$QTIFW_PKG_ROOT/data
25 | !mac: INSTALL_BINS = $$INSTALL_PREFIX
26 | win32: INSTALL_LIBS = $$INSTALL_BINS
27 | }
28 |
29 | isEmpty(INSTALL_PREFIX) {
30 | INSTALL_PREFIX = $$[QT_INSTALL_PREFIX]
31 | isEmpty(INSTALL_BINS): INSTALL_BINS = $$[QT_INSTALL_BINS]
32 | isEmpty(INSTALL_LIBS): INSTALL_LIBS = $$[QT_INSTALL_LIBS]
33 | isEmpty(INSTALL_HEADERS): INSTALL_HEADERS = $$[QT_INSTALL_HEADERS]
34 | isEmpty(INSTALL_PLUGINS): INSTALL_PLUGINS = $$[QT_INSTALL_PLUGINS]
35 | isEmpty(INSTALL_TRANSLATIONS): INSTALL_TRANSLATIONS = $$[QT_INSTALL_TRANSLATIONS]
36 | } else:mac {
37 | APP_PREFIX = Ani-Rem.app/Contents
38 | isEmpty(INSTALL_APPS): INSTALL_APPS = $${INSTALL_PREFIX}/.app-tmp
39 | isEmpty(INSTALL_BINS): INSTALL_BINS = $${INSTALL_PREFIX}/$${APP_PREFIX}/MacOS
40 | isEmpty(INSTALL_LIBS): INSTALL_LIBS = $${INSTALL_PREFIX}/$${APP_PREFIX}/Frameworks
41 | isEmpty(INSTALL_HEADERS): INSTALL_HEADERS = $${INSTALL_PREFIX}/$${APP_PREFIX}/Headers
42 | isEmpty(INSTALL_PLUGINS): INSTALL_PLUGINS = $${INSTALL_PREFIX}/$${APP_PREFIX}/PlugIns
43 | isEmpty(INSTALL_TRANSLATIONS): INSTALL_TRANSLATIONS = $${INSTALL_PREFIX}/$${APP_PREFIX}/Resources/translations
44 | isEmpty(INSTALL_SHARE): INSTALL_SHARE = $${INSTALL_PREFIX}/$${APP_PREFIX}/Resources
45 | } else {
46 | isEmpty(INSTALL_BINS): INSTALL_BINS = $${INSTALL_PREFIX}/bin
47 | isEmpty(INSTALL_LIBS): INSTALL_LIBS = $${INSTALL_PREFIX}/lib
48 | isEmpty(INSTALL_HEADERS): INSTALL_HEADERS = $${INSTALL_PREFIX}/include
49 | isEmpty(INSTALL_PLUGINS): INSTALL_PLUGINS = $${INSTALL_PREFIX}/plugins
50 | isEmpty(INSTALL_TRANSLATIONS): INSTALL_TRANSLATIONS = $${INSTALL_PREFIX}/translations
51 | }
52 | isEmpty(INSTALL_SHARE): INSTALL_SHARE = $${INSTALL_PREFIX}/share
53 |
--------------------------------------------------------------------------------
/gui/core/mainviewmodel.h:
--------------------------------------------------------------------------------
1 | #ifndef MAINVIEWMODEL_H
2 | #define MAINVIEWMODEL_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include
11 | class DetailsViewModel;
12 |
13 | class MainViewModel : public QtMvvm::ViewModel
14 | {
15 | Q_OBJECT
16 |
17 | Q_PROPERTY(QtDataSync::DataStoreModel* animeModel READ animeModel CONSTANT)
18 | Q_PROPERTY(QSortFilterProxyModel* sortedModel READ sortedModel CONSTANT)
19 | Q_PROPERTY(bool reloadingAnimes READ isReloadingAnimes NOTIFY reloadingAnimesChanged)
20 | Q_PROPERTY(QString filterString READ filterString WRITE setFilterString NOTIFY filterStringChanged)
21 |
22 | QTMVVM_INJECT_PROP(SyncedSettings*, settings, _settings)
23 | QTMVVM_INJECT_PROP(SeasonStatusLoader*, updater, _updater)
24 |
25 | public:
26 | Q_INVOKABLE explicit MainViewModel(QObject *parent = nullptr);
27 |
28 | QtDataSync::DataStoreModel* animeModel() const;
29 | QSortFilterProxyModel* sortedModel() const;
30 |
31 | bool isReloadingAnimes() const;
32 |
33 | QString filterString() const;
34 |
35 | public slots:
36 | void reload();
37 | void showSettings();
38 | void showSync();
39 | void showAbout();
40 | void showCaptcha();
41 |
42 | void uncheckAnime(int id);
43 | void itemAction(int id);
44 |
45 | void addAnime();
46 | void addAnimeBlank();
47 | void addAnimeFromEntryList();
48 | void addAnimeFromClipboard();
49 | void showDetails(int id);
50 | void openUrl(int id);
51 |
52 | void removeAnime(int id);
53 |
54 | void setFilterString(QString filterString);
55 |
56 | signals:
57 | void showStatus(const QString &message);
58 | void setProgress(int value, int max);
59 |
60 | void reloadingAnimesChanged(bool reloadingAnimes);
61 |
62 | void filterStringChanged(QString filterString);
63 |
64 | protected:
65 | void onInit(const QVariantHash ¶ms) override;
66 |
67 | private slots:
68 | void setDetailsView(QPointer details);
69 | void updaterStarted();
70 | void updaterDone(bool hasUpdates, const QString &error);
71 |
72 | private:
73 | QtDataSync::DataStoreModel *_model;
74 | QSortFilterProxyModel *_sortModel;
75 | SyncedSettings *_settings;
76 | SeasonStatusLoader *_updater;
77 | QString _filterString;
78 | bool _loading;
79 | bool _showNoChanges;
80 | int _migrationMax;
81 |
82 | QPointer _currentDetails;
83 |
84 | AnimeInfo infoFromId(int id) const;
85 | };
86 |
87 | #endif // MAINVIEWMODEL_H
88 |
--------------------------------------------------------------------------------
/lib/animeinfo.h:
--------------------------------------------------------------------------------
1 | #ifndef ANIMEINFO_H
2 | #define ANIMEINFO_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include "lib_anirem_global.h"
11 | #include "syncedsettings.h"
12 |
13 | class AnimeInfoData;
14 | class LIB_ANIREM_EXPORT AnimeInfo
15 | {
16 | Q_GADGET
17 | Q_DECLARE_TR_FUNCTIONS(AnimeInfo)
18 |
19 | Q_PROPERTY(int id READ id WRITE setId USER true)
20 | Q_PROPERTY(QString title READ title WRITE setTitle)
21 | Q_PROPERTY(AnimeInfo::SeasonMap seasonState READ seasonState WRITE setSeasonState)
22 | Q_PROPERTY(int totalSeasonCount READ totalSeasonCount STORED false)
23 | Q_PROPERTY(bool hasNewSeasons READ hasNewSeasons STORED false)
24 | Q_PROPERTY(QDateTime lastUpdateCheck READ lastUpdateCheck WRITE setLastUpdateCheck)
25 | Q_PROPERTY(QUrl relationsUrl READ relationsUrl CONSTANT STORED false)
26 |
27 | public:
28 | enum SeasonType {
29 | Anime,
30 | Ova,
31 | Movie,
32 | Hentai,
33 | Manga,
34 | Oneshot,
35 | Doujin,
36 | Hmanga,
37 | Unknown
38 | };
39 | Q_ENUM(SeasonType)
40 |
41 | using SeasonInfo = QPair; //(count, changed)
42 | using SeasonMap = QMap;
43 |
44 | Q_INVOKABLE AnimeInfo();
45 | AnimeInfo(int id, const QString &title);
46 | AnimeInfo(const AnimeInfo &other);
47 | AnimeInfo &operator=(const AnimeInfo &other);
48 | ~AnimeInfo();
49 |
50 | operator bool() const;
51 | bool operator!() const;
52 |
53 | int id() const;
54 | QString title() const;
55 | SeasonMap seasonState() const;
56 | SeasonInfo seasonInfo(SeasonType type) const;
57 | int totalSeasonCount() const;
58 | bool hasNewSeasons() const;
59 | QDateTime lastUpdateCheck() const;
60 | QUrl relationsUrl() const;
61 |
62 | void setSeasonState(const SeasonMap &seasonState);
63 | void setLastUpdateCheck(QDateTime lastUpdateCheck);
64 |
65 | Q_INVOKABLE void setAllUnchanged();
66 | Q_INVOKABLE void setSeasonInfo(SeasonType type, SeasonInfo info);
67 |
68 | Q_INVOKABLE static QString typeToString(SeasonType type);
69 |
70 | bool operator==(const AnimeInfo &other) const;
71 | bool operator!=(const AnimeInfo &other) const;
72 |
73 | static std::tuple apiMediumToType(const QString &medium, SyncedSettings *settings);
74 |
75 | private:
76 | QSharedDataPointer d;
77 |
78 | void setId(int id);
79 | void setTitle(const QString &title);
80 | };
81 |
82 | Q_DECLARE_METATYPE(AnimeInfo::SeasonInfo)
83 | Q_DECLARE_METATYPE(AnimeInfo::SeasonMap)
84 |
85 | #endif // ANIMEINFO_H
86 |
--------------------------------------------------------------------------------
/gui/widgets/widgetsupdatenotifier.cpp:
--------------------------------------------------------------------------------
1 | #include "widgetsupdatenotifier.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include "instancesetup.h"
7 |
8 | WidgetsUpdateNotifier::WidgetsUpdateNotifier(QObject *parent) :
9 | QObject(parent),
10 | _trayIcon(new QSystemTrayIcon(QApplication::windowIcon(), this)),
11 | _trayMenu(new QMenu()),
12 | _blinkTimer(new QTimer(this)),
13 | _normalIcon(QStringLiteral(":/icons/main.ico")),
14 | _invertIcon(QStringLiteral(":/icons/inverted.ico")),
15 | _currentNormal(true)
16 | {
17 | _trayMenu->addAction(QIcon::fromTheme(QStringLiteral("gtk-execute")), tr("Show Main Window"),
18 | this, &WidgetsUpdateNotifier::showMainViewModel);
19 | _trayMenu->addAction(QIcon::fromTheme(QStringLiteral("gtk-quit")), tr("Quit"),
20 | this, &WidgetsUpdateNotifier::quit);
21 | _trayIcon->setContextMenu(_trayMenu.data());
22 |
23 | _trayIcon->setToolTip(QApplication::applicationDisplayName());
24 | connect(_trayIcon, &QSystemTrayIcon::activated,
25 | this, &WidgetsUpdateNotifier::trayActivated);
26 | connect(_trayIcon, &QSystemTrayIcon::messageClicked,
27 | this, &WidgetsUpdateNotifier::showMainViewModel);
28 |
29 | _blinkTimer->setInterval(500);
30 | connect(_blinkTimer, &QTimer::timeout,
31 | this, &WidgetsUpdateNotifier::timerTriggered);
32 | }
33 |
34 | void WidgetsUpdateNotifier::showNotification(bool isPositive, const QString &title, const QString &description)
35 | {
36 | _trayIcon->show();
37 | _blinkTimer->start();
38 | _trayIcon->showMessage(title, description, isPositive ? QSystemTrayIcon::Information : QSystemTrayIcon::Critical);
39 | }
40 |
41 | void WidgetsUpdateNotifier::updateProgress(int value, int max)
42 | {
43 | Q_UNUSED(value)
44 | Q_UNUSED(max)
45 | }
46 |
47 | void WidgetsUpdateNotifier::timerTriggered()
48 | {
49 | if(_currentNormal)
50 | _trayIcon->setIcon(_invertIcon);
51 | else
52 | _trayIcon->setIcon(_normalIcon);
53 | _currentNormal = !_currentNormal;
54 | if(getMainWindow()) {
55 | _trayIcon->hide();
56 | _blinkTimer->stop();
57 | }
58 | }
59 |
60 | void WidgetsUpdateNotifier::trayActivated(QSystemTrayIcon::ActivationReason reason)
61 | {
62 | switch (reason) {
63 | case QSystemTrayIcon::DoubleClick:
64 | case QSystemTrayIcon::Trigger:
65 | showMainViewModel();
66 | break;
67 | default:
68 | break;
69 | }
70 | }
71 |
72 | void WidgetsUpdateNotifier::showMainViewModel()
73 | {
74 | _trayIcon->hide();
75 | _blinkTimer->stop();
76 | coreApp->show();
77 | }
78 |
79 | void WidgetsUpdateNotifier::quit()
80 | {
81 | _trayIcon->hide();
82 | _blinkTimer->stop();
83 | qApp->quit();
84 | }
85 |
--------------------------------------------------------------------------------
/gui/quick/DetailsView.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.10
2 | import QtQuick.Controls 2.3
3 | import QtQuick.Layouts 1.3
4 | import de.skycoder42.QtMvvm.Core 1.0
5 | import de.skycoder42.QtMvvm.Quick 1.0
6 | import de.skycoder42.anirem 1.0
7 |
8 | Page {
9 | id: detailsView
10 | property DetailsViewModel viewModel: null
11 |
12 | header: ContrastToolBar {
13 | id: toolbar
14 | RowLayout {
15 | anchors.fill: parent
16 | spacing: 0
17 |
18 | ActionButton {
19 | text: qsTr("Go back")
20 | icon.name: "arrow-left"
21 | icon.source: "qrc:/icons/ic_arrow_back.svg"
22 | onClicked: QuickPresenter.popView();
23 | }
24 |
25 | ToolBarLabel {
26 | text: qsTr("%1 Details").arg(viewModel.animeInfo ? viewModel.animeInfo.title : qsTr("Anime"))
27 | Layout.fillWidth: true
28 | }
29 |
30 | ActionButton {
31 | id: unmarkAction
32 | icon.name: "dialog-ok-applyk"
33 | icon.source: "qrc:/icons/ic_check.svg"
34 | text: qsTr("Unmark new seasons")
35 | onClicked: viewModel.uncheckAnime()
36 | visible: viewModel.animeInfo ? viewModel.animeInfo.hasNewSeasons : false
37 | }
38 |
39 | ActionButton {
40 | id: openInBrowser
41 | icon.name: "internet-web-browser"
42 | icon.source: "qrc:/icons/ic_open_in_browser.svg"
43 | text: qsTr("Open relations in your browser")
44 | onClicked: Qt.openUrlExternally(viewModel.animeInfo.relationsUrl)
45 | }
46 | }
47 | }
48 |
49 | PresenterProgress {
50 | z: 10
51 | }
52 |
53 | ScrollView {
54 | id: content
55 | anchors.fill: parent
56 | contentHeight: mainLayout.height
57 | contentWidth: width
58 | clip: true
59 |
60 | ColumnLayout {
61 | id: mainLayout
62 | anchors.left: parent.left
63 | anchors.right: parent.right
64 |
65 | Image {
66 | id: previewImage
67 | asynchronous: true
68 | fillMode: Image.PreserveAspectFit
69 | horizontalAlignment: Image.AlignHCenter
70 | verticalAlignment: Image.AlignVCenter
71 | Layout.minimumWidth: 200
72 | Layout.maximumWidth: 200
73 | Layout.minimumHeight: 277
74 | Layout.maximumHeight: 277
75 | Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
76 | source: viewModel && viewModel.animeInfo ? "image://proxer/%1".arg(viewModel.animeInfo.id) : ""
77 | mipmap: true
78 | }
79 |
80 | Label {
81 | id: detailsLabel
82 | Layout.preferredWidth: implicitWidth + 16
83 | Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
84 | wrapMode: Text.Wrap
85 | textFormat: Text.RichText
86 | text: viewModel ? viewModel.detailsText : ""
87 | horizontalAlignment: Text.AlignHCenter
88 | verticalAlignment: Text.AlignVCenter
89 | }
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/gui/quick/AddAnimeDialog.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.10
2 | import QtQuick.Controls 2.3
3 | import QtQuick.Layouts 1.3
4 | import de.skycoder42.QtMvvm.Core 1.0
5 | import de.skycoder42.QtMvvm.Quick 1.0
6 | import de.skycoder42.anirem 1.0
7 |
8 | AlertDialog {
9 | id: addAnimeDialog
10 | title: qsTr("Add Anime")
11 | property AddAnimeViewModel viewModel: null
12 |
13 | Connections {
14 | target: viewModel
15 | onClose: QuickPresenter.popView()
16 | }
17 |
18 | GridLayout {
19 | columns: 2
20 | columnSpacing: 20
21 | width: parent.width
22 |
23 | Label {
24 | text: qsTr("Proxer-ID:")
25 | Layout.fillWidth: true
26 | color: idEdit.focus ? idEdit.selectionColor : idEdit.color
27 | opacity: idEdit.focus ? 1 : 0.5
28 | }
29 |
30 | Item {
31 | id: previewItem
32 | Layout.rowSpan: 3
33 | Layout.preferredHeight: 100
34 | Layout.preferredWidth: 66
35 |
36 | readonly property bool showLoading: viewModel.loading || previewImage.status == Image.Loading
37 |
38 | Image {
39 | id: previewImage
40 | anchors.fill: parent
41 | asynchronous: true
42 | fillMode: Image.PreserveAspectFit
43 | horizontalAlignment: Image.AlignHCenter
44 | verticalAlignment: Image.AlignVCenter
45 | source: viewModel.id > 0 ? "image://proxer/%1".arg(viewModel.id) : ""
46 | mipmap: true
47 | visible: !previewItem.showLoading
48 | }
49 |
50 | BusyIndicator {
51 | anchors.fill: parent
52 | visible: previewItem.showLoading
53 | }
54 | }
55 |
56 | TextField {
57 | id: idEdit
58 | Layout.fillWidth: true
59 | placeholderText: "894"
60 | focus: true
61 | selectByMouse: true
62 | validator: IntValidator {
63 | bottom: 1
64 | top: 2147483647
65 | }
66 |
67 | text: viewModel.id > 0 ? viewModel.id : ""
68 | onEditingFinished: viewModel.id = text;
69 | }
70 |
71 | Label {
72 | text: qsTr("Title:")
73 | Layout.fillWidth: true
74 | color: titleField.focus ? idEdit.selectionColor : idEdit.color
75 | opacity: titleField.focus ? 1 : 0.5
76 | }
77 |
78 | ComboBox {
79 | id: titleField
80 | Layout.fillWidth: true
81 | Layout.columnSpan: 2
82 | editable: false
83 | model: viewModel.nameModel
84 | textRole: "display"
85 | onCurrentTextChanged: viewModel.title = currentText
86 |
87 | Connections {
88 | target: viewModel
89 | onTitleChanged: titleField.currentIndex = titleField.find(title)
90 | }
91 | }
92 | }
93 |
94 | footer: DialogButtonBox {
95 | id: btnBox
96 | standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
97 |
98 | Component.onCompleted: {
99 | var btn = standardButton(DialogButtonBox.Ok);
100 | btn.enabled = Qt.binding(function() { return viewModel.acceptable; });
101 | }
102 | }
103 |
104 | onAccepted: viewModel.accept();
105 | }
106 |
--------------------------------------------------------------------------------
/lib/libanirem.cpp:
--------------------------------------------------------------------------------
1 | #include "libanirem.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include "jsonseasondataconverter.h"
12 | #include "animeinfo.h"
13 | #include "proxerapi.h"
14 | #include "syncedsettings.h"
15 | #include "iupdatenotifier.h"
16 | #include "seasonstatusloader.h"
17 | #include "storedcookiejar.h"
18 |
19 | void AniRem::prepareTranslations()
20 | {
21 | auto translator = new QTranslator(qApp);
22 | if(translator->load(QLocale(),
23 | QStringLiteral("anirem"),
24 | QStringLiteral("_"),
25 | QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
26 | qApp->installTranslator(translator);
27 | }
28 |
29 | void AniRem::setup(QtDataSync::Setup &setup, bool passive)
30 | {
31 | setup.setRemoteConfiguration({QStringLiteral("wss://apps.skycoder42.de/datasync/")})
32 | .serializer()->addJsonTypeConverter();
33 | if(passive)
34 | setup.setRemoteObjectHost(QStringLiteral("local:de.skycoder42.anirem.daemon"));
35 |
36 | //also: setup API
37 | ProxerApi api;
38 | api.restClient()->serializer()->setAllowDefaultNull(true);
39 | api.restClient()->manager()->setCookieJar(new StoredCookieJar());
40 | if(LocalSettings::instance()->account.token.isSet())
41 | api.restClient()->addGlobalHeader("proxer-api-token", LocalSettings::instance()->account.token.get().toUtf8());
42 | }
43 |
44 | void AniRem::setProxerToken(const QString &token, LocalSettings *settings)
45 | {
46 | if(token.isEmpty()) {
47 | settings->account.token.reset();
48 | ProxerApi api;
49 | api.restClient()->removeGlobalHeader("proxer-api-token");
50 | } else {
51 | settings->account.token = token;
52 | ProxerApi api;
53 | api.restClient()->addGlobalHeader("proxer-api-token", token.toUtf8());
54 | }
55 | }
56 |
57 |
58 |
59 | namespace {
60 |
61 | void cleanSettings()
62 | {
63 | try {
64 | //WORKAROUND for settings destruction bug
65 | auto accessor = dynamic_cast(SyncedSettings::instance()->accessor());
66 | if(accessor)
67 | delete accessor;
68 | qDebug() << "Cleaned settings";
69 | } catch(QException &e) {
70 | qCritical() << "Failed to clean settings:" << e.what();
71 | }
72 |
73 | try {
74 | //WORKAROUND for settings destruction bug
75 | auto loader = QtMvvm::ServiceRegistry::instance()->service();
76 | if(loader)
77 | loader->preClean();
78 | qDebug() << "Cleaned status loader";
79 | } catch(QException &e) {
80 | qCritical() << "Failed to status loader:" << e.what();
81 | }
82 | }
83 |
84 | void setupAniRemLib()
85 | {
86 | qRegisterMetaType("AnimeInfo::SeasonMap");
87 |
88 | QJsonSerializer::registerListConverters();
89 |
90 | QtMvvm::registerInterfaceConverter();
91 | QtMvvm::ServiceRegistry::instance()->registerObject();
92 |
93 | qAddPostRoutine(cleanSettings);
94 | }
95 |
96 | }
97 | Q_COREAPP_STARTUP_FUNCTION(setupAniRemLib)
98 |
--------------------------------------------------------------------------------
/gui/core/detailsviewmodel.cpp:
--------------------------------------------------------------------------------
1 | #include "detailsviewmodel.h"
2 | #include
3 |
4 | const QString DetailsViewModel::ParamInfo = QStringLiteral("info");
5 | const QString DetailsViewModel::ParamOwner = QStringLiteral("owner");
6 |
7 | QVariantHash DetailsViewModel::params(const AnimeInfo &info, QtMvvm::ViewModel *owner)
8 | {
9 | return {
10 | {ParamInfo, QVariant::fromValue(info)},
11 | {ParamOwner, QVariant::fromValue(owner)}
12 | };
13 | }
14 |
15 | DetailsViewModel::DetailsViewModel(QObject *parent) :
16 | ViewModel(parent),
17 | _store(new AniremStore(this)),
18 | _loader(nullptr),
19 | _animeInfo()
20 | {
21 | connect(_store, &AniremStore::dataChanged,
22 | this, &DetailsViewModel::dataChanged);
23 | }
24 |
25 | AnimeInfo DetailsViewModel::animeInfo() const
26 | {
27 | return _animeInfo;
28 | }
29 |
30 | QString DetailsViewModel::detailsText() const
31 | {
32 | auto state = _animeInfo.seasonState();
33 | if(state.isEmpty()) {
34 | return tr("Anime has no connections…");
35 | } else {
36 | auto string = QStringLiteral("");
37 | for(auto it = state.constBegin(); it != state.constEnd(); it++) {
38 | auto row = tr("| %2: | %L1 |
");
39 | if(it->second)
40 | row = row.arg(tr("%L1 (new)"));
41 | string += row.arg(it->first)
42 | .arg(AnimeInfo::typeToString(it.key()));
43 | }
44 | return string + QStringLiteral("
");
45 | }
46 | }
47 |
48 | ImageLoader *DetailsViewModel::imageLoader() const
49 | {
50 | return _loader;
51 | }
52 |
53 | void DetailsViewModel::setAnimeInfo(const AnimeInfo &animeInfo)
54 | {
55 | if (_animeInfo == animeInfo)
56 | return;
57 |
58 | _animeInfo = animeInfo;
59 | emit animeInfoChanged();
60 | }
61 |
62 | void DetailsViewModel::clear()
63 | {
64 | _animeInfo = {};
65 | emit animeInfoChanged();
66 | }
67 |
68 | void DetailsViewModel::uncheckAnime()
69 | {
70 | if(_animeInfo && _animeInfo.hasNewSeasons()) {
71 | auto oldInfo = _animeInfo;
72 | try {
73 | _animeInfo.setAllUnchanged();
74 | _store->save(_animeInfo);
75 | emit animeInfoChanged();
76 | } catch(QException &e) {
77 | _animeInfo = oldInfo;
78 | qCritical() << "Failed to save entry with id" << _animeInfo.id()
79 | << "and error:" << e.what();
80 | QtMvvm::critical(tr("Adding failed!"),
81 | tr("Failed to add anime to underlying data storage."));
82 | }
83 | }
84 | }
85 |
86 | void DetailsViewModel::dataChanged(const QString &key, const QVariant &value)
87 | {
88 | if(_animeInfo.id() == AniremStore::toKey(key)) {
89 | _animeInfo = value.value();
90 | emit animeInfoChanged();
91 | }
92 | }
93 |
94 | void DetailsViewModel::onInit(const QVariantHash ¶ms)
95 | {
96 | if(params.contains(ParamInfo))
97 | setAnimeInfo(params.value(ParamInfo).value());
98 | auto owner = params.value(ParamOwner).value();
99 | if(owner) {
100 | QMetaObject::invokeMethod(owner, "setDetailsView",
101 | Q_ARG(QPointer, this));
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/lib/lib.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 |
3 | QT = core datasync restclient mvvmcore
4 | win32: CONFIG += skip_target_version_ext
5 | CONFIG += lib_bundle
6 |
7 | TARGET = anirem
8 |
9 | DEFINES += BUILD_LIB_ANIREM
10 |
11 | PUBLIC_HEADERS = \
12 | $$PWD/lib_anirem_global.h \
13 | $$PWD/libanirem.h \
14 | $$PWD/apihelper.h \
15 | $$PWD/seasonstatusloader.h \
16 | $$PWD/iupdatenotifier.h \
17 | $$PWD/animeinfo.h \
18 | $$PWD/passiveupdater.h
19 |
20 | HEADERS += $$PUBLIC_HEADERS \
21 | jsonseasondataconverter.h \
22 | storedcookiejar.h
23 |
24 | SOURCES += \
25 | libanirem.cpp \
26 | apihelper.cpp \
27 | animeinfo.cpp \
28 | jsonseasondataconverter.cpp \
29 | seasonstatusloader.cpp \
30 | passiveupdater.cpp \
31 | storedcookiejar.cpp
32 |
33 | REST_API_FILES += api/proxerstatus.xml \
34 | api/proxerentryvalue.xml \
35 | api/proxerentry.xml \
36 | api/proxerrelations.xml \
37 | api/proxernamevalue.xml \
38 | api/proxernames.xml \
39 | api/proxerlistvalue.xml \
40 | api/proxerlist.xml \
41 | api/proxerlogin.xml \
42 | api/proxerloginvalue.xml \
43 | api/proxerapi.xml \
44 | api/infoclass.xml \
45 | api/userclass.xml
46 |
47 | SETTINGS_GENERATORS += \
48 | localsettings.xml \
49 | syncedsettings.xml
50 |
51 | MVVM_SETTINGS_FILES += $$PWD/../gui/core/settings.xml
52 |
53 | TRANSLATIONS += anirem_lib_de.ts
54 |
55 | DISTFILES += $$TRANSLATIONS
56 |
57 | win32:CONFIG(release, debug|release): OUT_INC_DIR += $$OUT_PWD/release
58 | else:win32:CONFIG(debug, debug|release): OUT_INC_DIR += $$OUT_PWD/debug
59 | else: OUT_INC_DIR = $$OUT_PWD
60 | INCLUDEPATH += $$OUT_INC_DIR
61 |
62 | !ReleaseBuild:!DebugBuild:!system(qpmx -d $$shell_quote($$_PRO_FILE_PWD_) --qmake-run init $$QPMX_EXTRA_OPTIONS $$shell_quote($$QMAKE_QMAKE) $$shell_quote($$OUT_PWD)): error(qpmx initialization failed. Check the compilation log for details.)
63 | else: include($$OUT_PWD/qpmx_generated.pri)
64 |
65 | # install copy
66 | system($$QMAKE_MKDIR $$shell_quote($$shell_path($$shadowed($$SETTINGSGENERATOR_DIR))))
67 | for(header, SETTINGSGENERATOR_BUILD_HEADERS) {
68 | theader = $$shadowed($$SETTINGSGENERATOR_DIR/$$basename(header))
69 | !exists($$theader):system($$QMAKE_COPY_FILE $$shell_quote($$shell_path($$header)) $$shell_quote($$shell_path($$theader)))
70 | }
71 |
72 | # install
73 | target.path = $$INSTALL_LIBS
74 | qpmx_ts_target.path = $$INSTALL_TRANSLATIONS
75 | header_install.files = $$PUBLIC_HEADERS \
76 | $$SETTINGSGENERATOR_BUILD_HEADERS \
77 | $$OUT_INC_DIR/localsettings.h \
78 | $$OUT_INC_DIR/syncedsettings.h \
79 | $$OUT_INC_DIR/proxerstatus.h \
80 | $$OUT_INC_DIR/proxerentryvalue.h \
81 | $$OUT_INC_DIR/proxerentry.h \
82 | $$OUT_INC_DIR/proxerrelations.h \
83 | $$OUT_INC_DIR/infoclass.h \
84 | $$OUT_INC_DIR/proxerapi.h
85 | header_install.CONFIG += no_check_exist
86 | header_install.path = $$INSTALL_HEADERS/anirem
87 | INSTALLS += target qpmx_ts_target
88 | !android:!mac: INSTALLS += header_install
89 |
90 | # install osx headers
91 | FRAMEWORK_HEADERS.version = Versions
92 | for(hfile, header_install.files): FRAMEWORK_HEADERS.files += $$relative_path($$hfile, $$OUT_PWD)
93 | FRAMEWORK_HEADERS.path = Headers
94 | QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS
95 |
96 | QMAKE_EXTRA_TARGETS += qtifw
97 |
--------------------------------------------------------------------------------
/gui/quick/LoginDialog.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.10
2 | import QtQuick.Controls 2.3
3 | import QtQuick.Layouts 1.3
4 | import de.skycoder42.QtMvvm.Core 1.0
5 | import de.skycoder42.QtMvvm.Quick 1.0
6 | import de.skycoder42.anirem 1.0
7 |
8 | AlertDialog {
9 | id: loginDialog
10 | title: qsTr("Login")
11 | property LoginViewModel viewModel: null
12 |
13 | Connections {
14 | target: viewModel
15 | onLoginSuccessful: QuickPresenter.popView()
16 | }
17 |
18 | Item {
19 | id: content
20 | anchors.fill: parent
21 | implicitWidth: contentLayout.implicitWidth
22 | implicitHeight: contentLayout.implicitHeight
23 |
24 | ColumnLayout {
25 | id: contentLayout
26 | anchors.fill: parent
27 | enabled: !viewModel.loading
28 |
29 | Label {
30 | text: qsTr("Username:")
31 | Layout.fillWidth: true
32 | color: userNameEdit.focus ? userNameEdit.selectionColor : userNameEdit.color
33 | opacity: userNameEdit.focus ? 1 : 0.5
34 | }
35 |
36 | TextField {
37 | id: userNameEdit
38 | Layout.fillWidth: true
39 | focus: true
40 | selectByMouse: true
41 |
42 | MvvmBinding {
43 | viewModel: loginDialog.viewModel
44 | viewModelProperty: "userName"
45 | view: userNameEdit
46 | viewProperty: "text"
47 | viewChangeSignal: "editingFinished()"
48 | }
49 | }
50 |
51 | Label {
52 | text: qsTr("Password:")
53 | Layout.fillWidth: true
54 | color: passwordEdit.focus ? passwordEdit.selectionColor : userNameEdit.color
55 | opacity: passwordEdit.focus ? 1 : 0.5
56 | }
57 |
58 | TextField {
59 | id: passwordEdit
60 | Layout.fillWidth: true
61 | selectByMouse: true
62 | echoMode: TextInput.Password
63 |
64 | MvvmBinding {
65 | viewModel: loginDialog.viewModel
66 | viewModelProperty: "password"
67 | view: passwordEdit
68 | viewProperty: "text"
69 | viewChangeSignal: "editingFinished()"
70 | }
71 | }
72 |
73 | Label {
74 | text: qsTr("Authenticaton Code (optional):")
75 | Layout.fillWidth: true
76 | color: authEdit.focus ? authEdit.selectionColor : userNameEdit.color
77 | opacity: authEdit.focus ? 1 : 0.5
78 | }
79 |
80 | TextField {
81 | id: authEdit
82 | Layout.fillWidth: true
83 | selectByMouse: true
84 | validator: IntValidator {
85 | bottom: 0
86 | top: 999999
87 | }
88 |
89 | MvvmBinding {
90 | viewModel: loginDialog.viewModel
91 | viewModelProperty: "authCode"
92 | view: authEdit
93 | viewProperty: "text"
94 | viewChangeSignal: "editingFinished()"
95 | }
96 | }
97 |
98 | //FEATURE disable for now
99 | // Switch {
100 | // id: storePwSwitch
101 | // text: qsTr("Remember my password")
102 |
103 | // MvvmBinding {
104 | // viewModel: loginDialog.viewModel
105 | // viewModelProperty: "storePassword"
106 | // view: storePwSwitch
107 | // viewProperty: "checked"
108 | // }
109 | // }
110 | }
111 |
112 | BusyIndicator {
113 | anchors.centerIn: parent
114 | z: 50
115 | visible: viewModel.loading
116 | }
117 | }
118 |
119 | footer: DialogButtonBox {
120 | id: btnBox
121 | standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
122 | enabled: !viewModel.loading
123 | }
124 |
125 | onAccepted: {
126 | viewModel.login();
127 | loginDialog.visible = true;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/gui/quick/quick.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 |
3 | QT += quick mvvmquick mvvmdatasyncquick concurrent
4 | android: QT += androidextras
5 |
6 | android: TARGET = anirem-activity
7 | else: TARGET = anirem
8 |
9 | HEADERS += \
10 | proxerimageprovider.h \
11 | qmltoast.h
12 |
13 | SOURCES += main.cpp \
14 | proxerimageprovider.cpp \
15 | qmltoast.cpp
16 |
17 | RESOURCES += \
18 | anirem-quick.qrc
19 |
20 | TRANSLATIONS += anirem_quick_de.ts
21 |
22 | EXTRA_TRANSLATIONS += anirem_de.ts
23 |
24 | ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
25 |
26 | DISTFILES += $$TRANSLATIONS \
27 | $$EXTRA_TRANSLATIONS \
28 | android/AndroidManifest.xml \
29 | android/build.gradle \
30 | android/src/de/skycoder42/anirem/* \
31 | android/res/values/* \
32 | android/res/values-de/* \
33 | android/res/xml/* \
34 | android/res/drawable-hdpi/* \
35 | android/res/drawable-mdpi/* \
36 | android/res/drawable-xhdpi/* \
37 | android/res/drawable-xxhdpi/* \
38 | android/res/drawable-xxxhdpi/* \
39 | android/res/mipmap-hdpi/* \
40 | android/res/mipmap-mdpi/* \
41 | android/res/mipmap-xhdpi/* \
42 | android/res/mipmap-xxhdpi/* \
43 | android/res/mipmap-xxxhdpi/* \
44 | android/res/mipmap-anydpi-v26/* \
45 | android/src/de/skycoder42/anirem/Globals.java
46 |
47 | # install
48 | target.path = $$INSTALL_BINS
49 | qpmx_ts_target.path = $$INSTALL_TRANSLATIONS
50 | extra_ts_target.path = $$INSTALL_TRANSLATIONS
51 | INSTALLS += target qpmx_ts_target extra_ts_target
52 |
53 | # Link with core project
54 | win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../core/release/ -lanirem-core
55 | else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../core/debug/ -lanirem-core
56 | else:unix: LIBS += -L$$OUT_PWD/../core/ -lanirem-core
57 |
58 | INCLUDEPATH += $$PWD/../core
59 | DEPENDPATH += $$PWD/../core
60 |
61 | win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/release/libanirem-core.a
62 | else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/debug/libanirem-core.a
63 | else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/release/anirem-core.lib
64 | else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/debug/anirem-core.lib
65 | else:unix: PRE_TARGETDEPS += $$OUT_PWD/../core/libanirem-core.a
66 |
67 | include(../../lib.pri)
68 |
69 | android {
70 | LIBS += -L$$PWD/openssl/openssl -lcrypto -lssl
71 | ANDROID_EXTRA_LIBS += \
72 | $$PWD/openssl/openssl/libcrypto.so \
73 | $$PWD/openssl/openssl/libssl.so
74 | RESOURCES += anirem-android.qrc
75 |
76 | tsqtInstall.path = $$INSTALL_TRANSLATIONS
77 | tsqtInstall.files = \
78 | $$[QT_INSTALL_TRANSLATIONS]/qtbase_*.qm \
79 | $$[QT_INSTALL_TRANSLATIONS]/qtdeclarative_*.qm \
80 | $$[QT_INSTALL_TRANSLATIONS]/qtquickcontrols_*.qm \
81 | $$[QT_INSTALL_TRANSLATIONS]/qtwebsockets_*.qm \
82 | $$[QT_INSTALL_TRANSLATIONS]/qtdatasync_*.qm \
83 | $$[QT_INSTALL_TRANSLATIONS]/qtmvvmcore_*.qm \
84 | $$[QT_INSTALL_TRANSLATIONS]/qtmvvmdatasynccore_*.qm \
85 | $$[QT_INSTALL_TRANSLATIONS]/qtmvvmquick_*.qm \
86 | $$[QT_INSTALL_TRANSLATIONS]/qtmvvmdatasyncquick_*.qm
87 | INSTALLS += tsqtInstall
88 | }
89 |
90 | !ReleaseBuild:!DebugBuild:!system(qpmx -d $$shell_quote($$_PRO_FILE_PWD_) --qmake-run init $$QPMX_EXTRA_OPTIONS $$shell_quote($$QMAKE_QMAKE) $$shell_quote($$OUT_PWD)): error(qpmx initialization failed. Check the compilation log for details.)
91 | else: include($$OUT_PWD/qpmx_generated.pri)
92 |
93 | QMAKE_EXTRA_TARGETS += qtifw
94 |
--------------------------------------------------------------------------------
/gui/quick/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include "proxerimageprovider.h"
13 | #include "qmltoast.h"
14 |
15 | #ifdef Q_OS_ANDROID
16 | #include
17 | #endif
18 |
19 | QTMVVM_REGISTER_CORE_APP(AniRemApp)
20 |
21 | namespace {
22 |
23 | void setStatusBarColor(QColor color)
24 | {
25 | #ifdef Q_OS_ANDROID
26 | if(QtAndroid::androidSdkVersion() >= 21) {
27 | auto activity = QtAndroid::androidActivity();
28 | QtAndroid::runOnAndroidThreadSync([=](){
29 | const auto FLAG_TRANSLUCENT_STATUS = QAndroidJniObject::getStaticField("android/view/WindowManager$LayoutParams",
30 | "FLAG_TRANSLUCENT_STATUS");
31 | const auto FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = QAndroidJniObject::getStaticField("android/view/WindowManager$LayoutParams",
32 | "FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS");
33 | const auto jColor = QAndroidJniObject::callStaticMethod("android/graphics/Color",
34 | "parseColor",
35 | "(Ljava/lang/String;)I",
36 | QAndroidJniObject::fromString(color.name()).object());
37 |
38 | QAndroidJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;");
39 | if(window.isValid()) {
40 | window.callMethod("clearFlags", "(I)V", FLAG_TRANSLUCENT_STATUS);
41 | window.callMethod("addFlags", "(I)V", FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
42 | window.callMethod("setStatusBarColor", "(I)V", jColor);
43 | }
44 | });
45 | }
46 | #else
47 | Q_UNUSED(color);
48 | #endif
49 | }
50 |
51 | QObject *createQmlToast(QQmlEngine *qmlEngine, QJSEngine *jsEngine)
52 | {
53 | Q_UNUSED(jsEngine)
54 | return new QmlToast(qmlEngine);
55 | }
56 |
57 | }
58 |
59 | int main(int argc, char *argv[])
60 | {
61 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
62 | setStatusBarColor(QColor(0x4E, 0x4E, 0x4E));
63 | // If you want to support file dialogs on platforms other then android, use a QApplication instead (and add QT += widgets to the pro file)
64 | QGuiApplication app(argc, argv);
65 |
66 | qmlRegisterSingletonType("de.skycoder42.anirem", 1, 0, "QmlToast", createQmlToast);
67 |
68 | QtMvvm::registerDataSyncQuick();
69 | qmlRegisterUncreatableType("de.skycoder42.anirem", 1, 0, "MainViewModel", QStringLiteral("ViewModels cannot be created!"));
70 | qmlRegisterUncreatableType("de.skycoder42.anirem", 1, 0, "AddAnimeViewModel", QStringLiteral("ViewModels cannot be created!"));
71 | qmlRegisterUncreatableType("de.skycoder42.anirem", 1, 0, "DetailsViewModel", QStringLiteral("ViewModels cannot be created!"));
72 | qmlRegisterUncreatableType("de.skycoder42.anirem", 1, 0, "EntryViewModel", QStringLiteral("ViewModels cannot be created!"));
73 | qmlRegisterUncreatableType("de.skycoder42.anirem", 1, 0, "LoginViewModel", QStringLiteral("ViewModels cannot be created!"));
74 |
75 | QQmlApplicationEngine engine;
76 | engine.load(QUrl(QStringLiteral("qrc:/qml/App.qml")));
77 | engine.addImageProvider(QStringLiteral("proxer"), new ProxerImageProvider());
78 | if (engine.rootObjects().isEmpty())
79 | return -1;
80 |
81 | return app.exec();
82 | }
83 |
--------------------------------------------------------------------------------
/gui/core/imageloader.cpp:
--------------------------------------------------------------------------------
1 | #include "imageloader.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | const QString ImageLoader::CacheDirName = QStringLiteral("preview-images");
9 | const QString ImageLoader::ImageNameTemplate = QStringLiteral("img_%1.png");
10 |
11 | ImageLoader::ImageLoader(QObject *parent) :
12 | QObject(parent),
13 | _nam(nullptr),
14 | _cache()
15 | {}
16 |
17 | void ImageLoader::loadImage(int id)
18 | {
19 | QMetaObject::invokeMethod(this, "loadImageImpl", Qt::QueuedConnection,
20 | Q_ARG(int, id));
21 | }
22 |
23 | void ImageLoader::clearCache()
24 | {
25 | QMetaObject::invokeMethod(this, "clearCacheImpl", Qt::QueuedConnection);
26 | }
27 |
28 | void ImageLoader::loadImageImpl(int id)
29 | {
30 | // check if cached
31 | auto imagePtr = _cache.object(id);
32 | if(imagePtr) {
33 | emit imageLoaded(id, *imagePtr);
34 | return;
35 | }
36 |
37 | // check if in cache dir
38 | QDir cacheDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
39 | if(cacheDir.mkpath(CacheDirName) && cacheDir.cd(CacheDirName)) {
40 | auto imgPath = cacheDir.absoluteFilePath(ImageNameTemplate.arg(id));
41 | if(QFile::exists(imgPath)) {
42 | QImage image(imgPath, "png");
43 | if(!image.isNull()) {
44 | _cache.insert(id, new QImage(image));
45 | emit imageLoaded(id, image);
46 | return;
47 | }
48 | }
49 | } else
50 | qWarning() << "Cache directory is not accessible";
51 |
52 |
53 | if(!_nam)
54 | _nam = new QNetworkAccessManager(this);
55 | auto reply = _nam->get(QNetworkRequest(QStringLiteral("https://cdn.proxer.me/cover/%1.jpg").arg(id)));
56 | connect(reply, &QNetworkReply::finished, this, [=](){
57 | imageNetworkReply(id, reply);
58 | }, Qt::QueuedConnection);
59 | }
60 |
61 | void ImageLoader::clearCacheImpl()
62 | {
63 | _cache.clear();
64 |
65 | QDir cacheDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
66 | if(cacheDir.exists(CacheDirName)) {
67 | QString rmName = QStringLiteral(".rm-dir-") + QUuid::createUuid().toString();
68 | if(cacheDir.rename(CacheDirName, rmName)) {
69 | if(cacheDir.cd(rmName)) {
70 | QtConcurrent::run([cacheDir](){
71 | auto cDir = cacheDir;
72 | cDir.removeRecursively();
73 | });
74 | }
75 | } else if(cacheDir.cd(CacheDirName))
76 | cacheDir.removeRecursively();
77 | }
78 | }
79 |
80 | void ImageLoader::imageNetworkReply(int id, QNetworkReply *reply)
81 | {
82 | if(reply->error() == QNetworkReply::NoError) {
83 | QImageReader reader(reply, "jpg");
84 | QImage image;
85 | if(reader.read(&image)) {
86 | // send result
87 | _cache.insert(id, new QImage(image));
88 | emit imageLoaded(id, image);
89 |
90 | //save to cache
91 | QDir cacheDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
92 | if(cacheDir.mkpath(CacheDirName) && cacheDir.cd(CacheDirName)) {
93 | auto imgPath = cacheDir.absoluteFilePath(ImageNameTemplate.arg(id));
94 | if(!image.save(imgPath, "png"))
95 | qWarning() << "Failed to store image with id" << id << "to cache";
96 | } else
97 | qWarning() << "Cache directory is not accessible";
98 | } else
99 | emit imageLoadFailed(id, QStringLiteral("Failed to read image with id %1 with error: %2").arg(id).arg(reader.errorString()));
100 | } else
101 | emit imageLoadFailed(id, QStringLiteral("Network Error for id %1: %2").arg(id).arg(reply->errorString()));
102 |
103 | reply->deleteLater();
104 | }
105 |
--------------------------------------------------------------------------------
/gui/quick/android/src/de/skycoder42/anirem/AniremService.java:
--------------------------------------------------------------------------------
1 | package de.skycoder42.anirem;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 |
6 | import android.app.PendingIntent;
7 | import android.app.Notification;
8 | import android.app.NotificationManager;
9 |
10 | import android.graphics.BitmapFactory;
11 |
12 | import android.support.v4.app.NotificationCompat;
13 | import android.support.v4.app.NotificationCompat.BigTextStyle;
14 |
15 | import org.qtproject.qt5.android.bindings.QtService;
16 |
17 | public class AniremService extends QtService {
18 | private NotificationCompat.Builder progressBuilder = null;
19 |
20 | @Override
21 | public void onDestroy() {
22 | super.onDestroy();
23 | // explicitly exit to prevent the process from beeing cached
24 | System.exit(0);
25 | }
26 |
27 | @Override
28 | public int onStartCommand(Intent intent, int flags, int startId) {
29 | int result = super.onStartCommand(intent, flags, startId);
30 |
31 | if(progressBuilder == null) {
32 | progressBuilder = new NotificationCompat.Builder(this, Globals.ForegroundChannelId)
33 | .setContentTitle(getString(R.string.update_progress_title))
34 | .setContentText(getString(R.string.update_progress_text))
35 | .setContentInfo(getString(R.string.app_name))
36 | .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
37 | .setSmallIcon(R.drawable.ic_notification)
38 | .setColor(Globals.NormalColor)
39 | .setOngoing(true)
40 | .setProgress(0, 0, true)
41 | .setCategory(NotificationCompat.CATEGORY_PROGRESS);
42 |
43 | if(!Globals.isOreo())
44 | progressBuilder.setPriority(NotificationCompat.PRIORITY_MIN);
45 | }
46 |
47 | startForeground(Globals.ForegroundId, progressBuilder.build());
48 | handleStart();
49 | return result;
50 | }
51 |
52 | public void completeAction() {
53 | stopForeground(true);
54 | stopService(new Intent(this, AniremService.class));//Stop myself
55 | }
56 |
57 | public void updateProgress(int current, int max)
58 | {
59 | NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
60 | progressBuilder.setProgress(max, current, false)
61 | .setContentText(getString(R.string.update_progress_active_text) + current + " / " + max);
62 | manager.notify(Globals.ForegroundId, progressBuilder.build());
63 | }
64 |
65 | public void showUpdateNotification(boolean success, String title, String message) {
66 | NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
67 |
68 | Intent openIntent = new Intent(this, AniremActivity.class);
69 | PendingIntent pending = PendingIntent.getActivity(this,
70 | Globals.OpenIntentId,
71 | openIntent,
72 | PendingIntent.FLAG_UPDATE_CURRENT);
73 |
74 | NotificationCompat.Builder builder = new NotificationCompat.Builder(this, Globals.NormalChannelId)
75 | .setContentTitle(title)
76 | .setContentText(message)
77 | .setContentInfo(getString(R.string.app_name))
78 | .setStyle(new NotificationCompat.BigTextStyle()
79 | .bigText(message))
80 | .setContentIntent(pending)
81 | .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
82 | .setSmallIcon(R.drawable.ic_notification)
83 | .setColor(Globals.NormalColor)
84 | .setAutoCancel(true)
85 | .setCategory(success ? NotificationCompat.CATEGORY_EVENT : NotificationCompat.CATEGORY_ERROR);
86 |
87 | if(!Globals.isOreo())
88 | builder.setDefaults(Notification.DEFAULT_ALL);
89 |
90 | manager.notify(Globals.NotifyId, builder.build());
91 | }
92 |
93 | private static native void handleStart();
94 | }
95 |
--------------------------------------------------------------------------------
/gui/widgets/entrydialog.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | EntryDialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 647
10 | 358
11 |
12 |
13 |
14 | Anime/Manga History
15 |
16 |
17 | true
18 |
19 |
20 | true
21 |
22 |
23 | -
24 |
25 |
-
26 |
27 |
28 | List entries of &type:
29 |
30 |
31 | typeComboBox
32 |
33 |
34 |
35 | -
36 |
37 |
38 | -
39 |
40 |
41 | Qt::Horizontal
42 |
43 |
44 |
45 | 40
46 | 20
47 |
48 |
49 |
50 |
51 |
52 |
53 | -
54 |
55 |
56 | QAbstractItemView::NoEditTriggers
57 |
58 |
59 | true
60 |
61 |
62 | false
63 |
64 |
65 | false
66 |
67 |
68 | true
69 |
70 |
71 | false
72 |
73 |
74 | false
75 |
76 |
77 |
78 | -
79 |
80 |
81 | Qt::Horizontal
82 |
83 |
84 | QDialogButtonBox::Close
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | buttonBox
94 | accepted()
95 | EntryDialog
96 | accept()
97 |
98 |
99 | 248
100 | 254
101 |
102 |
103 | 157
104 | 274
105 |
106 |
107 |
108 |
109 | buttonBox
110 | rejected()
111 | EntryDialog
112 | reject()
113 |
114 |
115 | 316
116 | 260
117 |
118 |
119 | 286
120 | 274
121 |
122 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: cpp
2 |
3 | services:
4 | - docker
5 |
6 | sudo: required
7 | dist: trusty
8 | osx_image: xcode9.2
9 |
10 | env:
11 | global:
12 | - QPMX_CACHE_DIR=$HOME/.qpmx-cache
13 | - QT_VER=5.10.1
14 | - OPENSSL_VERSION=1.0.2n
15 | - EXTRA_MODULES=".qtremoteobjects .skycoder42 .gcc_64"
16 | - DOCKER_IMAGE=full
17 | - NO_TESTS=true
18 | - ANIREM_VER=2.0.1
19 | - secure: xrjRnCXdhkR0vGe+I7eNRsodmwu59if9H8KMgrRvfNpFSulxTP6gLxV/fOn6tNXHNynKt1wwf6KqRGoDPu8iAaaHyFnXDT0a70TbUH+Sbk+8UuGYaoHT4e+A3ywEhj/u6jX/PpKv40o0iqno2TDXWYAyq63Ik3S6+pu13tLUCOEG+RuS0MGaeKPaZOQAqviGBL3suslYtk2+HCKUpg70S4nuf/fvZbe/3M6KJdGLdBNXj0HxG6h168HObchAyjvGbo2/6GzwRDXbCxLKh0GUl/3kgDSF9/31MoGFEHlZNopTyrmfa6VUgpQ+mdraeqqKv3kwRk/BmwBup7GUL7tCJBrqOfQYdnAaV3YZXJ/a2thechiKZiT4qjuTFBSFXP3hv/wh4ZUpqi0MBUzPe1u8YKbwrfCddeIIzioyP9qVFkXEdAto+WgEGuT58mX83q9fjgRaGMc9x2KzXrqbCH3czdjhoAsg2fTIrAofh3COP2LraeEsy7QN9x4xfl+ySXT+m9WMuj3bR+VMzRQXXc+i5rlH2HGvEBK0nWsvq5LlRiSUMf1vDaoCNIPrcDuvVaIeciAuONHIg4GEbfJbIxvHOWbCXlWVDVhhQqVD8pgZKqVfLjJ0fz+/m/lHfueHKrbts1RXfK+kunQ7thfrjc1s6sNRFdpOZhytZaazTv1Htx0=
20 | - secure: lMC613uPXCZ/acVZBDOS1+9QdNkrB3MbkzlZOelrmR5T24p5DDzG9F1b01zH7QFc1LFcy8TCUR1ZD6a5j1sHi/ALWm2lAhJdbNTKlvtM71oP885gP0+IuHM6b/oRWZ14ER+Cug7zl34GmF76vMfm5QQe3cqQtreITzbf62R7QB5H2FpGT3lVz3nXHkZIhmSk3hOPRHDJKjdX0WUzk8rEQ1oq2mBjPl22BQUidsCVlnuhPokqWzreQ3/eGeH8G9iKkQXqY39vL5t88Yr8maJYMBNLfVAfbVblZmlruAqSMdakP3voWdkEQqT7nam3ZeNkYe8+vdBVwtsTsKAHpvydrouzjCfZr9pVYKjnDqqOe5ssgtxt/NtqDiczQutM/HDEUy/44AYJi8ljFSLfWgo4aVbz52XVAat12OELAEXggZsQMd1xXMtZCROtsshtGH+SvbVJ6ai2eHEDXqGpcp/mU6bEg8sgwbLipXzqMcLkfHWRO50UeSD7iint8Tc7pFvbr6QncY12YetLZ2XApzRDf2l9yxN+u2PbixPYHKDQrpL9+1GNC2MqBGiBLAY0PvljP+l/0+Wdh/uVlAUJXnBkifsKbLJLq6TmhLS3TKfdbRC84EkPbM4WGvWw1J5n9me67qzfr9EYv39cnKD3DwNMRRKg/efd1c0x3DzHWl1gB84=
21 | matrix:
22 | include:
23 | - os: linux
24 | env:
25 | - PLATFORM=gcc_64
26 | - os: linux
27 | env:
28 | - PLATFORM=android_armv7
29 | - os: linux
30 | env:
31 | - PLATFORM=android_x86
32 | - os: osx
33 | env:
34 | - PLATFORM=clang_64
35 |
36 | before_install:
37 | - git clone https://github.com/Skycoder42/QtModules.git ./qtmodules-travis
38 | - travis_wait 40 ./qtmodules-travis/ci/$TRAVIS_OS_NAME/setup.sh
39 |
40 | script:
41 | - ./ci/travis_init.sh
42 | - ./qtmodules-travis/ci/$TRAVIS_OS_NAME/build.sh
43 | - if [ "$TRAVIS_OS_NAME" == "osx" ]; then ./ci/travis_postbuild.sh; fi
44 |
45 | before_deploy:
46 | - ./ci/travis_deploy.sh
47 | deploy:
48 | provider: releases
49 | skip_cleanup: true
50 | api_key:
51 | secure: mSzeSXiPdqGn7LoIWF7tPqM01IpA7Q44P8i3wB5V8jBqr5lRuhkLzE+j+EI6NzLtxZEYZLl/Y+s11E7Q8e36qQe0eJtlgb5CJC+CrAE4vGWV6BjZFhUsaB5fs8zLOhWL2fhdMiHUQaQrh1foWnvrtJqt2Av5jf9kTttrrqg/YGRly8omXe3MV+9ZbHcZTuDWISaD29nLPmr4obunrcjy8oWiP1MRJmPsEp3poTLTQjWSOBLlTKqK/JZtieTTS1vzG+lENxSxCaRGQRuEYji056KudKEvx8lxI1cP6nPG+h7L74plYIGRZZ3V4Ux7ytnn6UfdBU45wRLT1bDSfG6nyGHT+4CetVFDH9+DE00PFz3oEcVOpRavb4eRArSq1r45coYtTdFKtjnN4o6kbZ77AdXKKJbUKks9wbV9jJmfyK1RP+snH5VmY5r2R5FlTg2zbcl4oYY3lSjzgCwbFkuEXT9NjWeKUX0CIZ5C7vUTh8Yk3hB6jFa3ZUxcTOT0pmzgC7RVmWG/1k55daoLClxpdF/HUr/x+OyXqFLMY7bXzYLLT1TxUZg3B5zcGbuZecrLs35f5Jr3cB+aJG+4CADDBoY+3Kw2jM8j433pYHuJa2P1Zl6BaZvsPkOvfaVD+9Zq+mcenwh2QQ8tcGAWgKcLd3TcaNhYVvxn7+mIY+5brFk=
52 | file_glob: true
53 | file:
54 | - ./install/repository_*.tar.xz
55 | - ./install/Ani-Rem Installer.*
56 | - ./install/*.apk
57 | on:
58 | repo: Skycoder42/AniRem
59 | tags: true
60 |
61 | before_cache:
62 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
63 | - rm -rf $HOME/.gradle/caches/*/plugin-resolution/
64 | - rm -rf $QPMX_CACHE_DIR/locks
65 | - rm -rf $QPMX_CACHE_DIR/tmp
66 | cache:
67 | directories:
68 | - $HOME/.gradle/caches/
69 | - $HOME/.gradle/wrapper/
70 | - $HOME/.android/build-cache
71 | - $QPMX_CACHE_DIR
72 |
--------------------------------------------------------------------------------
/icn/anirem_white.svg:
--------------------------------------------------------------------------------
1 |
2 |
91 |
--------------------------------------------------------------------------------
/icn/anirem.svg:
--------------------------------------------------------------------------------
1 |
2 |
99 |
--------------------------------------------------------------------------------
/gui/core/loginviewmodel.cpp:
--------------------------------------------------------------------------------
1 | #include "loginviewmodel.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | LoginViewModel::LoginViewModel(QObject *parent) :
8 | ViewModel(parent),
9 | _settings(nullptr),
10 | //WORKAROUND _user(ProxerApi::factory().user().instance(this)),
11 | _user((new ProxerApi(this))->user()),
12 | _userName(),
13 | _password(),
14 | _authCode(),
15 | _storePassword(false)
16 | {
17 | _user->setErrorTranslator(&ApiHelper::transformError);
18 | connect(_user, &UserClass::apiError,
19 | this, &LoginViewModel::apiError);
20 | }
21 |
22 | QString LoginViewModel::userName() const
23 | {
24 | return _userName;
25 | }
26 |
27 | QString LoginViewModel::password() const
28 | {
29 | return _password;
30 | }
31 |
32 | QString LoginViewModel::authCode() const
33 | {
34 | return _authCode;
35 | }
36 |
37 | bool LoginViewModel::storePassword() const
38 | {
39 | return _storePassword;
40 | }
41 |
42 | bool LoginViewModel::isLoading() const
43 | {
44 | return _loading;
45 | }
46 |
47 | void LoginViewModel::setUserName(const QString &userName)
48 | {
49 | if (_userName == userName)
50 | return;
51 |
52 | _userName = userName;
53 | emit userNameChanged(_userName);
54 | }
55 |
56 | void LoginViewModel::setPassword(const QString &password)
57 | {
58 | if (_password == password)
59 | return;
60 |
61 | _password = password;
62 | emit passwordChanged(_password);
63 | }
64 |
65 | void LoginViewModel::setAuthCode(const QString &authCode)
66 | {
67 | if (_authCode == authCode)
68 | return;
69 |
70 | _authCode = authCode;
71 | emit authCodeChanged(_authCode);
72 | }
73 |
74 | void LoginViewModel::setStorePassword(bool storePassword)
75 | {
76 | if (_storePassword == storePassword)
77 | return;
78 |
79 | _storePassword = storePassword;
80 | emit storePasswordChanged(_storePassword);
81 | }
82 |
83 | void LoginViewModel::login()
84 | {
85 | _loading = true;
86 | emit loadingChanged(_loading);
87 |
88 | QUrlQuery params;
89 | params.addQueryItem(QStringLiteral("username"), _userName);
90 | params.addQueryItem(QStringLiteral("password"), _password);
91 | if(!_authCode.isEmpty())
92 | params.addQueryItem(QStringLiteral("secretkey"), _authCode);
93 | auto nmReply = _user->restClass()->builder()
94 | .addPath(QStringLiteral("login"))
95 | .setBody(params.query().toUtf8(), "application/x-www-form-urlencoded")
96 | .setVerb(QtRestClient::RestClass::PostVerb)
97 | .send();
98 |
99 | auto reply = new QtRestClient::GenericRestReply {
100 | nmReply,
101 | _user->restClient(),
102 | this
103 | };
104 | reply->onSucceeded([this](int code, ProxerLogin login){
105 | if(ApiHelper::testValid(code, login) ||
106 | code == 3012) { // user already logged in
107 | _settings->account.username = _userName;
108 | _settings->account.storedPw = _storePassword;
109 | AniRem::setProxerToken(login.data().token(), _settings);
110 | if(_storePassword) {
111 | //FEATURE store password to keystore
112 | } else {
113 | //FEATURE remove password from keystore
114 | }
115 |
116 | emit resultReady(true);
117 | emit loginSuccessful();
118 | } else
119 | apiError(login.message(), login.code(), QtRestClient::RestReply::FailureError);
120 | });
121 | reply->onAllErrors([this](QString e, int c, QtRestClient::RestReply::ErrorType t) {
122 | apiError(e, c, t);
123 | });
124 | }
125 |
126 | void LoginViewModel::onInit(const QVariantHash ¶ms)
127 | {
128 | Q_UNUSED(params);
129 | setUserName(_settings->account.username);
130 | setStorePassword(_settings->account.storedPw);
131 |
132 | if(_storePassword) {
133 | //FEATURE load password from keystore
134 | }
135 | }
136 |
137 | void LoginViewModel::apiError(const QString &errorString, int errorCode, QtRestClient::RestReply::ErrorType errorType)
138 | {
139 | QtMvvm::critical(tr("Login failed"),
140 | ApiHelper::formatError(errorString, errorCode, errorType));
141 |
142 | _loading = false;
143 | emit loadingChanged(_loading);
144 | }
145 |
--------------------------------------------------------------------------------
/gui/widgets/addanimedialog.cpp:
--------------------------------------------------------------------------------
1 | #include "addanimedialog.h"
2 | #include "ui_addanimedialog.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | AddAnimeDialog::AddAnimeDialog(QtMvvm::ViewModel *viewModel, QWidget *parent) :
9 | QDialog(parent),
10 | _viewModel(static_cast(viewModel)),
11 | _ui(new Ui::AddAnimeDialog),
12 | _loadingMovie(new QMovie(QStringLiteral(":/animations/loading.gif"), "gif", this)),
13 | _currentPixmap(),
14 | _pmState(PmCleared)
15 | {
16 | _ui->setupUi(this);
17 | DialogMaster::masterDialog(this, true);
18 | _ui->proxerIDLineEdit->setValidator(new QIntValidator(0, INT_MAX, _ui->proxerIDLineEdit));
19 |
20 | _ui->titleComboBox->setModel(_viewModel->nameModel());
21 |
22 | connect(_viewModel->imageLoader(), &ImageLoader::imageLoaded,
23 | this, &AddAnimeDialog::imageLoaded,
24 | Qt::QueuedConnection);
25 | connect(_viewModel->imageLoader(), &ImageLoader::imageLoadFailed,
26 | this, &AddAnimeDialog::imageLoadFailed,
27 | Qt::QueuedConnection);
28 |
29 | connect(_ui->proxerIDLineEdit, &QLineEdit::editingFinished,
30 | this, &AddAnimeDialog::uiIdChanged);
31 |
32 | //viewmodel
33 | QtMvvm::bind(_viewModel, "id",
34 | this, "uiId");
35 | QtMvvm::bind(_viewModel, "title",
36 | _ui->titleComboBox, "currentText");
37 | QtMvvm::bind(_viewModel, "acceptable",
38 | _ui->buttonBox->button(QDialogButtonBox::Ok), "enabled",
39 | QtMvvm::Binding::OneWayToView);
40 |
41 | connect(_viewModel, &AddAnimeViewModel::loadingChanged,
42 | this, &AddAnimeDialog::loadingChanged);
43 | loadingChanged(_viewModel->isLoading());
44 |
45 | connect(_viewModel, &AddAnimeViewModel::close,
46 | this, &AddAnimeDialog::reject);
47 | }
48 |
49 | AddAnimeDialog::~AddAnimeDialog()
50 | {
51 | delete _ui;
52 | }
53 |
54 | void AddAnimeDialog::accept()
55 | {
56 | if(_viewModel->accept())
57 | QDialog::accept();
58 | }
59 |
60 | int AddAnimeDialog::uiId() const
61 | {
62 | auto ok = false;
63 | auto res = QLocale().toInt(_ui->proxerIDLineEdit->text(), &ok);
64 | if(ok)
65 | return res;
66 | else
67 | return -1;
68 | }
69 |
70 | void AddAnimeDialog::setUiId(int id)
71 | {
72 | _pmState = PmCleared;
73 | if(id == -1) {
74 | _ui->proxerIDLineEdit->clear();
75 | updatePm();
76 | } else {
77 | _ui->proxerIDLineEdit->setText(QString::number(id));
78 | _pmState = PmLoading;
79 | updatePm();
80 | _viewModel->imageLoader()->loadImage(id);
81 | }
82 | }
83 |
84 | void AddAnimeDialog::loadingChanged(bool loading)
85 | {
86 | _ui->proxerIDLineEdit->setEnabled(!loading);
87 | updatePm();
88 | }
89 |
90 | void AddAnimeDialog::imageLoaded(int id, const QImage &image)
91 | {
92 | if(_viewModel->id() == id) {
93 | _currentPixmap = QPixmap::fromImage(image);
94 | _pmState = PmLoaded;
95 | updatePm();
96 | }
97 | }
98 |
99 | void AddAnimeDialog::imageLoadFailed(int id, const QString &error)
100 | {
101 | if(_viewModel->id() == id) {
102 | qWarning().noquote() << error;
103 | _pmState = PmError;
104 | updatePm();
105 | }
106 | }
107 |
108 | void AddAnimeDialog::updatePm()
109 | {
110 | if(_viewModel->isLoading() || _pmState == PmLoading) {
111 | _loadingMovie->start();
112 | _ui->previewLabel->clear();
113 | _ui->previewLabel->setScaledContents(false);
114 | _ui->previewLabel->setMovie(_loadingMovie);
115 | } else {
116 | switch(_pmState) {
117 | case AddAnimeDialog::PmCleared:
118 | _currentPixmap = QPixmap();
119 | _ui->previewLabel->clear();
120 | _ui->previewLabel->setText(tr("Preview"));
121 | break;
122 | case AddAnimeDialog::PmLoaded:
123 | _ui->previewLabel->clear();
124 | _ui->previewLabel->setScaledContents(true);
125 | _ui->previewLabel->setPixmap(_currentPixmap);
126 | break;
127 | case AddAnimeDialog::PmError:
128 | _currentPixmap = QPixmap();
129 | _ui->previewLabel->clear();
130 | _ui->previewLabel->setText(tr("No image"));
131 | break;
132 | default:
133 | Q_UNREACHABLE();
134 | break;
135 | }
136 | _loadingMovie->stop();
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/gui/widgets/detailsdockwidget.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | DetailsDockWidget
4 |
5 |
6 |
7 | 0
8 | 0
9 | 202
10 | 419
11 |
12 |
13 |
14 |
15 | 202
16 | 90
17 |
18 |
19 |
20 | Anime Details
21 |
22 |
23 |
24 |
25 | 202
26 | 0
27 |
28 |
29 |
30 |
31 | 0
32 |
33 |
34 | 0
35 |
36 |
37 | 0
38 |
39 |
40 | 0
41 |
42 |
43 | 0
44 |
45 | -
46 |
47 |