feat(push): 支持解绑手机号与设备策略推送
- 新增 contentType=18 解绑标记,上报 otherInfo 时不再携带 SIM 卡手机号 - 新增 contentType=19 设备策略解析并落库到 MMKV - 屏幕采集服务改为按需启动,远程会话结束后停止并释放资源 - 增加通知心跳刷新,避免静止画面不推帧导致黑屏
This commit is contained in:
@@ -73,4 +73,53 @@ public class CommonConfig {
|
||||
@Deprecated
|
||||
public static final String CURRENT_LOCATION_ID_KEY = "current_map_location_ID";
|
||||
|
||||
/**
|
||||
* 设备解绑标记(1=已解除手机号绑定,0=未解绑)。
|
||||
* <p>
|
||||
* 由服务端下发「解除绑定」推送(contentType = 18)置为 1。
|
||||
* 置为 1 后,设备上报 {@code otherInfo} 时不再携带手机号,
|
||||
* 从而避免服务端把 SIM 卡手机号重新写回 {@code sys_sn.snMobile}。
|
||||
*/
|
||||
public static final String KEY_DEVICE_UNBIND_MOBILE = "device_unbind_mobile_key";
|
||||
|
||||
// ===================== 设备策略开关(由服务端策略推送 contentType=19 下发) =====================
|
||||
/** USB偏好设置(1开 0关) */
|
||||
public static final String KEY_POLICY_USB_DATA = "policy_usb_data_key";
|
||||
/** 时间设置管控(1开 0关) */
|
||||
public static final String KEY_POLICY_TIME_SETTING = "policy_time_setting_key";
|
||||
/** 存储卡(1开 0关) */
|
||||
public static final String KEY_POLICY_STORAGE_CARD = "policy_storage_card_key";
|
||||
/** 恢复出厂(1开 0关) */
|
||||
public static final String KEY_POLICY_FACTORY_RESET = "policy_factory_reset_key";
|
||||
/** WiFi热点开关(1开 0关) */
|
||||
public static final String KEY_POLICY_WIFI_HOTSPOT = "policy_wifi_hotspot_key";
|
||||
/** 蓝牙开关(1开 0关) */
|
||||
public static final String KEY_POLICY_BLUETOOTH_SWITCH = "policy_bluetooth_switch_key";
|
||||
/** 壁纸功能(1开 0关) */
|
||||
public static final String KEY_POLICY_WALLPAPER = "policy_wallpaper_key";
|
||||
/** 通知栏显示(1开 0关) */
|
||||
public static final String KEY_POLICY_NOTIFICATION_BAR = "policy_notification_bar_key";
|
||||
/** 状态栏下拉开关(1开 0关) */
|
||||
public static final String KEY_POLICY_STATUS_BAR_PULL_DOWN = "policy_status_bar_pull_down_key";
|
||||
/** 系统导航条显示(1开 0关) */
|
||||
public static final String KEY_POLICY_SYSTEM_NAV_BAR_SHOW = "policy_system_nav_bar_show_key";
|
||||
/** 系统导航栏设置(1开 0关) */
|
||||
public static final String KEY_POLICY_SYSTEM_NAV_BAR_SETTING = "policy_system_nav_bar_setting_key";
|
||||
/** 导航条选项(1开 0关) */
|
||||
public static final String KEY_POLICY_NAV_BAR_OPTIONS = "policy_nav_bar_options_key";
|
||||
/** 蓝牙功能开关(1开 0关) */
|
||||
public static final String KEY_POLICY_BLUETOOTH_FUNCTION = "policy_bluetooth_function_key";
|
||||
/** OTA升级管控(1开 0关) */
|
||||
public static final String KEY_POLICY_OTA_UPGRADE = "policy_ota_upgrade_key";
|
||||
/** 应用安装开关(1开 0关) */
|
||||
public static final String KEY_POLICY_APP_INSTALL = "policy_app_install_key";
|
||||
/** 自动旋转(1开 0关) */
|
||||
public static final String KEY_POLICY_AUTO_ROTATE = "policy_auto_rotate_key";
|
||||
/** 自动亮度(1开 0关) */
|
||||
public static final String KEY_POLICY_AUTO_BRIGHTNESS = "policy_auto_brightness_key";
|
||||
/** 护眼模式(1开 0关) */
|
||||
public static final String KEY_POLICY_EYE_PROTECTION = "policy_eye_protection_key";
|
||||
/** 深色模式(1开 0关) */
|
||||
public static final String KEY_POLICY_DARK_MODE = "policy_dark_mode_key";
|
||||
|
||||
}
|
||||
|
||||
@@ -34,9 +34,11 @@ import com.ttstd.dialer.bean.req.SnNetworkInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnOtherInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnSecurityInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnSystemInfoReq;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.dialer.utils.SystemUtils;
|
||||
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import cn.jpush.android.api.JPushInterface;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
@@ -625,7 +627,10 @@ public class DeviceInfoCollector {
|
||||
(TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if (tm != null) {
|
||||
req.setIccid(safeString(tm::getSimSerialNumber));
|
||||
req.setPhoneNumber(safeString(tm::getLine1Number));
|
||||
// 已解除绑定:不再上报 SIM 卡手机号,避免服务端重新写回 sys_sn.snMobile
|
||||
if (!isUnbindMobile()) {
|
||||
req.setPhoneNumber(safeString(tm::getLine1Number));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
@@ -728,6 +733,22 @@ public class DeviceInfoCollector {
|
||||
// 工具方法
|
||||
// ==================================================================
|
||||
|
||||
/**
|
||||
* 是否已解除手机号绑定。
|
||||
* <p>
|
||||
* 由服务端「解除绑定」推送(contentType = 18)在 {@link CommonConfig#KEY_DEVICE_UNBIND_MOBILE}
|
||||
* 写入 1。置位后上报 {@code otherInfo} 不再携带 SIM 卡手机号。
|
||||
*/
|
||||
private boolean isUnbindMobile() {
|
||||
try {
|
||||
MMKV mmkv = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
return mmkv.decodeInt(CommonConfig.KEY_DEVICE_UNBIND_MOBILE, 0) == 1;
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "isUnbindMobile failed: " + t.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 供 lambda 使用的取值接口,用于统一包裹异常。
|
||||
*/
|
||||
|
||||
@@ -91,6 +91,8 @@ public class PushExecutor {
|
||||
private static final String DEVICE_CLEAN = "15";
|
||||
private static final String DEVICE_MIRROR = "16";
|
||||
private static final String DEVICE_SCREEN_LOCK = "17";
|
||||
private static final String DEVICE_UNBIND_MOBILE = "18";
|
||||
private static final String DEVICE_POLICY = "19";
|
||||
|
||||
|
||||
/**
|
||||
@@ -198,6 +200,12 @@ public class PushExecutor {
|
||||
case DEVICE_SCREEN_LOCK:
|
||||
screenLock();
|
||||
break;
|
||||
case DEVICE_UNBIND_MOBILE:
|
||||
unbindMobile();
|
||||
break;
|
||||
case DEVICE_POLICY:
|
||||
setDevicePolicy(extra);
|
||||
break;
|
||||
default:
|
||||
Logger.e(TAG, "setPushMessage: default");
|
||||
}
|
||||
@@ -710,7 +718,7 @@ public class PushExecutor {
|
||||
return;
|
||||
}
|
||||
|
||||
if (acceptKey == 1 || isAutoRemoteAssistanceEnabled()) {
|
||||
if (acceptKey == 1) {
|
||||
// 自动接收并采集窗口串流(推送指定自动,或本地已开启自动接收开关)
|
||||
Logger.d(TAG, "mirror: 自动接收远程协助,启动采集");
|
||||
ScreenCaptureActivity.start(mContext);
|
||||
@@ -754,18 +762,6 @@ public class PushExecutor {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取本地"自动接收远程协助"开关(与设置页 siAutoRemoteAssistance 同步)。
|
||||
*/
|
||||
private boolean isAutoRemoteAssistanceEnabled() {
|
||||
try {
|
||||
MMKV mmkv = MMKV.mmkvWithID("MMKV_DEFAULT");
|
||||
return mmkv != null && mmkv.decodeBool("AutoRemoteAssistanceEnable", false);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 屏幕锁定(contentType = 17, screenLock)。
|
||||
* <p>
|
||||
@@ -776,6 +772,156 @@ public class PushExecutor {
|
||||
Logger.e(TAG, "screenLock: 收到屏幕锁定推送,功能待实现");
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除设备手机号绑定(contentType = 18, unbindMobile)。
|
||||
* <p>
|
||||
* 由后端 {@code DeviceService.unbindMobile} 下发。收到后在本地标记「已解绑」,
|
||||
* 之后上报 {@code otherInfo} 不再携带 SIM 卡手机号,避免服务端重新建立绑定。
|
||||
*/
|
||||
private void unbindMobile() {
|
||||
Logger.e(TAG, "unbindMobile: 收到解除绑定推送");
|
||||
try {
|
||||
mMMKV.encode(CommonConfig.KEY_DEVICE_UNBIND_MOBILE, 1);
|
||||
Logger.e(TAG, "unbindMobile: 已本地标记解除绑定");
|
||||
} catch (Throwable t) {
|
||||
Logger.e(TAG, "unbindMobile failed: " + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置设备策略开关(contentType = 19, policy)。
|
||||
* <p>
|
||||
* 由后端在策略全量保存 / 单独设置后推送,extra 承载全部策略开关状态(0关闭 1开启)。
|
||||
* 解析后写入本地 MMKV,并对已有设备控制能力的开关即时应用;暂无能力的开关先落库并记录日志。
|
||||
*/
|
||||
private void setDevicePolicy(String extra) {
|
||||
Logger.e(TAG, "setDevicePolicy: 收到策略推送, extra=" + extra);
|
||||
if (TextUtils.isEmpty(extra)) {
|
||||
Logger.e(TAG, "setDevicePolicy: extra 为空");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
JsonObject json = JsonParser.parseString(extra).getAsJsonObject();
|
||||
DeviceManagerService mdm = DeviceManagerService.getInstance();
|
||||
|
||||
// USB偏好设置
|
||||
int usbData = getIntField(json, "usbData", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_USB_DATA, usbData);
|
||||
|
||||
// 时间设置管控
|
||||
int timeSetting = getIntField(json, "timeSetting", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_TIME_SETTING, timeSetting);
|
||||
|
||||
// 存储卡
|
||||
int storageCard = getIntField(json, "storageCard", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_STORAGE_CARD, storageCard);
|
||||
|
||||
// 恢复出厂
|
||||
int factoryReset = getIntField(json, "factoryReset", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_FACTORY_RESET, factoryReset);
|
||||
|
||||
// WiFi热点开关
|
||||
int wifiHotspot = getIntField(json, "wifiHotspot", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_WIFI_HOTSPOT, wifiHotspot);
|
||||
|
||||
// 蓝牙开关
|
||||
int bluetoothSwitch = getIntField(json, "bluetoothSwitch", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_BLUETOOTH_SWITCH, bluetoothSwitch);
|
||||
|
||||
// 壁纸功能
|
||||
int wallpaper = getIntField(json, "wallpaper", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_WALLPAPER, wallpaper);
|
||||
|
||||
// 通知栏显示
|
||||
int notificationBar = getIntField(json, "notificationBar", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_NOTIFICATION_BAR, notificationBar);
|
||||
|
||||
// 状态栏下拉开关
|
||||
int statusBarPullDown = getIntField(json, "statusBarPullDown", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_STATUS_BAR_PULL_DOWN, statusBarPullDown);
|
||||
|
||||
// 系统导航条显示
|
||||
int systemNavBarShow = getIntField(json, "systemNavBarShow", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_SYSTEM_NAV_BAR_SHOW, systemNavBarShow);
|
||||
|
||||
// 系统导航栏设置
|
||||
int systemNavBarSetting = getIntField(json, "systemNavBarSetting", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_SYSTEM_NAV_BAR_SETTING, systemNavBarSetting);
|
||||
|
||||
// 导航条选项
|
||||
int navBarOptions = getIntField(json, "navBarOptions", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_NAV_BAR_OPTIONS, navBarOptions);
|
||||
|
||||
// 蓝牙功能开关
|
||||
int bluetoothFunction = getIntField(json, "bluetoothFunction", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_BLUETOOTH_FUNCTION, bluetoothFunction);
|
||||
|
||||
// OTA升级管控
|
||||
int otaUpgrade = getIntField(json, "otaUpgrade", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_OTA_UPGRADE, otaUpgrade);
|
||||
|
||||
// 应用安装开关(对应 deviceController.installPackage / uninstallPackage 的放行控制)
|
||||
int appInstall = getIntField(json, "appInstall", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_APP_INSTALL, appInstall);
|
||||
|
||||
// 自动旋转
|
||||
int autoRotate = getIntField(json, "autoRotate", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_AUTO_ROTATE, autoRotate);
|
||||
|
||||
// 自动亮度
|
||||
int autoBrightness = getIntField(json, "autoBrightness", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_AUTO_BRIGHTNESS, autoBrightness);
|
||||
|
||||
// 护眼模式
|
||||
int eyeProtection = getIntField(json, "eyeProtection", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_EYE_PROTECTION, eyeProtection);
|
||||
|
||||
// 深色模式
|
||||
int darkMode = getIntField(json, "darkMode", 1);
|
||||
mMMKV.encode(CommonConfig.KEY_POLICY_DARK_MODE, darkMode);
|
||||
|
||||
Logger.e(TAG, "setDevicePolicy: 策略解析完成并落库, usbData=" + usbData
|
||||
+ ", timeSetting=" + timeSetting
|
||||
+ ", storageCard=" + storageCard
|
||||
+ ", factoryReset=" + factoryReset
|
||||
+ ", wifiHotspot=" + wifiHotspot
|
||||
+ ", bluetoothSwitch=" + bluetoothSwitch
|
||||
+ ", wallpaper=" + wallpaper
|
||||
+ ", notificationBar=" + notificationBar
|
||||
+ ", statusBarPullDown=" + statusBarPullDown
|
||||
+ ", systemNavBarShow=" + systemNavBarShow
|
||||
+ ", systemNavBarSetting=" + systemNavBarSetting
|
||||
+ ", navBarOptions=" + navBarOptions
|
||||
+ ", bluetoothFunction=" + bluetoothFunction
|
||||
+ ", otaUpgrade=" + otaUpgrade
|
||||
+ ", appInstall=" + appInstall
|
||||
+ ", autoRotate=" + autoRotate
|
||||
+ ", autoBrightness=" + autoBrightness
|
||||
+ ", eyeProtection=" + eyeProtection
|
||||
+ ", darkMode=" + darkMode);
|
||||
|
||||
// TODO: 针对各策略开关的 OS 级能力(导航栏/通知栏/WiFi热点/蓝牙/壁纸等)已具备落地数据,
|
||||
// 接入对应 DeviceManagerService / DeviceController 能力后在此即时应用。
|
||||
|
||||
} catch (Exception e) {
|
||||
Logger.e(TAG, "setDevicePolicy: 解析策略失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从推送 extra JSON 中读取整型字段;字段缺失或解析失败时返回默认值。
|
||||
*/
|
||||
private int getIntField(JsonObject json, String field, int defaultValue) {
|
||||
try {
|
||||
if (json.has(field)) {
|
||||
return json.get(field).getAsInt();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.e(TAG, "getIntField: " + field + " 解析失败: " + e.getMessage());
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重启(重启系统)(contentType = 12, reboot)。
|
||||
* 通过 {@code reboot} 命令实现,需要设备具备 root / shell 权限。
|
||||
|
||||
@@ -281,7 +281,8 @@ public class ScreenCaptureService extends Service {
|
||||
}
|
||||
|
||||
if (isInitialized) {
|
||||
return START_STICKY;
|
||||
// 服务已在运行:不自动重启(远程控制会话结束后应保持停止,等待下一次命令拉起)
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
int resultCode = (sResultCode != Activity.RESULT_CANCELED)
|
||||
@@ -301,7 +302,9 @@ public class ScreenCaptureService extends Service {
|
||||
|
||||
isInitialized = true;
|
||||
startScreenCapture(resultCode, resultData);
|
||||
return START_STICKY;
|
||||
// 按需启动:远程控制会话结束(onControllerDisconnected)即 stopSelf,
|
||||
// 且不随系统重启自动复活,避免空转占用前台服务与 MediaProjection。
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
private void startScreenCapture(int resultCode, Intent resultData) {
|
||||
@@ -365,6 +368,11 @@ public class ScreenCaptureService extends Service {
|
||||
});
|
||||
|
||||
webRtcClient.setVideoCapturer(screenCapturer, width, height, fps);
|
||||
|
||||
// 启动心脏起搏器:周期刷新前台通知,强制 MediaProjection 持续产帧,
|
||||
// 避免静止画面(无触摸/无变化)下不推帧导致控制端黑屏。
|
||||
mainHandler.removeCallbacks(heartbeatRunnable);
|
||||
mainHandler.post(heartbeatRunnable);
|
||||
}
|
||||
|
||||
/** 解析信令服务器地址(ws:// 或 wss://)。 */
|
||||
@@ -496,9 +504,16 @@ public class ScreenCaptureService extends Service {
|
||||
|
||||
private void onSignalServerDisconnected() {
|
||||
if (isShuttingDown) return;
|
||||
Log.i(TAG, "Signal server disconnected, will auto-reconnect");
|
||||
updateNotification("信令断开,正在重连...");
|
||||
Log.i(TAG, "Signal server disconnected");
|
||||
if (stateListener != null) stateListener.onSignalDisconnected();
|
||||
// 信令已断开:若当前无控制端在观看,采集服务已无意义,直接停止;
|
||||
// 全局信令单例会自行重连,待下次命令再拉起本服务。
|
||||
if (controllingName == null || controllingName.isEmpty()) {
|
||||
Log.i(TAG, "No active controller, stopping screen capture service");
|
||||
stopSelf();
|
||||
} else {
|
||||
updateNotification("信令断开,正在重连...");
|
||||
}
|
||||
}
|
||||
|
||||
private void onControllerDisconnected(String controllerId) {
|
||||
@@ -506,9 +521,11 @@ public class ScreenCaptureService extends Service {
|
||||
String name = (controllingName != null && !controllingName.isEmpty())
|
||||
? controllingName : controllerId;
|
||||
Log.i(TAG, "Remote controller disconnected: " + name);
|
||||
updateNotification("远程控制服务正在运行");
|
||||
if (stateListener != null) stateListener.onControllerDisconnected(name);
|
||||
controllingName = null;
|
||||
// 远程控制会话已结束:停止前台采集服务,释放 MediaProjection / WebRTC 等资源。
|
||||
// 全局信令 WebSocket 由 siRemoteAssistance 开关控制,不受本服务停止影响。
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
private void sendConnectionRejected(String controllerId) {
|
||||
@@ -717,6 +734,8 @@ public class ScreenCaptureService extends Service {
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
isShuttingDown = true;
|
||||
// 停止心脏起搏器
|
||||
mainHandler.removeCallbacks(heartbeatRunnable);
|
||||
sResultCode = Activity.RESULT_CANCELED;
|
||||
sResultData = null;
|
||||
if (instance == this) instance = null;
|
||||
@@ -754,6 +773,40 @@ public class ScreenCaptureService extends Service {
|
||||
manager.notify(NOTIFICATION_ID, createNotification(contentText));
|
||||
}
|
||||
|
||||
/**
|
||||
* 心脏起搏器:周期刷新前台通知,强制 MediaProjection 持续产帧,
|
||||
* 解决静止画面(无触摸/无变化的屏幕)下 MediaProjection 不推帧、
|
||||
* 控制端看到黑屏或"等待画面"的问题。
|
||||
*
|
||||
* 优化点(避免打扰用户):
|
||||
* - 通知渠道为 IMPORTANCE_LOW + setSilent(true):周期刷新不响铃/不振铃/不弹横幅。
|
||||
* - 仅在「有远端控制端正在观看」时(controllingName != null)才刷新通知扰动推帧;
|
||||
* 空闲(无控制端连接)时不扰动、也不反复刷新,既省电又安静。
|
||||
* - 每 1 秒刷新一次通知文案(叠加动态圆点),状态栏变化即触发采集管线产出一帧。
|
||||
*/
|
||||
private final Runnable heartbeatRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isShuttingDown) {
|
||||
updateNotificationHeartbeat();
|
||||
mainHandler.postDelayed(this, 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private int heartbeatCount = 0;
|
||||
|
||||
private void updateNotificationHeartbeat() {
|
||||
// 空闲时(无控制端在观看)不做任何扰动,避免无谓刷新/打扰。
|
||||
if (controllingName == null || controllingName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
StringBuilder dots = new StringBuilder();
|
||||
for (int i = 0; i < (heartbeatCount % 3) + 1; i++) dots.append(".");
|
||||
heartbeatCount++;
|
||||
updateNotification("远程控制服务运行中" + dots);
|
||||
}
|
||||
|
||||
private Notification createNotification() {
|
||||
return createNotification("远程控制服务正在运行");
|
||||
}
|
||||
@@ -768,16 +821,21 @@ public class ScreenCaptureService extends Service {
|
||||
.setContentText(contentText)
|
||||
.setSmallIcon(android.R.drawable.ic_menu_camera)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
// 静默:即便周期刷新也不响铃/不振铃/不弹横幅(setOnlyAlertOnce 保证重复 notify 不再提醒)
|
||||
.setSilent(true)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setOngoing(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// IMPORTANCE_LOW:静默通知,不响铃/不振铃/不弹横幅,仅在通知栏安静展示,
|
||||
// 避免"心脏起搏器"周期刷新通知时反复打扰用户。
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
CHANNEL_ID, "屏幕共享服务", NotificationManager.IMPORTANCE_DEFAULT);
|
||||
CHANNEL_ID, "屏幕共享服务", NotificationManager.IMPORTANCE_LOW);
|
||||
channel.setDescription("远程控制屏幕采集前台服务");
|
||||
channel.setShowBadge(false);
|
||||
NotificationManager manager = getSystemService(NotificationManager.class);
|
||||
manager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user