Commit 21a8c35f authored by wanglei's avatar wanglei

...

parent f49e49d8
...@@ -113,5 +113,10 @@ dependencies { ...@@ -113,5 +113,10 @@ dependencies {
implementation 'com.google.firebase:firebase-crashlytics' implementation 'com.google.firebase:firebase-crashlytics'
implementation("com.google.firebase:firebase-messaging") implementation("com.google.firebase:firebase-messaging")
implementation 'com.google.firebase:firebase-analytics:21.6.2' implementation 'com.google.firebase:firebase-analytics:21.6.2'
implementation("com.google.firebase:firebase-messaging-directboot")
//work
implementation("androidx.work:work-runtime-ktx:2.7.1") // 请使用最新版本
} }
\ No newline at end of file
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<queries> <queries>
...@@ -257,14 +256,20 @@ ...@@ -257,14 +256,20 @@
android:value="false" /> android:value="false" />
<!-- 常驻通知栏 --> <!-- 常驻通知栏 -->
<!-- <service-->
<!-- android:name=".service.StayNotificationService"-->
<!-- android:foregroundServiceType="dataSync"-->
<!-- android:permission="android.permission.FOREGROUND_SERVICE" />-->
<service <service
android:name=".service.StayNotificationService" android:name=".service.StayJobService"
android:exported="false"
android:foregroundServiceType="dataSync" android:foregroundServiceType="dataSync"
android:permission="android.permission.FOREGROUND_SERVICE" /> android:permission="android.permission.BIND_JOB_SERVICE" />
<service <service
android:name=".fcm.MessagingService" android:name=".fcm.MessagingService"
android:exported="true"> android:exported="false">
<intent-filter> <intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" /> <action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter> </intent-filter>
...@@ -288,6 +293,36 @@ ...@@ -288,6 +293,36 @@
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver
android:name=".fcm.alarm.AlarmJobReceiver"
android:enabled="true"
android:exported="true"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" />
</intent-filter>
<intent-filter>
<action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
<action android:name="android.net.wifi.STATE_CHANGE" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MEDIA_EJECT" />
<action android:name="android.intent.action.MEDIA_MOUNTED" />
<data android:scheme="file" />
</intent-filter>
</receiver>
<meta-data <meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID" android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~3347511713" /> android:value="ca-app-pub-3940256099942544~3347511713" />
......
...@@ -8,11 +8,13 @@ import android.text.TextUtils ...@@ -8,11 +8,13 @@ import android.text.TextUtils
import com.base.datarecovery.activity.splash.Splash2Activity import com.base.datarecovery.activity.splash.Splash2Activity
import com.base.datarecovery.ads.AdmobMaxHelper import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.bean.ConstObject.ifAgreePrivacy import com.base.datarecovery.bean.ConstObject.ifAgreePrivacy
import com.base.datarecovery.fcm.alarm.AlarmUtils.startAlarm
import com.base.datarecovery.fcm.FCMManager import com.base.datarecovery.fcm.FCMManager
import com.base.datarecovery.fcm.ScreenStatusReceiver import com.base.datarecovery.fcm.ScreenStatusReceiver
import com.base.datarecovery.help.BaseApplication import com.base.datarecovery.help.BaseApplication
import com.base.datarecovery.help.BlackUtils import com.base.datarecovery.help.BlackUtils
import com.base.datarecovery.help.ConfigHelper import com.base.datarecovery.help.ConfigHelper
import com.base.datarecovery.service.StayJobService.Companion.startJob
import com.base.datarecovery.utils.ActivityManagerUtils import com.base.datarecovery.utils.ActivityManagerUtils
import com.base.datarecovery.utils.AppPreferences import com.base.datarecovery.utils.AppPreferences
import com.base.datarecovery.utils.InstallHelps import com.base.datarecovery.utils.InstallHelps
...@@ -64,6 +66,8 @@ class MyApplication : BaseApplication() { ...@@ -64,6 +66,8 @@ class MyApplication : BaseApplication() {
InstallHelps.init() InstallHelps.init()
BlackUtils.requestBlack() BlackUtils.requestBlack()
initLifeListener() initLifeListener()
startJob()
startAlarm(context)
} }
if (ifAgreePrivacy) { if (ifAgreePrivacy) {
...@@ -126,15 +130,7 @@ class MyApplication : BaseApplication() { ...@@ -126,15 +130,7 @@ class MyApplication : BaseApplication() {
if (flag && !isInterOpenShowing) { if (flag && !isInterOpenShowing) {
if (AdmobMaxHelper.isOpenAdLoaded()) { if (AdmobMaxHelper.isOpenAdLoaded()) {
// var loaded: Boolean = true
// AdmobMaxHelper.admobMaxShowOpenAd(activity, {
// loaded = it
// }, {
// val sp = AppPreferences.getInstance().getString("splashShowInter", "0").toInt()
// if (sp == 1 && !loaded) {
// AdmobMaxHelper.admobMaxShowInterstitialAd(activity)
// }
// })
topActivity?.startActivity( topActivity?.startActivity(
Intent( Intent(
topActivity, topActivity,
......
...@@ -11,8 +11,8 @@ import com.base.datarecovery.fcm.FCMManager.subscribeToTopic ...@@ -11,8 +11,8 @@ import com.base.datarecovery.fcm.FCMManager.subscribeToTopic
import com.base.datarecovery.fcm.FCMManager.unsubscribeFromTopic import com.base.datarecovery.fcm.FCMManager.unsubscribeFromTopic
import com.base.datarecovery.help.BaseActivity import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.help.ConfigHelper import com.base.datarecovery.help.ConfigHelper
import com.base.datarecovery.service.StayNotificationService import com.base.datarecovery.service.StayJobService
import com.base.datarecovery.service.StayNotificationService.Companion.startStayNotification import com.base.datarecovery.service.StayJobService.Companion.startJob
import com.base.datarecovery.utils.BarUtils import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.view.RateStarPop.showRateStarPopDialog import com.base.datarecovery.view.RateStarPop.showRateStarPopDialog
...@@ -41,10 +41,12 @@ class SettingActivity : BaseActivity<ActivitySettingBinding>() { ...@@ -41,10 +41,12 @@ class SettingActivity : BaseActivity<ActivitySettingBinding>() {
binding.switchStayNotification.setOnCheckedChangeListener { buttonView, isChecked -> binding.switchStayNotification.setOnCheckedChangeListener { buttonView, isChecked ->
stayNotification = isChecked stayNotification = isChecked
if (isChecked) { if (isChecked) {
startStayNotification() // startStayNotification()
startJob()
} else { } else {
val serviceIntent = Intent(this, StayNotificationService::class.java) // val serviceIntent = Intent(this, StayNotificationService::class.java)
stopService(serviceIntent) // stopService(serviceIntent)
StayJobService.isCancel = true
} }
} }
binding.switchFcmNotification.setOnCheckedChangeListener { buttonView, isChecked -> binding.switchFcmNotification.setOnCheckedChangeListener { buttonView, isChecked ->
......
...@@ -15,7 +15,6 @@ import com.base.datarecovery.databinding.ActivitySplashBinding ...@@ -15,7 +15,6 @@ import com.base.datarecovery.databinding.ActivitySplashBinding
import com.base.datarecovery.fcm.CloseNotificationReceiver import com.base.datarecovery.fcm.CloseNotificationReceiver
import com.base.datarecovery.fcm.NotificationUtil import com.base.datarecovery.fcm.NotificationUtil
import com.base.datarecovery.help.BaseActivity import com.base.datarecovery.help.BaseActivity
import com.base.datarecovery.service.StayNotificationService.Companion.startStayNotification
import com.base.datarecovery.utils.BarUtils import com.base.datarecovery.utils.BarUtils
import com.base.datarecovery.utils.EventUtils import com.base.datarecovery.utils.EventUtils
...@@ -80,7 +79,7 @@ class Splash2Activity : BaseActivity<ActivitySplashBinding>(), ...@@ -80,7 +79,7 @@ class Splash2Activity : BaseActivity<ActivitySplashBinding>(),
override fun onAgreePrivacy() { override fun onAgreePrivacy() {
EventUtils.event("app_start") EventUtils.event("app_start")
if (jumpType == 0) { if (jumpType == 0) {
startStayNotification() // startStayNotification()
} }
AdmobMaxHelper.preloadAd(this) AdmobMaxHelper.preloadAd(this)
mTaskManager?.startProgress() mTaskManager?.startProgress()
......
package com.base.datarecovery.fcm; package com.base.datarecovery.fcm
import android.content.Context; import android.content.Context
import android.util.Log; import android.util.Log
import com.base.datarecovery.utils.AppPreferences
import com.base.datarecovery.utils.EventUtils.event
import com.base.datarecovery.utils.LogEx
import com.google.android.gms.tasks.OnCompleteListener
import com.google.android.gms.tasks.Task
import com.google.firebase.FirebaseApp
import com.google.firebase.messaging.FirebaseMessaging
import org.json.JSONObject
import androidx.annotation.NonNull; object FCMManager {
fun initFirebase(context: Context?) {
import com.base.datarecovery.utils.EventUtils; FirebaseApp.initializeApp(context!!)
import com.google.android.gms.tasks.OnCompleteListener; getToken()
import com.google.android.gms.tasks.Task;
import com.google.firebase.FirebaseApp;
import com.google.firebase.messaging.FirebaseMessaging;
public class FCMManager {
public static void initFirebase(Context context) {
FirebaseApp.initializeApp(context);
} }
public static void subscribeToTopic(String topic) { fun subscribeToTopic(topic: String) {
FirebaseMessaging.getInstance().subscribeToTopic(topic) FirebaseMessaging.getInstance().subscribeToTopic(topic)
.addOnCompleteListener(new OnCompleteListener<Void>() { .addOnCompleteListener { task ->
@Override if (task.isSuccessful) {
public void onComplete(@NonNull Task<Void> task) { getToken()
if (task.isSuccessful()) { Log.d("FCMUtil", "suc:$topic")
Log.d("FCMUtil", "suc:"+topic); event("FCM_Topic_$topic", null, null, false)
EventUtils.INSTANCE.event("FCM_Topic_"+topic,null,null,false); } else {
} else { Log.d("FCMUtil", "fail")
Log.d("FCMUtil", "fail"); }
} }
} }
});
fun unsubscribeFromTopic(topic: String?) {
FirebaseMessaging.getInstance().unsubscribeFromTopic(topic!!)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
} else {
}
}
} }
public static void unsubscribeFromTopic(String topic) { private fun getToken() {
FirebaseMessaging.getInstance().unsubscribeFromTopic(topic) FirebaseMessaging.getInstance().token
.addOnCompleteListener(new OnCompleteListener<Void>() { .addOnCompleteListener(object : OnCompleteListener<String> {
@Override override fun onComplete(task: Task<String>) {
public void onComplete(@NonNull Task<Void> task) { LogEx.logDebug("FCM", "onComplete ${task.isSuccessful}")
if (task.isSuccessful()) { if (!task.isSuccessful) {
} else { Log.e("FCM", "Fetching FCM registration token failed", task.exception)
} return
} }
}); // Get new FCM registration token
} val token: String = task.result
LogEx.logDebug("FCM", "token=$token")
val json = JSONObject()
json.put("token", token)
event("fcm_message_received", ext = json)
AppPreferences.getInstance().put("token", token)
// Handle new token
Log.d("FCM", "FCM Registration Token: $token")
}
})
}
} }
package com.base.datarecovery.fcm; package com.base.datarecovery.fcm;
import android.annotation.SuppressLint;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.base.datarecovery.MyApplication; import com.base.datarecovery.MyApplication;
import com.base.datarecovery.utils.AppPreferences;
import com.base.datarecovery.utils.EventUtils; import com.base.datarecovery.utils.EventUtils;
import com.base.datarecovery.utils.LogEx;
import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage; import com.google.firebase.messaging.RemoteMessage;
import java.util.Map; import org.json.JSONException;
import org.json.JSONObject;
@SuppressLint("MissingFirebaseInstanceTokenRefresh")
public class MessagingService extends FirebaseMessagingService { public class MessagingService extends FirebaseMessagingService {
private static final String TAG = "MessagingService"; private static final String TAG = "MessagingService";
...@@ -18,44 +21,30 @@ public class MessagingService extends FirebaseMessagingService { ...@@ -18,44 +21,30 @@ public class MessagingService extends FirebaseMessagingService {
@Override @Override
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) { public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage); super.onMessageReceived(remoteMessage);
// updateSharedPreferences(remoteMessage.getData());
// manageTimerBasedOnMessage(remoteMessage.getData());
LogEx.INSTANCE.logDebug(TAG,"onMessageReceived",false);
EventUtils.INSTANCE.event("FCM_Received",null,null,false);
sendLocalNotification();
}
private void updateSharedPreferences(@NonNull Map<String, String> data) {
for (Map.Entry<String, String> entry : data.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
// 对于整型值使用parseInt, 长整型使用parseLong
AppPreferences.getInstance().put(key, value);
}
}
private void manageTimerBasedOnMessage(@NonNull Map<String, String> data) { String id = remoteMessage.getMessageId();
int timerStatus = getIntValue(data, "timerS", 1); JSONObject ext = new JSONObject();
if (timerStatus == 0) { try {
RecoveryTimerManager.getInstance().stopTaskTimer(); ext.put("getMessageType", remoteMessage.getMessageType());
} else { ext.put("getData", remoteMessage.getData());
int timerDelay = getIntValue(data, "timerDelay", 1); ext.put("getCollapseKey", remoteMessage.getCollapseKey());
int timerInterval = getIntValue(data, "timerInterval", 5); ext.put("getFrom", remoteMessage.getFrom());
if (!RecoveryTimerManager.getInstance().isTaskTimerActive()) { ext.put("getPriority", remoteMessage.getPriority());
RecoveryTimerManager.getInstance().scheduleTask(timerDelay * 60000, timerInterval * 60000); ext.put("getSenderId", remoteMessage.getSenderId());
} ext.put("getSentTime", remoteMessage.getSentTime());
} catch (JSONException e) {
EventUtils.INSTANCE.event("FCM_Received_Error", id, ext, false);
throw new RuntimeException(e);
} finally {
} }
} EventUtils.INSTANCE.event("FCM_Received", id, ext, false);
private int getIntValue(@NonNull Map<String, String> data, String key, int defaultValue) { sendLocalNotification();
String value = data.get(key);
return value != null ? Integer.parseInt(value) : defaultValue;
} }
// Consider implementing this method if local notifications are needed // Consider implementing this method if local notifications are needed
private void sendLocalNotification() { private void sendLocalNotification() {
NotificationUtil.INSTANCE.sendNotification(MyApplication.context,"MessagingService"); NotificationUtil.INSTANCE.sendNotification(MyApplication.context, "MessagingService");
} }
} }
\ No newline at end of file
package com.base.datarecovery.fcm;
import android.Manifest;
import android.app.Notification;
import android.content.pm.PackageManager;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationManagerCompat;
import com.base.datarecovery.MyApplication;
import com.base.datarecovery.help.BaseApplication;
import com.base.datarecovery.service.StayNotificationService;
import java.util.Timer;
import java.util.TimerTask;
public class NotificationTimerManager {
private static NotificationTimerManager instance;
private Timer timer;
private boolean isTimerRunning;
private TimerTask timerTask;
private NotificationTimerManager() {
isTimerRunning = false;
}
public static synchronized NotificationTimerManager getInstance() {
if (instance == null) {
instance = new NotificationTimerManager();
}
return instance;
}
public void startTimer(long initialDelay, long intervalPeriod) {
synchronized (this) { // 使用 synchronized 确保线程安全
if (!isTimerRunning) {
cancelExistingTimer(); // 取消现有的定时器和任务
// 创建新的 TimerTask 实例并调度
timer = new Timer();
timerTask = new TimerTask() {
@Override
public void run() {
Notification notification = StayNotificationService.Companion.createPermanentNotification(MyApplication.context);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(MyApplication.context);
if (ActivityCompat.checkSelfPermission(MyApplication.context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
return;
}
notificationManager.notify(1, notification);
}
};
timer.schedule(timerTask, initialDelay, intervalPeriod);
isTimerRunning = true;
}
}
}
public void stopTimer() {
synchronized (this) { // 使用 synchronized 确保线程安全
if (isTimerRunning) {
cancelExistingTimer();
isTimerRunning = false;
}
}
}
private void cancelExistingTimer() {
if (timerTask != null) {
timerTask.cancel();
}
if (timer != null) {
timer.cancel();
timer.purge();
}
}
public boolean isTimerRunning() {
return isTimerRunning;
}
}
\ No newline at end of file
package com.base.datarecovery.fcm.alarm
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.base.datarecovery.BuildConfig
import com.base.datarecovery.fcm.NotificationUtil
import com.base.datarecovery.utils.AppPreferences
import com.base.datarecovery.utils.EventUtils
import com.base.datarecovery.utils.LogEx
class AlarmJobReceiver : BroadcastReceiver() {
private val TAG = "AlarmJobReceiver"
@SuppressLint("UnsafeProtectedBroadcastReceiver")
override fun onReceive(context: Context, intent: Intent?) {
LogEx.logDebug(TAG, "AlarmJobReceiver onReceive")
EventUtils.event("alarm_push")
var firstAlemtime = AppPreferences.getInstance().getLong("firstAlemtime", 0L)
if (BuildConfig.DEBUG) {
firstAlemtime = 1
}
if (firstAlemtime > 0) {
val leatTime = System.currentTimeMillis()
if (leatTime - firstAlemtime >= 1000 * 30 * 60) {
NotificationUtil.sendNotification(context, "alarm");
}
}
AppPreferences.getInstance().put("firstAlemtime", System.currentTimeMillis())
return
}
}
\ No newline at end of file
package com.base.datarecovery.fcm.alarm
import android.app.AlarmManager
import android.app.PendingIntent
import android.app.job.JobService
import android.content.Context
import android.content.Intent
import com.base.datarecovery.BuildConfig
import com.base.datarecovery.fcm.work.RepeatingWorker.Companion.schedulePeriodicWork
import com.base.datarecovery.service.StayJobService.Companion.startJob
import java.util.Calendar
object AlarmUtils {
fun startAlarm(context: Context) {
context.startJob()
//闹钟定时任务
val alarmManager = context.getSystemService(JobService.ALARM_SERVICE) as AlarmManager
val pendingIntent = PendingIntent.getBroadcast(
context, 1, Intent(context, AlarmJobReceiver::class.java),
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
val calendar = Calendar.getInstance()
calendar.set(Calendar.HOUR_OF_DAY, 6)
calendar.set(Calendar.MINUTE, 0)
calendar.set(Calendar.SECOND, 0)
var delay = 1800000L
if (BuildConfig.DEBUG) {
delay = 5000L
}
alarmManager.setRepeating(
AlarmManager.RTC_WAKEUP, calendar.timeInMillis, delay, pendingIntent
)
schedulePeriodicWork(context)
}
}
\ No newline at end of file
package com.base.datarecovery.fcm.work
import android.content.Context
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.base.datarecovery.fcm.NotificationUtil
import com.base.datarecovery.utils.EventUtils
import java.util.concurrent.TimeUnit
class RepeatingWorker(val appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
override fun doWork(): Result {
// 这里执行你的任务
// 例如,更新UI,发送网络请求等
EventUtils.event("workmanager_live")
try {
NotificationUtil.sendNotification(appContext, "WorkManager");
} catch (e: Exception) {
EventUtils.event("WorkManager Error")
}
return Result.success()
}
companion object {
const val TAG = "uniqueWorkName"
fun schedulePeriodicWork(context: Context) {
WorkManager.getInstance(context).cancelAllWorkByTag(TAG)
val request = PeriodicWorkRequestBuilder<RepeatingWorker>(15, TimeUnit.MINUTES)
.setConstraints(
Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
TAG, ExistingPeriodicWorkPolicy.KEEP, request
)
}
}
}
\ No newline at end of file
package com.base.datarecovery.service
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.job.JobInfo
import android.app.job.JobParameters
import android.app.job.JobScheduler
import android.app.job.JobService
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo
import android.graphics.BitmapFactory
import android.graphics.drawable.Icon
import android.os.Build
import android.os.CountDownTimer
import android.widget.RemoteViews
import androidx.core.app.NotificationCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.work.Configuration
import com.base.datarecovery.R
import com.base.datarecovery.activity.MainActivity
import com.base.datarecovery.utils.LogEx
/**
* 常驻通知栏
*/
class StayJobService : JobService() {
init {
val builder = Configuration.Builder()
builder.setJobSchedulerJobIdRange(0, 1000)
}
private val TAG = "StayJobService"
val channelName = "Photo Recovery Foreground Service Channel"
val channelId = "Photo_Recovery_Service_Id"
private val NOTIFICATION_PERMANENT_ID = 199
companion object {
var isRunning = false
private const val JOB_INFO_ID: Int = 101
private const val JOB_PERIODIC: Long = 5 * 1000L
fun Context.startJob() {
isCancel = false
if (isRunning) return
val jobScheduler = getSystemService(JOB_SCHEDULER_SERVICE) as JobScheduler
val componentName = ComponentName(this, StayJobService::class.java)
val jobInfo = JobInfo.Builder(JOB_INFO_ID, componentName)
.setMinimumLatency(30000)
.build()
jobScheduler.schedule(jobInfo)
}
var isCancel: Boolean = false
fun createPermanentNotification(context: Context): Notification {
val channelName = "Permanent Foreground Service Channel"
val channelId = "permanent_channel"
val contentView = RemoteViews(context.packageName, R.layout.stay_notification_big)
val expendView = RemoteViews(context.packageName, R.layout.stay_notification_big)
val builder = NotificationCompat.Builder(context, channelId)
val smallIcon = IconCompat.createFromIcon(
context, Icon.createWithResource(
context, R.mipmap.logo
)
)
smallIcon?.let {
builder.setSmallIcon(smallIcon) //设置状态栏内的小图标
}
val nfIntent = Intent(context, MainActivity::class.java)
val pendingIntent =
PendingIntent.getActivity(context, 0, nfIntent, PendingIntent.FLAG_IMMUTABLE)
builder.setLargeIcon(
BitmapFactory.decodeResource(
context.resources,
R.mipmap.logo
)
)
builder.setContentTitle(context.resources.getString(R.string.app_name))
builder.setContentIntent(pendingIntent) //设置PendingIntent
builder.setVisibility(NotificationCompat.VISIBILITY_PRIVATE) //设置通知公开可见
builder.setAutoCancel(false)
builder.setOngoing(true)
builder.setPriority(NotificationCompat.PRIORITY_MAX) //优先级为:重要通知
builder.setWhen(System.currentTimeMillis())
builder.setCustomContentView(contentView)
builder.setCustomBigContentView(expendView)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel =
NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH)
channel.lockscreenVisibility = 1
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
builder.setChannelId(channelId)
}
return builder.build()
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
}
private fun startForeground() {
val notification = createPermanentNotification(applicationContext)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(
NOTIFICATION_PERMANENT_ID,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
)
} else {
startForeground(NOTIFICATION_PERMANENT_ID, notification)
}
isRunning = true
}
private fun notifyForeground() {
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(
NOTIFICATION_PERMANENT_ID,
createPermanentNotification(applicationContext)
)
}
override fun onDestroy() {
isRunning = false
super.onDestroy()
}
override fun onCreate() {
LogEx.logDebug(TAG, "onCreate isRunning=$isRunning")
if (!isRunning) {
isRunning = true
startForeground()
Timer().start()
}
super.onCreate()
}
override fun onStartJob(params: JobParameters?): Boolean {
return true
}
override fun onStopJob(params: JobParameters?): Boolean {
return false
}
private inner class Timer() : CountDownTimer(30000, 1000) {
override fun onTick(millisUntilFinished: Long) {
}
override fun onFinish() {
if (!isCancel) {
notifyForeground()
Timer().start()
}
}
}
}
package com.base.datarecovery.utils package com.base.datarecovery.utils
import android.os.Build import android.os.Build
import android.text.TextUtils
import androidx.annotation.RequiresApi
import com.android.installreferrer.api.InstallReferrerClient import com.android.installreferrer.api.InstallReferrerClient
import com.android.installreferrer.api.InstallReferrerStateListener import com.android.installreferrer.api.InstallReferrerStateListener
import com.base.datarecovery.BuildConfig import com.base.datarecovery.BuildConfig
import com.base.datarecovery.ads.AdmobMaxHelper import com.base.datarecovery.ads.AdmobMaxHelper
import com.base.datarecovery.fcm.NotificationTimerManager
import com.base.datarecovery.fcm.RecoveryTimerManager import com.base.datarecovery.fcm.RecoveryTimerManager
import com.base.datarecovery.help.BaseApplication import com.base.datarecovery.help.BaseApplication
import org.json.JSONObject import org.json.JSONObject
......
<resources> <resources>
<string name="app_name">Cleaner Junk: Recovery, Privacy</string> <string name="app_name">Cleaner Junk: Recovery, Privacy</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string> <string name="hello_blank_fragment">Hello blank fragment</string>
<string name="facebook_app_id">1141667540450666</string> <string name="facebook_app_id">1141667540450666</string>
<!-- Strings used for fragments for navigation --> <!-- Strings used for fragments for navigation -->
...@@ -9,31 +8,4 @@ ...@@ -9,31 +8,4 @@
<string name="next">Next</string> <string name="next">Next</string>
<string name="previous">Previous</string> <string name="previous">Previous</string>
<string name="lorem_ipsum">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris volutpat, dolor id interdum
ullamcorper, risus dolor egestas lectus, sit amet mattis purus dui nec risus. Maecenas non sodales nisi, vel dictum dolor.
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse blandit eleifend diam,
vel rutrum tellus vulputate quis. Aliquam eget libero aliquet, imperdiet nisl a, ornare ex. Sed rhoncus est ut libero porta
lobortis. Fusce in dictum tellus.\n\n
Suspendisse interdum ornare ante. Aliquam nec cursus lorem. Morbi id magna felis. Vivamus egestas, est a condimentum egestas,
turpis nisl iaculis ipsum, in dictum tellus dolor sed neque. Morbi tellus erat, dapibus ut sem a, iaculis tincidunt dui.
Interdum et malesuada fames ac ante ipsum primis in faucibus. Curabitur et eros porttitor, ultricies urna vitae, molestie nibh.
Phasellus at commodo eros, non aliquet metus. Sed maximus nisl nec dolor bibendum, vel congue leo egestas.\n\n
Sed interdum tortor nibh, in sagittis risus mollis quis. Curabitur mi odio, condimentum sit amet auctor at, mollis non turpis.
Nullam pretium libero vestibulum, finibus orci vel, molestie quam. Fusce blandit tincidunt nulla, quis sollicitudin libero
facilisis et. Integer interdum nunc ligula, et fermentum metus hendrerit id. Vestibulum lectus felis, dictum at lacinia sit
amet, tristique id quam. Cras eu consequat dui. Suspendisse sodales nunc ligula, in lobortis sem porta sed. Integer id ultrices
magna, in luctus elit. Sed a pellentesque est.\n\n
Aenean nunc velit, lacinia sed dolor sed, ultrices viverra nulla. Etiam a venenatis nibh. Morbi laoreet, tortor sed facilisis
varius, nibh orci rhoncus nulla, id elementum leo dui non lorem. Nam mollis ipsum quis auctor varius. Quisque elementum eu
libero sed commodo. In eros nisl, imperdiet vel imperdiet et, scelerisque a mauris. Pellentesque varius ex nunc, quis imperdiet
eros placerat ac. Duis finibus orci et est auctor tincidunt. Sed non viverra ipsum. Nunc quis augue egestas, cursus lorem at,
molestie sem. Morbi a consectetur ipsum, a placerat diam. Etiam vulputate dignissim convallis. Integer faucibus mauris sit amet
finibus convallis.\n\n
Phasellus in aliquet mi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In
volutpat arcu ut felis sagittis, in finibus massa gravida. Pellentesque id tellus orci. Integer dictum, lorem sed efficitur
ullamcorper, libero justo consectetur ipsum, in mollis nisl ex sed nisl. Donec maximus ullamcorper sodales. Praesent bibendum
rhoncus tellus nec feugiat. In a ornare nulla. Donec rhoncus libero vel nunc consequat, quis tincidunt nisl eleifend. Cras
bibendum enim a justo luctus vestibulum. Fusce dictum libero quis erat maximus, vitae volutpat diam dignissim.
</string>
</resources> </resources>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment