diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ebd72d3a6..7f9fd2992 100755
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -23,6 +23,7 @@
android:name="android.hardware.touchscreen"
android:required="false" />
+
diff --git a/app/src/main/java/com/hippo/ehviewer/EhApplication.java b/app/src/main/java/com/hippo/ehviewer/EhApplication.java
index 7ebe4885b..5d60aa981 100644
--- a/app/src/main/java/com/hippo/ehviewer/EhApplication.java
+++ b/app/src/main/java/com/hippo/ehviewer/EhApplication.java
@@ -61,6 +61,7 @@ import com.hippo.ehviewer.client.parser.EventPaneParser;
import com.hippo.ehviewer.download.DownloadManager;
import com.hippo.ehviewer.spider.SpiderDen;
import com.hippo.ehviewer.ui.CommonOperations;
+import com.hippo.ehviewer.ui.EhActivity;
import com.hippo.image.Image;
import com.hippo.image.ImageBitmap;
import com.hippo.network.StatusCodeException;
@@ -532,9 +533,9 @@ public class EhApplication extends SceneApplication {
}
@Nullable
- public Activity getTopActivity() {
+ public EhActivity getTopActivity() {
if (!mActivityList.isEmpty()) {
- return mActivityList.get(mActivityList.size() - 1);
+ return (EhActivity) mActivityList.get(mActivityList.size() - 1);
} else {
return null;
}
diff --git a/app/src/main/java/com/hippo/ehviewer/Settings.java b/app/src/main/java/com/hippo/ehviewer/Settings.java
index b90e7880f..f7f698a69 100755
--- a/app/src/main/java/com/hippo/ehviewer/Settings.java
+++ b/app/src/main/java/com/hippo/ehviewer/Settings.java
@@ -76,6 +76,8 @@ public class Settings {
public static final String KEY_DOWNLOAD_SAVE_SCHEME = "image_scheme";
public static final String KEY_DOWNLOAD_SAVE_AUTHORITY = "image_authority";
public static final String KEY_DOWNLOAD_SAVE_PATH = "image_path";
+
+ public static final String KEY_NOTIFICATION_REQUIRED = "notification_required";
public static final String KEY_DOWNLOAD_SAVE_QUERY = "image_query";
public static final String KEY_DOWNLOAD_SAVE_FRAGMENT = "image_fragment";
public static final String KEY_MEDIA_SCAN = "media_scan";
@@ -1183,4 +1185,12 @@ public class Settings {
LIST_THUMB_SIZE_INITED = true;
return size;
}
+
+ public static boolean getNotificationRequired() {
+ return getBoolean(KEY_NOTIFICATION_REQUIRED, false);
+ }
+
+ public static void putNotificationRequired() {
+ putBoolean(KEY_NOTIFICATION_REQUIRED, true);
+ }
}
diff --git a/app/src/main/java/com/hippo/ehviewer/download/DownloadService.java b/app/src/main/java/com/hippo/ehviewer/download/DownloadService.java
deleted file mode 100644
index ad5c091fc..000000000
--- a/app/src/main/java/com/hippo/ehviewer/download/DownloadService.java
+++ /dev/null
@@ -1,581 +0,0 @@
-/*
- * Copyright 2016 Hippo Seven
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.hippo.ehviewer.download;
-
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.SystemClock;
-import android.util.Log;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.Nullable;
-import androidx.core.app.NotificationChannelCompat;
-import androidx.core.app.NotificationCompat;
-import androidx.core.app.NotificationManagerCompat;
-
-import com.hippo.ehviewer.EhApplication;
-import com.hippo.ehviewer.R;
-import com.hippo.ehviewer.client.EhUtils;
-import com.hippo.ehviewer.client.data.GalleryInfo;
-import com.hippo.ehviewer.dao.DownloadInfo;
-import com.hippo.ehviewer.ui.MainActivity;
-import com.hippo.ehviewer.ui.scene.DownloadsScene;
-import com.hippo.scene.StageActivity;
-import com.hippo.util.ReadableTime;
-import com.hippo.yorozuya.FileUtils;
-import com.hippo.yorozuya.SimpleHandler;
-import com.hippo.yorozuya.collect.LongList;
-import com.hippo.yorozuya.collect.SparseJBArray;
-import com.hippo.yorozuya.collect.SparseJLArray;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-public class DownloadService extends Service implements DownloadManager.DownloadListener {
-
- public static final String ACTION_START = "start";
- public static final String ACTION_START_RANGE = "start_range";
- public static final String ACTION_START_ALL = "start_all";
- public static final String ACTION_STOP = "stop";
- public static final String ACTION_STOP_RANGE = "stop_range";
- public static final String ACTION_STOP_CURRENT = "stop_current";
- public static final String ACTION_STOP_ALL = "stop_all";
- public static final String ACTION_DELETE = "delete";
- public static final String ACTION_DELETE_RANGE = "delete_range";
-
- public static final String ACTION_CLEAR = "clear";
-
- public static final String KEY_GALLERY_INFO = "gallery_info";
- public static final String KEY_LABEL = "label";
- public static final String KEY_GID = "gid";
- public static final String KEY_GID_LIST = "gid_list";
-
- private static final int ID_DOWNLOADING = 1;
- private static final int ID_DOWNLOADED = 2;
- private static final int ID_509 = 3;
- private final static SparseJBArray sItemStateArray = new SparseJBArray();
- private final static SparseJLArray sItemTitleArray = new SparseJLArray<>();
- private static int sFailedCount;
- private static int sFinishedCount;
- private static int sDownloadedCount;
- @Nullable
- private NotificationManagerCompat mNotifyManager;
- @Nullable
- private DownloadManager mDownloadManager;
- private NotificationCompat.Builder mDownloadingBuilder;
- private NotificationCompat.Builder mDownloadedBuilder;
- private NotificationCompat.Builder m509dBuilder;
- private NotificationDelay mDownloadingDelay;
- private NotificationDelay mDownloadedDelay;
- private NotificationDelay m509Delay;
- private String CHANNEL_ID;
-
- public static void clear() {
- sFailedCount = 0;
- sFinishedCount = 0;
- sDownloadedCount = 0;
- sItemStateArray.clear();
- sItemTitleArray.clear();
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
-
- CHANNEL_ID = getPackageName() + ".download";
- mNotifyManager = NotificationManagerCompat.from(this);
- mNotifyManager.createNotificationChannel(
- new NotificationChannelCompat.Builder(CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_LOW)
- .setName(getString(R.string.download_service))
- .build());
- mDownloadManager = EhApplication.getDownloadManager(getApplicationContext());
- mDownloadManager.setDownloadListener(this);
-
- ensureDownloadingBuilder();
-
- mDownloadingBuilder.setContentTitle(getString(R.string.download_service))
- .setContentText(null)
- .setSubText(null)
- .setProgress(0, 0, true);
-
- startForeground(ID_DOWNLOADING, mDownloadingBuilder.build());
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
-
- mNotifyManager = null;
- if (mDownloadManager != null) {
- mDownloadManager.setDownloadListener(null);
- mDownloadManager = null;
- }
- mDownloadingBuilder = null;
- mDownloadedBuilder = null;
- m509dBuilder = null;
- if (mDownloadingDelay != null) {
- mDownloadingDelay.release();
- }
- if (mDownloadedDelay != null) {
- mDownloadedDelay.release();
- }
- if (m509Delay != null) {
- m509Delay.release();
- }
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- handleIntent(intent);
- return START_STICKY;
- }
-
- private void handleIntent(Intent intent) {
- String action = null;
- if (intent != null) {
- action = intent.getAction();
- }
-
- if (ACTION_START.equals(action)) {
- GalleryInfo gi = intent.getParcelableExtra(KEY_GALLERY_INFO);
- String label = intent.getStringExtra(KEY_LABEL);
- if (gi != null && mDownloadManager != null) {
- mDownloadManager.startDownload(gi, label);
- }
- } else if (ACTION_START_RANGE.equals(action)) {
- LongList gidList = intent.getParcelableExtra(KEY_GID_LIST);
- if (gidList != null && mDownloadManager != null) {
- mDownloadManager.startRangeDownload(gidList);
- }
- } else if (ACTION_START_ALL.equals(action)) {
- if (mDownloadManager != null) {
- mDownloadManager.startAllDownload();
- }
- } else if (ACTION_STOP.equals(action)) {
- long gid = intent.getLongExtra(KEY_GID, -1);
- if (gid != -1 && mDownloadManager != null) {
- mDownloadManager.stopDownload(gid);
- }
- } else if (ACTION_STOP_CURRENT.equals(action)) {
- if (mDownloadManager != null) {
- mDownloadManager.stopCurrentDownload();
- }
- } else if (ACTION_STOP_RANGE.equals(action)) {
- LongList gidList = intent.getParcelableExtra(KEY_GID_LIST);
- if (gidList != null && mDownloadManager != null) {
- mDownloadManager.stopRangeDownload(gidList);
- }
- } else if (ACTION_STOP_ALL.equals(action)) {
- if (mDownloadManager != null) {
- mDownloadManager.stopAllDownload();
- }
- } else if (ACTION_DELETE.equals(action)) {
- long gid = intent.getLongExtra(KEY_GID, -1);
- if (gid != -1 && mDownloadManager != null) {
- mDownloadManager.deleteDownload(gid);
- }
- } else if (ACTION_DELETE_RANGE.equals(action)) {
- LongList gidList = intent.getParcelableExtra(KEY_GID_LIST);
- if (gidList != null && mDownloadManager != null) {
- mDownloadManager.deleteRangeDownload(gidList);
- }
- } else if (ACTION_CLEAR.equals(action)) {
- clear();
- }
-
- checkStopSelf();
- }
-
- @Nullable
- @Override
- public IBinder onBind(Intent intent) {
- throw new IllegalStateException("No bindService");
- }
-
- private void ensureDownloadingBuilder() {
- if (mDownloadingBuilder != null) {
- return;
- }
-
- Intent stopAllIntent = new Intent(this, DownloadService.class);
- stopAllIntent.setAction(ACTION_STOP_ALL);
- PendingIntent piStopAll = PendingIntent.getService(this, 0, stopAllIntent, PendingIntent.FLAG_IMMUTABLE);
-
- mDownloadingBuilder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID)
- .setSmallIcon(android.R.drawable.stat_sys_download)
- .setOngoing(true)
- .setAutoCancel(false)
- .setCategory(NotificationCompat.CATEGORY_PROGRESS)
- .addAction(R.drawable.v_pause_x24, getString(R.string.stat_download_action_stop_all), piStopAll)
- .setShowWhen(false)
- .setChannelId(CHANNEL_ID);
-
- mDownloadingDelay = new NotificationDelay(this, mNotifyManager, mDownloadingBuilder, ID_DOWNLOADING);
- }
-
- private void ensureDownloadedBuilder() {
- if (mDownloadedBuilder != null) {
- return;
- }
-
- Intent clearIntent = new Intent(this, DownloadService.class);
- clearIntent.setAction(ACTION_CLEAR);
- PendingIntent piClear = PendingIntent.getService(this, 0, clearIntent, PendingIntent.FLAG_IMMUTABLE);
-
- Bundle bundle = new Bundle();
- bundle.putString(DownloadsScene.KEY_ACTION, DownloadsScene.ACTION_CLEAR_DOWNLOAD_SERVICE);
- Intent activityIntent = new Intent(this, MainActivity.class);
- activityIntent.setAction(StageActivity.ACTION_START_SCENE);
- activityIntent.putExtra(StageActivity.KEY_SCENE_NAME, DownloadsScene.class.getName());
- activityIntent.putExtra(StageActivity.KEY_SCENE_ARGS, bundle);
- PendingIntent piActivity = PendingIntent.getActivity(DownloadService.this, 0,
- activityIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
-
- mDownloadedBuilder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID)
- .setSmallIcon(android.R.drawable.stat_sys_download_done)
- .setContentTitle(getString(R.string.stat_download_done_title))
- .setDeleteIntent(piClear)
- .setOngoing(false)
- .setAutoCancel(true)
- .setContentIntent(piActivity);
-
- mDownloadedDelay = new NotificationDelay(this, mNotifyManager, mDownloadedBuilder, ID_DOWNLOADED);
- }
-
- private void ensure509Builder() {
- if (m509dBuilder != null) {
- return;
- }
-
- m509dBuilder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID)
- .setSmallIcon(R.drawable.ic_baseline_warning_24)
- .setContentText(getString(R.string.stat_509_alert_title))
- .setContentText(getString(R.string.stat_509_alert_text))
- .setStyle(new NotificationCompat.BigTextStyle().bigText(getString(R.string.stat_509_alert_text)))
- .setAutoCancel(true)
- .setOngoing(false)
- .setCategory(NotificationCompat.CATEGORY_ERROR);
-
- m509Delay = new NotificationDelay(this, mNotifyManager, m509dBuilder, ID_509);
- }
-
- @Override
- public void onGet509() {
- if (mDownloadManager != null) {
- mDownloadManager.stopAllDownload();
- }
- if (mNotifyManager == null) {
- return;
- }
-
- ensure509Builder();
- m509dBuilder.setWhen(System.currentTimeMillis());
- m509Delay.show();
- }
-
- @Override
- public void onStart(DownloadInfo info) {
- if (mNotifyManager == null) {
- return;
- }
-
- ensureDownloadingBuilder();
-
- Bundle bundle = new Bundle();
- bundle.putLong(DownloadsScene.KEY_GID, info.gid);
- Intent activityIntent = new Intent(this, MainActivity.class);
- activityIntent.setAction(StageActivity.ACTION_START_SCENE);
- activityIntent.putExtra(StageActivity.KEY_SCENE_NAME, DownloadsScene.class.getName());
- activityIntent.putExtra(StageActivity.KEY_SCENE_ARGS, bundle);
- PendingIntent piActivity = PendingIntent.getActivity(DownloadService.this, 0,
- activityIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
-
- mDownloadingBuilder.setContentTitle(EhUtils.getSuitableTitle(info))
- .setContentText(null)
- .setSubText(null)
- .setProgress(0, 0, true)
- .setContentIntent(piActivity);
-
- mDownloadingDelay.startForeground();
- }
-
- private void onUpdate(DownloadInfo info) {
- if (mNotifyManager == null) {
- return;
- }
- ensureDownloadingBuilder();
-
- long speed = info.speed;
- if (speed < 0) {
- speed = 0;
- }
- String text = FileUtils.humanReadableByteCount(speed, false) + "/s";
- long remaining = info.remaining;
- if (remaining >= 0) {
- text = getString(R.string.download_speed_text_2, text, ReadableTime.getShortTimeInterval(remaining));
- } else {
- text = getString(R.string.download_speed_text, text);
- }
- mDownloadingBuilder.setContentTitle(EhUtils.getSuitableTitle(info))
- .setContentText(text)
- .setStyle(new NotificationCompat.BigTextStyle().bigText(text))
- .setSubText(info.total == -1 || info.finished == -1 ? null : info.finished + "/" + info.total)
- .setProgress(info.total, info.finished, false);
-
- mDownloadingDelay.startForeground();
- }
-
- @Override
- public void onDownload(DownloadInfo info) {
- onUpdate(info);
- }
-
- @Override
- public void onGetPage(DownloadInfo info) {
- onUpdate(info);
- }
-
- @Override
- public void onFinish(DownloadInfo info) {
- if (mNotifyManager == null) {
- return;
- }
-
- if (null != mDownloadingDelay) {
- mDownloadingDelay.cancel();
- }
-
- ensureDownloadedBuilder();
-
- boolean finish = info.state == DownloadInfo.STATE_FINISH;
- long gid = info.gid;
- int index = sItemStateArray.indexOfKey(gid);
- if (index < 0) { // Not contain
- sItemStateArray.put(gid, finish);
- sItemTitleArray.put(gid, EhUtils.getSuitableTitle(info));
- sDownloadedCount++;
- if (finish) {
- sFinishedCount++;
- } else {
- sFailedCount++;
- }
- } else { // Contain
- boolean oldFinish = sItemStateArray.valueAt(index);
- sItemStateArray.put(gid, finish);
- sItemTitleArray.put(gid, EhUtils.getSuitableTitle(info));
- if (oldFinish && !finish) {
- sFinishedCount--;
- sFailedCount++;
- } else if (!oldFinish && finish) {
- sFinishedCount++;
- sFailedCount--;
- }
- }
-
- String text;
- boolean needStyle;
- if (sFinishedCount != 0 && sFailedCount == 0) {
- if (sFinishedCount == 1) {
- if (sItemTitleArray.size() >= 1) {
- text = getString(R.string.stat_download_done_line_succeeded, sItemTitleArray.valueAt(0));
- } else {
- Log.d("TAG", "WTF, sItemTitleArray is null");
- text = getString(R.string.error_unknown);
- }
- needStyle = false;
- } else {
- text = getString(R.string.stat_download_done_text_succeeded, sFinishedCount);
- needStyle = true;
- }
- } else if (sFinishedCount == 0 && sFailedCount != 0) {
- if (sFailedCount == 1) {
- if (sItemTitleArray.size() >= 1) {
- text = getString(R.string.stat_download_done_line_failed, sItemTitleArray.valueAt(0));
- } else {
- Log.d("TAG", "WTF, sItemTitleArray is null");
- text = getString(R.string.error_unknown);
- }
- needStyle = false;
- } else {
- text = getString(R.string.stat_download_done_text_failed, sFailedCount);
- needStyle = true;
- }
- } else {
- text = getString(R.string.stat_download_done_text_mix, sFinishedCount, sFailedCount);
- needStyle = true;
- }
-
- NotificationCompat.InboxStyle style;
- if (needStyle) {
- style = new NotificationCompat.InboxStyle();
- style.setBigContentTitle(getString(R.string.stat_download_done_title));
- SparseJBArray stateArray = sItemStateArray;
- for (int i = 0, n = stateArray.size(); i < n; i++) {
- long id = stateArray.keyAt(i);
- boolean fin = stateArray.valueAt(i);
- String title = sItemTitleArray.get(id);
- if (title == null) {
- continue;
- }
- style.addLine(getString(fin ? R.string.stat_download_done_line_succeeded :
- R.string.stat_download_done_line_failed, title));
- }
- } else {
- style = null;
- }
-
- mDownloadedBuilder.setContentText(text)
- .setStyle(style)
- .setWhen(System.currentTimeMillis())
- .setNumber(sDownloadedCount);
-
- mDownloadedDelay.show();
-
- checkStopSelf();
- }
-
- @Override
- public void onCancel(DownloadInfo info) {
- if (mNotifyManager == null) {
- return;
- }
-
- if (null != mDownloadingDelay) {
- mDownloadingDelay.cancel();
- }
-
- checkStopSelf();
- }
-
- private void checkStopSelf() {
- if (mDownloadManager == null || mDownloadManager.isIdle()) {
- stopForeground(true);
- stopSelf();
- }
- }
-
- // TODO Include all notification in one delay
- // Avoid frequent notification
- private static class NotificationDelay implements Runnable {
-
- private static final int OPS_NOTIFY = 0;
- private static final int OPS_CANCEL = 1;
- private static final int OPS_START_FOREGROUND = 2;
- private static final long DELAY = 1000; // 1s
- private final NotificationManagerCompat mNotifyManager;
- private final NotificationCompat.Builder mBuilder;
- private final int mId;
- private Service mService;
- private long mLastTime;
- private boolean mPosted;
- // false for show, true for cancel
- @Ops
- private int mOps;
-
- public NotificationDelay(Service service, NotificationManagerCompat notifyManager,
- NotificationCompat.Builder builder, int id) {
- mService = service;
- mNotifyManager = notifyManager;
- mBuilder = builder;
- mId = id;
- }
-
- public void release() {
- mService = null;
- }
-
- public void show() {
- if (mPosted) {
- mOps = OPS_NOTIFY;
- } else {
- long now = SystemClock.currentThreadTimeMillis();
- if (now - mLastTime > DELAY) {
- // Wait long enough, do it now
- mNotifyManager.notify(mId, mBuilder.build());
- } else {
- // Too quick, post delay
- mOps = OPS_NOTIFY;
- mPosted = true;
- SimpleHandler.getInstance().postDelayed(this, DELAY);
- }
- mLastTime = now;
- }
- }
-
- public void cancel() {
- if (mPosted) {
- mOps = OPS_CANCEL;
- } else {
- long now = SystemClock.currentThreadTimeMillis();
- if (now - mLastTime > DELAY) {
- // Wait long enough, do it now
- mNotifyManager.cancel(mId);
- } else {
- // Too quick, post delay
- mOps = OPS_CANCEL;
- mPosted = true;
- SimpleHandler.getInstance().postDelayed(this, DELAY);
- }
- }
- }
-
- public void startForeground() {
- if (mPosted) {
- mOps = OPS_START_FOREGROUND;
- } else {
- long now = SystemClock.currentThreadTimeMillis();
- if (now - mLastTime > DELAY) {
- // Wait long enough, do it now
- if (mService != null) {
- mService.startForeground(mId, mBuilder.build());
- }
- } else {
- // Too quick, post delay
- mOps = OPS_START_FOREGROUND;
- mPosted = true;
- SimpleHandler.getInstance().postDelayed(this, DELAY);
- }
- }
- }
-
- @Override
- public void run() {
- mPosted = false;
- switch (mOps) {
- case OPS_NOTIFY:
- mNotifyManager.notify(mId, mBuilder.build());
- break;
- case OPS_CANCEL:
- mNotifyManager.cancel(mId);
- break;
- case OPS_START_FOREGROUND:
- if (mService != null) {
- mService.startForeground(mId, mBuilder.build());
- }
- break;
- }
- }
-
- @IntDef({OPS_NOTIFY, OPS_CANCEL, OPS_START_FOREGROUND})
- @Retention(RetentionPolicy.SOURCE)
- private @interface Ops {
- }
- }
-}
diff --git a/app/src/main/java/com/hippo/ehviewer/download/DownloadService.kt b/app/src/main/java/com/hippo/ehviewer/download/DownloadService.kt
new file mode 100644
index 000000000..52d58f032
--- /dev/null
+++ b/app/src/main/java/com/hippo/ehviewer/download/DownloadService.kt
@@ -0,0 +1,550 @@
+/*
+ * Copyright 2016 Hippo Seven
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.hippo.ehviewer.download
+
+import android.Manifest
+import android.app.PendingIntent
+import android.app.Service
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.os.IBinder
+import android.os.SystemClock
+import android.util.Log
+import androidx.annotation.IntDef
+import androidx.core.app.ActivityCompat
+import androidx.core.app.NotificationChannelCompat
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import com.hippo.ehviewer.EhApplication
+import com.hippo.ehviewer.R
+import com.hippo.ehviewer.client.EhUtils
+import com.hippo.ehviewer.client.data.GalleryInfo
+import com.hippo.ehviewer.dao.DownloadInfo
+import com.hippo.ehviewer.ui.MainActivity
+import com.hippo.ehviewer.ui.scene.DownloadsScene
+import com.hippo.scene.StageActivity
+import com.hippo.util.ReadableTime
+import com.hippo.yorozuya.FileUtils
+import com.hippo.yorozuya.SimpleHandler
+import com.hippo.yorozuya.collect.LongList
+import com.hippo.yorozuya.collect.SparseJBArray
+import com.hippo.yorozuya.collect.SparseJLArray
+
+class DownloadService : Service(), DownloadManager.DownloadListener {
+ private var mNotifyManager: NotificationManagerCompat? = null
+ private var mDownloadManager: DownloadManager? = null
+ private var mDownloadingBuilder: NotificationCompat.Builder? = null
+ private var mDownloadedBuilder: NotificationCompat.Builder? = null
+ private var m509dBuilder: NotificationCompat.Builder? = null
+ private var mDownloadingDelay: NotificationDelay? = null
+ private var mDownloadedDelay: NotificationDelay? = null
+ private var m509Delay: NotificationDelay? = null
+ private var mChannelID: String? = null
+
+ override fun onCreate() {
+ super.onCreate()
+ mChannelID = "$packageName.download"
+ mNotifyManager = NotificationManagerCompat.from(this)
+ mNotifyManager!!.createNotificationChannel(
+ NotificationChannelCompat.Builder(
+ mChannelID!!,
+ NotificationManagerCompat.IMPORTANCE_LOW
+ )
+ .setName(getString(R.string.download_service))
+ .build()
+ )
+ mDownloadManager = EhApplication.getDownloadManager(applicationContext)
+ mDownloadManager!!.setDownloadListener(this)
+ ensureDownloadingBuilder()
+ mDownloadingBuilder!!.setContentTitle(getString(R.string.download_service))
+ .setContentText(null)
+ .setSubText(null)
+ .setProgress(0, 0, true)
+ startForeground(ID_DOWNLOADING, mDownloadingBuilder!!.build())
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ mNotifyManager = null
+ if (mDownloadManager != null) {
+ mDownloadManager!!.setDownloadListener(null)
+ mDownloadManager = null
+ }
+ mDownloadingBuilder = null
+ mDownloadedBuilder = null
+ m509dBuilder = null
+ }
+
+ override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
+ handleIntent(intent)
+ return START_STICKY
+ }
+
+ private fun handleIntent(intent: Intent?) {
+ var action: String? = null
+ if (intent != null) {
+ action = intent.action
+ }
+ if (ACTION_START == action) {
+ val gi = intent!!.getParcelableExtra(KEY_GALLERY_INFO)
+ val label = intent.getStringExtra(KEY_LABEL)
+ if (gi != null && mDownloadManager != null) {
+ mDownloadManager!!.startDownload(gi, label)
+ }
+ } else if (ACTION_START_RANGE == action) {
+ val gidList = intent!!.getParcelableExtra(KEY_GID_LIST)
+ if (gidList != null && mDownloadManager != null) {
+ mDownloadManager!!.startRangeDownload(gidList)
+ }
+ } else if (ACTION_START_ALL == action) {
+ if (mDownloadManager != null) {
+ mDownloadManager!!.startAllDownload()
+ }
+ } else if (ACTION_STOP == action) {
+ val gid = intent!!.getLongExtra(KEY_GID, -1)
+ if (gid != -1L && mDownloadManager != null) {
+ mDownloadManager!!.stopDownload(gid)
+ }
+ } else if (ACTION_STOP_CURRENT == action) {
+ if (mDownloadManager != null) {
+ mDownloadManager!!.stopCurrentDownload()
+ }
+ } else if (ACTION_STOP_RANGE == action) {
+ val gidList = intent!!.getParcelableExtra(KEY_GID_LIST)
+ if (gidList != null && mDownloadManager != null) {
+ mDownloadManager!!.stopRangeDownload(gidList)
+ }
+ } else if (ACTION_STOP_ALL == action) {
+ if (mDownloadManager != null) {
+ mDownloadManager!!.stopAllDownload()
+ }
+ } else if (ACTION_DELETE == action) {
+ val gid = intent!!.getLongExtra(KEY_GID, -1)
+ if (gid != -1L && mDownloadManager != null) {
+ mDownloadManager!!.deleteDownload(gid)
+ }
+ } else if (ACTION_DELETE_RANGE == action) {
+ val gidList = intent!!.getParcelableExtra(KEY_GID_LIST)
+ if (gidList != null && mDownloadManager != null) {
+ mDownloadManager!!.deleteRangeDownload(gidList)
+ }
+ } else if (ACTION_CLEAR == action) {
+ clear()
+ }
+ checkStopSelf()
+ }
+
+ override fun onBind(intent: Intent): IBinder? {
+ throw IllegalStateException("No bindService")
+ }
+
+ private fun ensureDownloadingBuilder() {
+ if (mDownloadingBuilder != null) {
+ return
+ }
+ val stopAllIntent = Intent(this, DownloadService::class.java)
+ stopAllIntent.action = ACTION_STOP_ALL
+ val piStopAll =
+ PendingIntent.getService(this, 0, stopAllIntent, PendingIntent.FLAG_IMMUTABLE)
+ mDownloadingBuilder = NotificationCompat.Builder(applicationContext, mChannelID!!)
+ .setSmallIcon(android.R.drawable.stat_sys_download)
+ .setOngoing(true)
+ .setAutoCancel(false)
+ .setCategory(NotificationCompat.CATEGORY_PROGRESS)
+ .addAction(
+ R.drawable.v_pause_x24,
+ getString(R.string.stat_download_action_stop_all),
+ piStopAll
+ )
+ .setShowWhen(false)
+ .setChannelId(mChannelID!!)
+ mDownloadingDelay =
+ NotificationDelay(this, mNotifyManager!!, mDownloadingBuilder!!, ID_DOWNLOADING)
+ }
+
+ private fun ensureDownloadedBuilder() {
+ if (mDownloadedBuilder != null) {
+ return
+ }
+ val clearIntent = Intent(this, DownloadService::class.java)
+ clearIntent.action = ACTION_CLEAR
+ val piClear = PendingIntent.getService(this, 0, clearIntent, PendingIntent.FLAG_IMMUTABLE)
+ val bundle = Bundle()
+ bundle.putString(DownloadsScene.KEY_ACTION, DownloadsScene.ACTION_CLEAR_DOWNLOAD_SERVICE)
+ val activityIntent = Intent(this, MainActivity::class.java)
+ activityIntent.action = StageActivity.ACTION_START_SCENE
+ activityIntent.putExtra(StageActivity.KEY_SCENE_NAME, DownloadsScene::class.java.name)
+ activityIntent.putExtra(StageActivity.KEY_SCENE_ARGS, bundle)
+ val piActivity = PendingIntent.getActivity(
+ this@DownloadService, 0,
+ activityIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ mDownloadedBuilder = NotificationCompat.Builder(applicationContext, mChannelID!!)
+ .setSmallIcon(android.R.drawable.stat_sys_download_done)
+ .setContentTitle(getString(R.string.stat_download_done_title))
+ .setDeleteIntent(piClear)
+ .setOngoing(false)
+ .setAutoCancel(true)
+ .setContentIntent(piActivity)
+ mDownloadedDelay =
+ NotificationDelay(this, mNotifyManager!!, mDownloadedBuilder!!, ID_DOWNLOADED)
+ }
+
+ private fun ensure509Builder() {
+ if (m509dBuilder != null) {
+ return
+ }
+ m509dBuilder = NotificationCompat.Builder(applicationContext, mChannelID!!)
+ .setSmallIcon(R.drawable.ic_baseline_warning_24)
+ .setContentText(getString(R.string.stat_509_alert_title))
+ .setContentText(getString(R.string.stat_509_alert_text))
+ .setStyle(
+ NotificationCompat.BigTextStyle().bigText(getString(R.string.stat_509_alert_text))
+ )
+ .setAutoCancel(true)
+ .setOngoing(false)
+ .setCategory(NotificationCompat.CATEGORY_ERROR)
+ m509Delay = NotificationDelay(this, mNotifyManager!!, m509dBuilder!!, ID_509)
+ }
+
+ override fun onGet509() {
+ if (mDownloadManager != null) {
+ mDownloadManager!!.stopAllDownload()
+ }
+ if (mNotifyManager == null) {
+ return
+ }
+ ensure509Builder()
+ m509dBuilder!!.setWhen(System.currentTimeMillis())
+ m509Delay!!.show()
+ }
+
+ override fun onStart(info: DownloadInfo) {
+ if (mNotifyManager == null) {
+ return
+ }
+ ensureDownloadingBuilder()
+ val bundle = Bundle()
+ bundle.putLong(DownloadsScene.KEY_GID, info.gid)
+ val activityIntent = Intent(this, MainActivity::class.java)
+ activityIntent.action = StageActivity.ACTION_START_SCENE
+ activityIntent.putExtra(StageActivity.KEY_SCENE_NAME, DownloadsScene::class.java.name)
+ activityIntent.putExtra(StageActivity.KEY_SCENE_ARGS, bundle)
+ val piActivity = PendingIntent.getActivity(
+ this@DownloadService, 0,
+ activityIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ mDownloadingBuilder!!.setContentTitle(EhUtils.getSuitableTitle(info))
+ .setContentText(null)
+ .setSubText(null)
+ .setProgress(0, 0, true)
+ .setContentIntent(piActivity)
+ mDownloadingDelay!!.startForeground()
+ }
+
+ private fun onUpdate(info: DownloadInfo) {
+ if (mNotifyManager == null) {
+ return
+ }
+ ensureDownloadingBuilder()
+ var speed = info.speed
+ if (speed < 0) {
+ speed = 0
+ }
+ var text = FileUtils.humanReadableByteCount(speed, false) + "/s"
+ val remaining = info.remaining
+ text = if (remaining >= 0) {
+ getString(
+ R.string.download_speed_text_2,
+ text,
+ ReadableTime.getShortTimeInterval(remaining)
+ )
+ } else {
+ getString(R.string.download_speed_text, text)
+ }
+ mDownloadingBuilder!!.setContentTitle(EhUtils.getSuitableTitle(info))
+ .setContentText(text)
+ .setStyle(NotificationCompat.BigTextStyle().bigText(text))
+ .setSubText(if (info.total == -1 || info.finished == -1) null else info.finished.toString() + "/" + info.total)
+ .setProgress(info.total, info.finished, false)
+ mDownloadingDelay!!.startForeground()
+ }
+
+ override fun onDownload(info: DownloadInfo) {
+ onUpdate(info)
+ }
+
+ override fun onGetPage(info: DownloadInfo) {
+ onUpdate(info)
+ }
+
+ override fun onFinish(info: DownloadInfo) {
+ if (mNotifyManager == null) {
+ return
+ }
+ if (null != mDownloadingDelay) {
+ mDownloadingDelay!!.cancel()
+ }
+ ensureDownloadedBuilder()
+ val finish = info.state == DownloadInfo.STATE_FINISH
+ val gid = info.gid
+ val index = sItemStateArray.indexOfKey(gid)
+ if (index < 0) { // Not contain
+ sItemStateArray.put(gid, finish)
+ sItemTitleArray.put(gid, EhUtils.getSuitableTitle(info))
+ sDownloadedCount++
+ if (finish) {
+ sFinishedCount++
+ } else {
+ sFailedCount++
+ }
+ } else { // Contain
+ val oldFinish = sItemStateArray.valueAt(index)
+ sItemStateArray.put(gid, finish)
+ sItemTitleArray.put(gid, EhUtils.getSuitableTitle(info))
+ if (oldFinish && !finish) {
+ sFinishedCount--
+ sFailedCount++
+ } else if (!oldFinish && finish) {
+ sFinishedCount++
+ sFailedCount--
+ }
+ }
+ val text: String
+ val needStyle: Boolean
+ if (sFinishedCount != 0 && sFailedCount == 0) {
+ if (sFinishedCount == 1) {
+ text = if (sItemTitleArray.size() >= 1) {
+ getString(
+ R.string.stat_download_done_line_succeeded,
+ sItemTitleArray.valueAt(0)
+ )
+ } else {
+ Log.d("TAG", "WTF, sItemTitleArray is null")
+ getString(R.string.error_unknown)
+ }
+ needStyle = false
+ } else {
+ text = getString(R.string.stat_download_done_text_succeeded, sFinishedCount)
+ needStyle = true
+ }
+ } else if (sFinishedCount == 0 && sFailedCount != 0) {
+ if (sFailedCount == 1) {
+ text = if (sItemTitleArray.size() >= 1) {
+ getString(
+ R.string.stat_download_done_line_failed,
+ sItemTitleArray.valueAt(0)
+ )
+ } else {
+ Log.d("TAG", "WTF, sItemTitleArray is null")
+ getString(R.string.error_unknown)
+ }
+ needStyle = false
+ } else {
+ text = getString(R.string.stat_download_done_text_failed, sFailedCount)
+ needStyle = true
+ }
+ } else {
+ text = getString(R.string.stat_download_done_text_mix, sFinishedCount, sFailedCount)
+ needStyle = true
+ }
+ val style: NotificationCompat.InboxStyle?
+ if (needStyle) {
+ style = NotificationCompat.InboxStyle()
+ style.setBigContentTitle(getString(R.string.stat_download_done_title))
+ val stateArray = sItemStateArray
+ var i = 0
+ val n = stateArray.size()
+ while (i < n) {
+ val id = stateArray.keyAt(i)
+ val fin = stateArray.valueAt(i)
+ val title = sItemTitleArray[id]
+ if (title == null) {
+ i++
+ continue
+ }
+ style.addLine(
+ getString(
+ if (fin) R.string.stat_download_done_line_succeeded else R.string.stat_download_done_line_failed,
+ title
+ )
+ )
+ i++
+ }
+ } else {
+ style = null
+ }
+ mDownloadedBuilder!!.setContentText(text)
+ .setStyle(style)
+ .setWhen(System.currentTimeMillis())
+ .setNumber(sDownloadedCount)
+ mDownloadedDelay!!.show()
+ checkStopSelf()
+ }
+
+ override fun onCancel(info: DownloadInfo) {
+ if (mNotifyManager == null) {
+ return
+ }
+ if (null != mDownloadingDelay) {
+ mDownloadingDelay!!.cancel()
+ }
+ checkStopSelf()
+ }
+
+ private fun checkStopSelf() {
+ if (mDownloadManager == null || mDownloadManager!!.isIdle) {
+ stopForeground(true)
+ stopSelf()
+ }
+ }
+
+ private class NotificationDelay(
+ private var mService: Service,
+ private val mNotifyManager: NotificationManagerCompat,
+ private val mBuilder: NotificationCompat.Builder,
+ private val mId: Int
+ ) : Runnable {
+ private var mLastTime: Long = 0
+ private var mPosted = false
+
+ // false for show, true for cancel
+ @Ops
+ private var mOps = 0
+
+ fun show() {
+ if (mPosted) {
+ mOps = OPS_NOTIFY
+ } else {
+ val now = SystemClock.currentThreadTimeMillis()
+ if (now - mLastTime > DELAY) {
+ // Wait long enough, do it now
+ if (ActivityCompat.checkSelfPermission(
+ mService,
+ Manifest.permission.POST_NOTIFICATIONS
+ ) != PackageManager.PERMISSION_GRANTED
+ ) {
+ return
+ }
+ mNotifyManager.notify(mId, mBuilder.build())
+ } else {
+ // Too quick, post delay
+ mOps = OPS_NOTIFY
+ mPosted = true
+ SimpleHandler.getInstance().postDelayed(this, DELAY)
+ }
+ mLastTime = now
+ }
+ }
+
+ fun cancel() {
+ if (mPosted) {
+ mOps = OPS_CANCEL
+ } else {
+ val now = SystemClock.currentThreadTimeMillis()
+ if (now - mLastTime > DELAY) {
+ // Wait long enough, do it now
+ mNotifyManager.cancel(mId)
+ } else {
+ // Too quick, post delay
+ mOps = OPS_CANCEL
+ mPosted = true
+ SimpleHandler.getInstance().postDelayed(this, DELAY)
+ }
+ }
+ }
+
+ fun startForeground() {
+ if (mPosted) {
+ mOps = OPS_START_FOREGROUND
+ } else {
+ val now = SystemClock.currentThreadTimeMillis()
+ if (now - mLastTime > DELAY) {
+ // Wait long enough, do it now
+ mService.startForeground(mId, mBuilder.build())
+ } else {
+ // Too quick, post delay
+ mOps = OPS_START_FOREGROUND
+ mPosted = true
+ SimpleHandler.getInstance().postDelayed(this, DELAY)
+ }
+ }
+ }
+
+ override fun run() {
+ mPosted = false
+ when (mOps) {
+ OPS_NOTIFY -> {
+ if (ActivityCompat.checkSelfPermission(
+ mService,
+ Manifest.permission.POST_NOTIFICATIONS
+ ) != PackageManager.PERMISSION_GRANTED
+ ) {
+ return
+ }
+ mNotifyManager.notify(mId, mBuilder.build())
+ }
+ OPS_CANCEL -> mNotifyManager.cancel(mId)
+ OPS_START_FOREGROUND -> mService.startForeground(mId, mBuilder.build())
+ }
+ }
+
+ @IntDef(OPS_NOTIFY, OPS_CANCEL, OPS_START_FOREGROUND)
+ @Retention(AnnotationRetention.SOURCE)
+ private annotation class Ops
+ companion object {
+ private const val OPS_NOTIFY = 0
+ private const val OPS_CANCEL = 1
+ private const val OPS_START_FOREGROUND = 2
+ private const val DELAY: Long = 1000 // 1s
+ }
+ }
+
+ companion object {
+ const val ACTION_START = "start"
+ const val ACTION_START_RANGE = "start_range"
+ const val ACTION_START_ALL = "start_all"
+ const val ACTION_STOP = "stop"
+ const val ACTION_STOP_RANGE = "stop_range"
+ const val ACTION_STOP_CURRENT = "stop_current"
+ const val ACTION_STOP_ALL = "stop_all"
+ const val ACTION_DELETE = "delete"
+ const val ACTION_DELETE_RANGE = "delete_range"
+ const val ACTION_CLEAR = "clear"
+ const val KEY_GALLERY_INFO = "gallery_info"
+ const val KEY_LABEL = "label"
+ const val KEY_GID = "gid"
+ const val KEY_GID_LIST = "gid_list"
+ private const val ID_DOWNLOADING = 1
+ private const val ID_DOWNLOADED = 2
+ private const val ID_509 = 3
+ private val sItemStateArray = SparseJBArray()
+ private val sItemTitleArray = SparseJLArray()
+ private var sFailedCount = 0
+ private var sFinishedCount = 0
+ private var sDownloadedCount = 0
+
+ @JvmStatic
+ fun clear() {
+ sFailedCount = 0
+ sFinishedCount = 0
+ sDownloadedCount = 0
+ sItemStateArray.clear()
+ sItemTitleArray.clear()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.java b/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.java
index fa42be226..84e3bfa44 100644
--- a/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.java
+++ b/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.java
@@ -18,6 +18,7 @@ package com.hippo.ehviewer.ui;
import android.app.Activity;
import android.content.Intent;
+import android.os.Build;
import androidx.core.content.ContextCompat;
@@ -42,6 +43,7 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
public final class CommonOperations {
@@ -110,8 +112,14 @@ public final class CommonOperations {
startDownload(activity, Collections.singletonList(galleryInfo), forceDefault);
}
- // TODO Add context if activity and context are different style
public static void startDownload(final MainActivity activity, final List galleryInfos, boolean forceDefault) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Objects.requireNonNull(EhApplication.getInstance().getTopActivity()).checkAndRequestNotificationPermission();
+ }
+ doStartDownload(activity, galleryInfos, forceDefault);
+ }
+
+ private static void doStartDownload(final MainActivity activity, final List galleryInfos, boolean forceDefault) {
final DownloadManager dm = EhApplication.getDownloadManager(activity);
LongList toStart = new LongList();
diff --git a/app/src/main/java/com/hippo/ehviewer/ui/EhActivity.kt b/app/src/main/java/com/hippo/ehviewer/ui/EhActivity.kt
index 7db1df032..a15b3341c 100644
--- a/app/src/main/java/com/hippo/ehviewer/ui/EhActivity.kt
+++ b/app/src/main/java/com/hippo/ehviewer/ui/EhActivity.kt
@@ -13,126 +13,139 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.hippo.ehviewer.ui
-package com.hippo.ehviewer.ui;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.os.Build;
-import android.os.Bundle;
-import android.view.Window;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.StyleRes;
-import androidx.appcompat.app.AppCompatActivity;
-
-import com.hippo.ehviewer.EhApplication;
-import com.hippo.ehviewer.R;
-import com.hippo.ehviewer.Settings;
-
-import rikka.core.res.ResourcesKt;
-import rikka.insets.WindowInsetsHelper;
-import rikka.layoutinflater.view.LayoutInflaterFactory;
-
-public abstract class EhActivity extends AppCompatActivity {
-
- private static final String THEME_DEFAULT = "DEFAULT";
- private static final String THEME_BLACK = "BLACK";
-
- public static boolean isBlackNightTheme() {
- return Settings.getBoolean("black_dark_theme", false);
- }
-
- public static String getTheme(Context context) {
- if (isBlackNightTheme() && isNightMode(context.getResources().getConfiguration()))
- return THEME_BLACK;
- return THEME_DEFAULT;
- }
-
- public static boolean isNightMode(Configuration configuration) {
- return (configuration.uiMode & Configuration.UI_MODE_NIGHT_YES) > 0;
- }
+import android.Manifest
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.content.res.Resources.Theme
+import android.graphics.Color
+import android.os.Build
+import android.os.Bundle
+import android.view.WindowManager
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.annotation.RequiresApi
+import androidx.annotation.StyleRes
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
+import com.hippo.ehviewer.EhApplication
+import com.hippo.ehviewer.R
+import com.hippo.ehviewer.Settings
+import com.hippo.ehviewer.ui.SecurityActivity.Companion.isAuthenticationSupported
+import rikka.core.res.resolveColor
+import rikka.insets.WindowInsetsHelper
+import rikka.layoutinflater.view.LayoutInflaterFactory
+abstract class EhActivity : AppCompatActivity() {
@StyleRes
- public int getThemeStyleRes(Context context) {
- if (THEME_BLACK.equals(getTheme(context))) {
- return R.style.ThemeOverlay_Black;
- }
- return R.style.ThemeOverlay;
+ fun getThemeStyleRes(context: Context): Int {
+ return if (THEME_BLACK == getTheme(context)) {
+ R.style.ThemeOverlay_Black
+ } else R.style.ThemeOverlay
}
- @Override
- protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
- if (getParent() == null) {
- theme.applyStyle(resid, true);
+ override fun onApplyThemeResource(theme: Theme, resid: Int, first: Boolean) {
+ if (parent == null) {
+ theme.applyStyle(resid, true)
} else {
try {
- theme.setTo(getParent().getTheme());
- } catch (Exception ignored) {
+ theme.setTo(parent.theme)
+ } catch (ignored: Exception) {
}
- theme.applyStyle(resid, false);
+ theme.applyStyle(resid, false)
}
- theme.applyStyle(getThemeStyleRes(this), true);
- super.onApplyThemeResource(theme, R.style.ThemeOverlay, first);
+ theme.applyStyle(getThemeStyleRes(this), true)
+ super.onApplyThemeResource(theme, R.style.ThemeOverlay, first)
}
- @Override
- protected void onNightModeChanged(int mode) {
- getTheme().applyStyle(getThemeStyleRes(this), true);
+ override fun onNightModeChanged(mode: Int) {
+ theme.applyStyle(getThemeStyleRes(this), true)
}
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- getLayoutInflater().setFactory2((new LayoutInflaterFactory(getDelegate())).addOnViewCreatedListener(WindowInsetsHelper.getLISTENER()));
- super.onCreate(savedInstanceState);
-
-
- Window window = getWindow();
- window.setStatusBarColor(Color.TRANSPARENT);
-
- window.getDecorView().post(() -> {
- WindowInsets rootWindowInsets = window.getDecorView().getRootWindowInsets();
- if (rootWindowInsets != null && rootWindowInsets.getSystemWindowInsetBottom() >= Resources.getSystem().getDisplayMetrics().density * 40) {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ layoutInflater.factory2 =
+ LayoutInflaterFactory(delegate).addOnViewCreatedListener(
+ WindowInsetsHelper.LISTENER
+ )
+ super.onCreate(savedInstanceState)
+ val window = window
+ window.statusBarColor = Color.TRANSPARENT
+ window.decorView.post {
+ val rootWindowInsets = window.decorView.rootWindowInsets
+ if (rootWindowInsets != null && rootWindowInsets.systemWindowInsetBottom >= Resources.getSystem().displayMetrics.density * 40) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- window.setNavigationBarDividerColor(getColor(R.color.navigation_bar_divider));
+ window.navigationBarDividerColor = getColor(R.color.navigation_bar_divider)
}
- window.setNavigationBarColor(ResourcesKt.resolveColor(getTheme(), android.R.attr.navigationBarColor) & 0x00ffffff | -0x20000000);
+ window.navigationBarColor =
+ theme.resolveColor(android.R.attr.navigationBarColor) and 0x00ffffff or -0x20000000
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- window.setNavigationBarContrastEnforced(false);
+ window.isNavigationBarContrastEnforced = false
}
} else {
- window.setNavigationBarColor(Color.TRANSPARENT);
+ window.navigationBarColor = Color.TRANSPARENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- window.setNavigationBarContrastEnforced(true);
+ window.isNavigationBarContrastEnforced = true
}
}
- });
-
- ((EhApplication) getApplication()).registerActivity(this);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- ((EhApplication) getApplication()).unregisterActivity(this);
- }
-
- @Override
- protected void onResume() {
- if (Settings.getSecurity() && SecurityActivity.Companion.isAuthenticationSupported(this) && EhApplication.locked) {
- startActivity(new Intent(this, SecurityActivity.class));
}
- super.onResume();
+ (application as EhApplication).registerActivity(this)
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ (application as EhApplication).unregisterActivity(this)
+ }
+
+ override fun onResume() {
+ if (Settings.getSecurity() && isAuthenticationSupported(this) && EhApplication.locked) {
+ startActivity(Intent(this, SecurityActivity::class.java))
+ }
+ super.onResume()
if (Settings.getEnabledSecurity()) {
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
- WindowManager.LayoutParams.FLAG_SECURE);
+ window.setFlags(
+ WindowManager.LayoutParams.FLAG_SECURE,
+ WindowManager.LayoutParams.FLAG_SECURE
+ )
} else {
- getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
}
}
-}
+
+ private val requestPermissionLauncher = registerForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) {
+ Settings.putNotificationRequired()
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
+ fun checkAndRequestNotificationPermission(){
+ if (ContextCompat.checkSelfPermission(
+ this,
+ Manifest.permission.POST_NOTIFICATIONS
+ ) == PackageManager.PERMISSION_GRANTED
+ ) {
+ return
+ }
+ if (Settings.getNotificationRequired())
+ return
+ requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
+ }
+
+ companion object {
+ private const val THEME_DEFAULT = "DEFAULT"
+ private const val THEME_BLACK = "BLACK"
+ private val isBlackNightTheme: Boolean
+ get() = Settings.getBoolean("black_dark_theme", false)
+
+ fun getTheme(context: Context): String {
+ return if (isBlackNightTheme && isNightMode(context.resources.configuration)) THEME_BLACK else THEME_DEFAULT
+ }
+
+ private fun isNightMode(configuration: Configuration): Boolean {
+ return configuration.uiMode and Configuration.UI_MODE_NIGHT_YES > 0
+ }
+ }
+}
\ No newline at end of file