diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 639c264d5..728bf6336 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -169,6 +169,7 @@ dependencies { implementation("androidx.room:room-paging:$room_version") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01") + implementation("androidx.work:work-runtime-ktx:2.8.0") implementation("com.drakeet.drawer:drawer:1.0.3") implementation("com.github.chrisbanes:PhotoView:2.3.0") // Dead Dependency implementation("com.github.tachiyomiorg:DirectionalViewPager:1.0.0") // Dead Dependency diff --git a/app/src/main/java/com/hippo/ehviewer/EhApplication.kt b/app/src/main/java/com/hippo/ehviewer/EhApplication.kt index baa586d41..a6f9807c0 100644 --- a/app/src/main/java/com/hippo/ehviewer/EhApplication.kt +++ b/app/src/main/java/com/hippo/ehviewer/EhApplication.kt @@ -21,10 +21,6 @@ import android.app.Activity import android.app.ActivityManager import android.app.Application import android.content.ComponentCallbacks2 -import android.text.Html -import android.text.method.LinkMovementMethod -import android.view.View -import android.widget.TextView import androidx.appcompat.app.AppCompatDelegate import androidx.collection.LruCache import androidx.core.content.getSystemService @@ -34,15 +30,11 @@ import androidx.lifecycle.ProcessLifecycleOwner import coil.ImageLoader import coil.ImageLoaderFactory import com.hippo.Native -import com.hippo.app.BaseDialogBuilder import com.hippo.ehviewer.client.EhCookieStore import com.hippo.ehviewer.client.EhDns -import com.hippo.ehviewer.client.EhRequestBuilder import com.hippo.ehviewer.client.EhSSLSocketFactory import com.hippo.ehviewer.client.EhTagDatabase -import com.hippo.ehviewer.client.EhUrl import com.hippo.ehviewer.client.data.GalleryDetail -import com.hippo.ehviewer.client.parser.EventPaneParser import com.hippo.ehviewer.dao.buildMainDB import com.hippo.ehviewer.download.DownloadManager import com.hippo.ehviewer.ui.CommonOperations @@ -56,7 +48,6 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.util.lang.launchIO import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import okhttp3.OkHttpClient import java.net.Proxy import java.security.KeyStore @@ -111,9 +102,6 @@ class EhApplication : Application(), DefaultLifecycleObserver, ImageLoaderFactor launchIO { cleanupDownload() } - launchIO { - theDawnOfNewDay() - } } mIdGenerator.setNextId(Settings.getInt(KEY_GLOBAL_STUFF_NEXT_ID, 0)) } @@ -140,54 +128,6 @@ class EhApplication : Application(), DefaultLifecycleObserver, ImageLoaderFactor } } - private fun theDawnOfNewDay() { - if (!Settings.getRequestNews()) { - return - } - - if (ehCookieStore.hasSignedIn()) { - val referer = EhUrl.REFERER_E - val request = EhRequestBuilder(EhUrl.HOST_E + "news.php", referer).build() - val call = okHttpClient.newCall(request) - try { - call.execute().use { response -> - val responseBody = response.body - val body = responseBody.string() - val html = EventPaneParser.parse(body) - if (html != null) { - showEventPane(html) - } - } - } catch (e: Throwable) { - e.printStackTrace() - } - } - } - - fun showEventPane(html: String) { - if (Settings.getHideHvEvents() && html.contains("You have encountered a monster!")) { - return - } - val activity = topActivity - activity?.runOnUiThread { - val dialog = BaseDialogBuilder(activity) - .setMessage(Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY)) - .setPositiveButton(android.R.string.ok, null) - .create() - dialog.setOnShowListener { - val messageView = dialog.findViewById(android.R.id.message) - if (messageView is TextView) { - messageView.movementMethod = LinkMovementMethod.getInstance() - } - } - try { - dialog.show() - } catch (t: Throwable) { - // ignore - } - } - } - private fun clearTempDir() { var dir = AppConfig.getTempDir() if (null != dir) { @@ -243,7 +183,6 @@ class EhApplication : Application(), DefaultLifecycleObserver, ImageLoaderFactor locked = true } - @OptIn(ExperimentalCoroutinesApi::class) override fun newImageLoader(): ImageLoader { return ImageLoader.Builder(this).apply { okHttpClient(okHttpClient) diff --git a/app/src/main/java/com/hippo/ehviewer/Settings.java b/app/src/main/java/com/hippo/ehviewer/Settings.java index 247d81326..549baf6d7 100755 --- a/app/src/main/java/com/hippo/ehviewer/Settings.java +++ b/app/src/main/java/com/hippo/ehviewer/Settings.java @@ -63,6 +63,9 @@ public class Settings { public static final String KEY_EXCLUDED_TAG_NAMESPACES = "excluded_tag_namespaces"; public static final String KEY_EXCLUDED_LANGUAGES = "excluded_languages"; public static final String KEY_REQUEST_NEWS = "request_news"; + public static final String KEY_REQUEST_NEWS_TIMER = "request_news_timer"; + public static final String KEY_REQUEST_NEWS_TIMER_HOUR = "request_news_timer_hour"; + public static final String KEY_REQUEST_NEWS_TIMER_MINUTE = "request_news_timer_minute"; public static final String KEY_HIDE_HV_EVENTS = "hide_hv_events"; public static final String[] SIGN_IN_REQUIRED = { KEY_IMAGE_LIMITS, @@ -205,7 +208,7 @@ public class Settings { private static final int DEFAULT_CLIPBOARD_TEXT_HASH_CODE = 0; private static final String KEY_DOWNLOAD_DELAY = "download_delay"; private static final int DEFAULT_DOWNLOAD_DELAY = 0; - private static final boolean DEFAULT_REQUEST_NEWS = true; + private static final boolean DEFAULT_REQUEST_NEWS = false; private static final String KEY_ARCHIVE_PASSWDS = "archive_passwds"; private static final String KEY_QS_SAVE_PROGRESS = "qs_save_progress"; private static final boolean DEFAULT_QS_SAVE_PROGRESS = true; diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt index 2f22cc559..2d47b8b69 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.kt @@ -20,7 +20,6 @@ import android.util.Log import android.util.Pair import com.hippo.ehviewer.AppConfig import com.hippo.ehviewer.EhApplication -import com.hippo.ehviewer.EhApplication.Companion.application import com.hippo.ehviewer.GetText import com.hippo.ehviewer.R import com.hippo.ehviewer.Settings @@ -31,7 +30,6 @@ import com.hippo.ehviewer.client.data.PreviewSet import com.hippo.ehviewer.client.exception.EhException import com.hippo.ehviewer.client.exception.ParseException import com.hippo.ehviewer.client.parser.ArchiveParser -import com.hippo.ehviewer.client.parser.EventPaneParser import com.hippo.ehviewer.client.parser.FavoritesParser import com.hippo.ehviewer.client.parser.ForumsParser import com.hippo.ehviewer.client.parser.GalleryApiParser @@ -322,10 +320,6 @@ object EhEngine { code = response.code headers = response.headers body = response.body.string() - val html = EventPaneParser.parse(body) - if (html != null) { - application.showEventPane(html) - } return GalleryDetailParser.parse(body!!) } } catch (e: Throwable) { diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/EventPaneParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/EventPaneParser.kt similarity index 53% rename from app/src/main/java/com/hippo/ehviewer/client/parser/EventPaneParser.java rename to app/src/main/java/com/hippo/ehviewer/client/parser/EventPaneParser.kt index a09eb219c..bd90cd89e 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/EventPaneParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/EventPaneParser.kt @@ -15,31 +15,12 @@ * You should have received a copy of the GNU General Public License along with EhViewer. * If not, see . */ +package com.hippo.ehviewer.client.parser -package com.hippo.ehviewer.client.parser; +import org.jsoup.Jsoup -import com.hippo.util.ExceptionUtils; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; - -public class EventPaneParser { - - public static String parse(String body) { - String event = null; - try { - Document d = Jsoup.parse(body); - Element eventpane = d.getElementById("eventpane"); - if (eventpane != null) { - event = eventpane.html(); - } - } catch (Throwable e) { - ExceptionUtils.throwIfFatal(e); - e.printStackTrace(); - } - return event; +object EventPaneParser { + fun parse(body: String): String? { + return Jsoup.parse(body).getElementById("eventpane")?.html() } - - } diff --git a/app/src/main/java/com/hippo/ehviewer/dailycheck/DailyCheckWork.kt b/app/src/main/java/com/hippo/ehviewer/dailycheck/DailyCheckWork.kt new file mode 100644 index 000000000..fcd7dc8ac --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/dailycheck/DailyCheckWork.kt @@ -0,0 +1,112 @@ +package com.hippo.ehviewer.dailycheck + +import android.annotation.SuppressLint +import android.content.Context +import android.text.Html +import androidx.core.app.NotificationChannelCompat +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.work.Constraints +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.NetworkType +import androidx.work.PeriodicWorkRequest +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkManager +import androidx.work.Worker +import androidx.work.WorkerParameters +import com.hippo.ehviewer.EhApplication +import com.hippo.ehviewer.R +import com.hippo.ehviewer.Settings +import com.hippo.ehviewer.client.EhRequestBuilder +import com.hippo.ehviewer.client.EhUrl +import com.hippo.ehviewer.client.parser.EventPaneParser +import java.time.Duration +import java.time.LocalDateTime + +private val signedIn + get() = EhApplication.ehCookieStore.hasSignedIn() + +private const val CHANNEL_ID = "DailyCheckNotification" + +class DailyCheckWork(val context: Context, workerParams: WorkerParameters) : + Worker(context, workerParams) { + override fun doWork(): Result { + runCatching { + if (signedIn) { + val referer = EhUrl.REFERER_E + val request = EhRequestBuilder(EhUrl.HOST_E + "news.php", referer).build() + val call = EhApplication.okHttpClient.newCall(request) + call.execute().use { response -> + val responseBody = response.body + val body = responseBody.string() + EventPaneParser.parse(body)?.let { showEventPane(it) } + } + } + }.onFailure { + it.printStackTrace() + return Result.retry() + } + return Result.success() + } + + @SuppressLint("MissingPermission") + private fun showEventPane(html: String) { + if (Settings.getHideHvEvents() && html.contains("You have encountered a monster!")) { + return + } + val notificationManager = NotificationManagerCompat.from(context) + val chan = NotificationChannelCompat + .Builder(CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_LOW) + .setName(CHANNEL_ID) + .build() + notificationManager.createNotificationChannel(chan) + val msg = NotificationCompat.Builder(context, CHANNEL_ID) + .setSmallIcon(R.drawable.ic_launcher_monochrome) + .setContentText(Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY)) + .setStyle(NotificationCompat.BigTextStyle()) + .build() + runCatching { + notificationManager.notify(1, msg) + }.onFailure { + it.printStackTrace() + } + } +} + +val schedHour + get() = Settings.getInt(Settings.KEY_REQUEST_NEWS_TIMER_HOUR, -1).takeUnless { it == -1 } + +val schedMinute + get() = Settings.getInt(Settings.KEY_REQUEST_NEWS_TIMER_MINUTE, -1).takeUnless { it == -1 } + +private val whenToWork + get() = LocalDateTime.now() + .run { withHour(schedHour ?: hour) } + .run { withMinute(schedMinute ?: minute) } + +private val initialDelay + get() = Duration.between(LocalDateTime.now(), whenToWork) + +private fun getDailyCheckWorkRequest(): PeriodicWorkRequest { + val constraints = Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + return PeriodicWorkRequestBuilder(Duration.ofDays(1)) + .setConstraints(constraints) + .setInitialDelay(initialDelay) + .build() +} + +private const val workName = "DailyCheckWork" + +fun updateDailyCheckWork(context: Context) { + if (Settings.getRequestNews()) { + WorkManager.getInstance(context).enqueueUniquePeriodicWork( + workName, + ExistingPeriodicWorkPolicy.UPDATE, + getDailyCheckWorkRequest() + ) + } else { + WorkManager.getInstance(context).cancelUniqueWork(workName) + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/fragment/EhFragment.kt b/app/src/main/java/com/hippo/ehviewer/ui/fragment/EhFragment.kt index 9af8911fc..693289b06 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/fragment/EhFragment.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/fragment/EhFragment.kt @@ -23,12 +23,17 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.lifecycle.lifecycleScope import androidx.preference.ListPreference import androidx.preference.Preference +import com.google.android.material.timepicker.MaterialTimePicker +import com.google.android.material.timepicker.TimeFormat.CLOCK_12H import com.hippo.ehviewer.EhApplication import com.hippo.ehviewer.R import com.hippo.ehviewer.Settings import com.hippo.ehviewer.client.EhCookieStore import com.hippo.ehviewer.client.EhEngine import com.hippo.ehviewer.client.EhTagDatabase +import com.hippo.ehviewer.dailycheck.schedHour +import com.hippo.ehviewer.dailycheck.schedMinute +import com.hippo.ehviewer.dailycheck.updateDailyCheckWork import eu.kanade.tachiyomi.util.lang.launchNonCancellable class EhFragment : BasePreferenceFragment() { @@ -67,6 +72,27 @@ class EhFragment : BasePreferenceFragment() { preferenceScreen.removePreference(preference!!) } } + + findPreference(Settings.KEY_REQUEST_NEWS_TIMER)!!.apply { + setOnPreferenceClickListener { + MaterialTimePicker.Builder() + .apply { + schedHour?.let { setHour(it) } + schedMinute?.let { setMinute(it) } + } + .setTimeFormat(CLOCK_12H) + .build() + .apply { + addOnPositiveButtonClickListener { + Settings.putInt(Settings.KEY_REQUEST_NEWS_TIMER_HOUR, hour) + Settings.putInt(Settings.KEY_REQUEST_NEWS_TIMER_MINUTE, minute) + updateDailyCheckWork(requireContext()) + } + } + .show(childFragmentManager, null) + false + } + } } override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean { @@ -105,6 +131,10 @@ class EhFragment : BasePreferenceFragment() { EhApplication.application.recreateAllActivity() } return true + } else if (Settings.KEY_REQUEST_NEWS == key) { + updateDailyCheckWork(requireContext()) + } else if (Settings.KEY_REQUEST_NEWS_TIMER == key) { + updateDailyCheckWork(requireContext()) } return true } diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 4bb7c7355..c1fe66934 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -516,7 +516,7 @@ 流量计费网络警告 正在使用流量计费网络 从第 %d 页阅读 - 启动时请求新闻页面 + 定时请求新闻页面 隐藏 HV 事件通知 复制翻译 恢复默认路径 @@ -627,5 +627,6 @@ 反向竖屏 关闭 使用Display P3色彩空间 + 设置请求新闻页面的时间 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8dbb727c7..434179c1f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -585,7 +585,7 @@ Metered network warning Connected to metered networks Read page %d - Request news page on start + Timed request news page Hide HV event Notifications Copy translation Reset to default @@ -696,5 +696,6 @@ Save Save to… Use Display P3 color space + Set time to request news diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index fdf58c88e..24ec900ba 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -44,6 +44,7 @@ @style/MaterialCalendarTheme @style/BottomSheetDialogTheme @style/MaterialAlertDialogTheme + @style/MaterialTimePickerTheme + + diff --git a/app/src/main/res/xml/eh_settings.xml b/app/src/main/res/xml/eh_settings.xml index ab6d4bef2..143faea7e 100644 --- a/app/src/main/res/xml/eh_settings.xml +++ b/app/src/main/res/xml/eh_settings.xml @@ -171,10 +171,17 @@ android:title="@string/settings_eh_request_news" app:iconSpaceReserved="false" /> + + diff --git a/gradle.properties b/gradle.properties index f40354e6d..803bc4c26 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,4 +20,3 @@ org.gradle.parallel=true org.gradle.caching=true android.useAndroidX=true android.enableAppCompileTimeRClass=true -android.experimental.enableNewResourceShrinker.preciseShrinking=true