refactor(notification): 将通知显示从Activity改为DialogFragment
重构通知显示机制,使用DialogFragment替代Activity,提升性能和用户体验: 1. 删除CommonNotificationActivity及相关资源 2. 新增NotificationDialogFragment实现全屏弹窗 3. 更新NotificationManager适配新机制 4. 添加全屏样式和布局文件 5. 优化系统UI隐藏逻辑
This commit is contained in:
parent
13a2bc5c00
commit
e01fd86961
0
.qoder/quests/project-interpretation-1758003461.md
Normal file
0
.qoder/quests/project-interpretation-1758003461.md
Normal file
0
.qoder/quests/project-interpretation-1758003538.md
Normal file
0
.qoder/quests/project-interpretation-1758003538.md
Normal file
0
.qoder/quests/project-interpretation.md
Normal file
0
.qoder/quests/project-interpretation.md
Normal file
@ -61,11 +61,7 @@
|
||||
android:name="com.ismart.ib86.feature.network.TestBSSIDActivity"
|
||||
android:exported="true" />
|
||||
|
||||
<!-- 通用通知Activity -->
|
||||
<activity
|
||||
android:name="com.ismart.ib86.common.notification.CommonNotificationActivity"
|
||||
android:exported="false"
|
||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" />
|
||||
|
||||
|
||||
<!-- 网络状态测试Activity -->
|
||||
<activity
|
||||
|
@ -224,7 +224,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
if (permissionsNeeded.isEmpty()) {
|
||||
// 所有权限已授予
|
||||
initializeParams();
|
||||
//initializeSDK();
|
||||
initializeSDK();
|
||||
} else {
|
||||
// 显示权限说明对话框
|
||||
showPermissionRationaleDialog(permissionsNeeded);
|
||||
@ -259,7 +259,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
Toast.makeText(this, "权限不足,应用可能无法正常运行", Toast.LENGTH_LONG).show();
|
||||
// 即使权限不足也尝试初始化SDK,让应用尽可能运行
|
||||
initializeParams();
|
||||
//initializeSDK();
|
||||
initializeSDK();
|
||||
})
|
||||
.show();
|
||||
}
|
||||
@ -284,7 +284,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
// 所有权限已授予
|
||||
Log.d(TAG, "所有权限已授予");
|
||||
initializeParams();
|
||||
//initializeSDK();
|
||||
initializeSDK();
|
||||
} else {
|
||||
// 有权限被拒绝
|
||||
Log.w(TAG, "部分权限被拒绝: " + deniedPermissions);
|
||||
@ -325,7 +325,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
Toast.makeText(this, "权限不足,应用可能无法正常运行", Toast.LENGTH_LONG).show();
|
||||
// 即使权限不足也尝试初始化SDK,让应用尽可能运行
|
||||
initializeParams();
|
||||
//initializeSDK();
|
||||
initializeSDK();
|
||||
})
|
||||
.show();
|
||||
}
|
||||
@ -351,7 +351,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
Toast.makeText(this, "权限不足,应用可能无法正常运行", Toast.LENGTH_LONG).show();
|
||||
// 即使权限不足也尝试初始化SDK,让应用尽可能运行
|
||||
initializeParams();
|
||||
//initializeSDK();
|
||||
initializeSDK();
|
||||
})
|
||||
.show();
|
||||
}
|
||||
@ -825,7 +825,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
authParams.setApiKey("sk-e5fe272222374ffca089a27cd1a75d11");
|
||||
authParams.setWorkspaceId("llm-ux9gbx66pglk2h3j");
|
||||
authParams.setVqaImageLink("https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7043267371/p909896.png");
|
||||
authParams.setDialogMode(Constant.DialogMode.DUPLEX);
|
||||
authParams.setDialogMode(Constant.DialogMode.TAP2TALK);
|
||||
authParams.setModelId("multimodal-dialog");
|
||||
authParams.setEnableDebug(true);
|
||||
authParams.setChainMode(Constant.ChainMode.WEBSOCKET);
|
||||
@ -1009,7 +1009,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
audioRecorder.pause();
|
||||
}
|
||||
Thread.sleep(1000);
|
||||
//initializeSDK();
|
||||
initializeSDK();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -1244,7 +1244,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
|
||||
try {
|
||||
String cmdName = new JSONArray(command).getJSONObject(0).getString("name");
|
||||
|
||||
Log.d(TAG, "执行命令: " + cmdName);
|
||||
switch (cmdName) {
|
||||
case "check_battery":
|
||||
multiModalDialog.requestToRespond("transcript", "当前电量为87%", null);
|
||||
@ -1252,6 +1252,12 @@ public class MainActivity extends AppCompatActivity {
|
||||
case "self_health_get_bloodoxygen":
|
||||
multiModalDialog.requestToRespond("transcript", "查询到最近一个月的平均血氧饱和度为93%", null);
|
||||
break;
|
||||
//增大音量
|
||||
case "increase_volume_default":
|
||||
|
||||
break;
|
||||
case "decrease_volume_default":
|
||||
break;
|
||||
// case "visual_qa":
|
||||
// executeVQACommand();
|
||||
// break;
|
||||
|
@ -1,228 +0,0 @@
|
||||
package com.ismart.ib86.common.notification;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import com.ismart.ib86.app.R;
|
||||
|
||||
/**
|
||||
* 通用通知Activity
|
||||
*/
|
||||
public class CommonNotificationActivity extends Activity {
|
||||
public static final String ACTION_CLOSE_ACTIVITY = "com.ismart.ib86.notification.CLOSE_COMMON_NOTIFICATION_ACTIVITY";
|
||||
public static final String ACTION_NOTIFICATION_CLOSED = "com.ismart.ib86.notification.NOTIFICATION_CLOSED";
|
||||
public static final String EXTRA_MESSAGE = "message";
|
||||
public static final String EXTRA_ICON = "icon";
|
||||
public static final String EXTRA_DURATION = "duration";
|
||||
public static final String EXTRA_TYPE = "type";
|
||||
public static final String EXTRA_SOUND_PATH = "sound_path";
|
||||
|
||||
private static final String TAG = "CommonNotificationActivity";
|
||||
private ImageView ivIcon;
|
||||
private TextView tvMessage;
|
||||
private String notificationType;
|
||||
private String soundPath;
|
||||
private BroadcastReceiver closeReceiver;
|
||||
private MediaPlayer mediaPlayer;
|
||||
private Handler handler;
|
||||
private Runnable finishRunnable;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_common_notification);
|
||||
|
||||
// 设置窗口属性,确保在锁屏时也能显示,并且全屏
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
||||
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
|
||||
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
|
||||
// 隐藏状态栏和导航栏(API 19及以上)
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||
}
|
||||
|
||||
initializeViews();
|
||||
handleIntent(getIntent());
|
||||
setupCloseReceiver();
|
||||
playNotificationSound();
|
||||
}
|
||||
|
||||
private void initializeViews() {
|
||||
ivIcon = findViewById(R.id.iv_icon);
|
||||
tvMessage = findViewById(R.id.tv_message);
|
||||
}
|
||||
|
||||
private void handleIntent(Intent intent) {
|
||||
if (intent != null) {
|
||||
String message = intent.getStringExtra(EXTRA_MESSAGE);
|
||||
int iconResId = intent.getIntExtra(EXTRA_ICON, android.R.drawable.ic_dialog_alert); // 默认使用系统警告图标
|
||||
int durationSeconds = intent.getIntExtra(EXTRA_DURATION, 5); // 默认5秒
|
||||
notificationType = intent.getStringExtra(EXTRA_TYPE);
|
||||
soundPath = intent.getStringExtra(EXTRA_SOUND_PATH);
|
||||
|
||||
Log.d(TAG, "Received intent with duration: " + durationSeconds + " seconds");
|
||||
|
||||
if (message != null) {
|
||||
tvMessage.setText(message);
|
||||
}
|
||||
|
||||
if (iconResId != 0) {
|
||||
ivIcon.setImageResource(iconResId);
|
||||
}
|
||||
|
||||
// 设置自动关闭
|
||||
if (durationSeconds > 0) {
|
||||
handler = new Handler();
|
||||
finishRunnable = () -> {
|
||||
Log.d(TAG, "Auto closing notification activity after " + durationSeconds + " seconds");
|
||||
finish();
|
||||
};
|
||||
handler.postDelayed(finishRunnable, durationSeconds * 1000L);
|
||||
Log.d(TAG, "Scheduled auto close in " + durationSeconds + " seconds");
|
||||
} else {
|
||||
Log.d(TAG, "Duration is 0 or negative, no auto close scheduled");
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Received null intent");
|
||||
}
|
||||
}
|
||||
|
||||
private void setupCloseReceiver() {
|
||||
closeReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (ACTION_CLOSE_ACTIVITY.equals(intent.getAction())) {
|
||||
Log.d(TAG, "Received close activity broadcast");
|
||||
finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IntentFilter filter = new IntentFilter(ACTION_CLOSE_ACTIVITY);
|
||||
registerReceiver(closeReceiver, filter);
|
||||
}
|
||||
|
||||
private void playNotificationSound() {
|
||||
try {
|
||||
// 如果提供了自定义音效路径,则使用自定义音效
|
||||
if (soundPath != null && !soundPath.isEmpty()) {
|
||||
if (soundPath.startsWith("android.resource://")) {
|
||||
// 处理res/raw资源
|
||||
Uri uri = Uri.parse(soundPath);
|
||||
mediaPlayer = MediaPlayer.create(this, uri);
|
||||
} else {
|
||||
// 处理文件路径
|
||||
mediaPlayer = new MediaPlayer();
|
||||
mediaPlayer.setDataSource(soundPath);
|
||||
mediaPlayer.prepare();
|
||||
}
|
||||
} else if (notificationType != null) {
|
||||
// 根据通知类型使用对应的默认音效
|
||||
switch (notificationType) {
|
||||
case "NETWORK_LOST":
|
||||
mediaPlayer = MediaPlayer.create(this, R.raw.network_lost_notification);
|
||||
break;
|
||||
case "NETWORK_CONFIG":
|
||||
mediaPlayer = MediaPlayer.create(this, R.raw.network_config_notification);
|
||||
break;
|
||||
case "BATTERY_LOW":
|
||||
mediaPlayer = MediaPlayer.create(this, R.raw.battery_low_notification);
|
||||
break;
|
||||
default:
|
||||
mediaPlayer = MediaPlayer.create(this, R.raw.default_notification);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// 使用系统默认提示音
|
||||
// mediaPlayer = MediaPlayer.create(this, android.R.raw.notification_sound);
|
||||
}
|
||||
|
||||
if (mediaPlayer != null) {
|
||||
mediaPlayer.setOnCompletionListener(mp -> {
|
||||
try {
|
||||
// 在播放完成后释放MediaPlayer资源
|
||||
mp.release();
|
||||
// 将mediaPlayer引用置为null,避免在onDestroy中重复释放
|
||||
mediaPlayer = null;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error releasing media player", e);
|
||||
}
|
||||
});
|
||||
mediaPlayer.start();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to play notification sound", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
Log.d(TAG, "onDestroy called");
|
||||
super.onDestroy();
|
||||
|
||||
// 通知NotificationManager重置显示状态
|
||||
Intent intent = new Intent(ACTION_NOTIFICATION_CLOSED);
|
||||
sendBroadcast(intent);
|
||||
|
||||
// 清理资源
|
||||
if (handler != null && finishRunnable != null) {
|
||||
Log.d(TAG, "Removing callbacks from handler");
|
||||
handler.removeCallbacks(finishRunnable);
|
||||
}
|
||||
|
||||
if (mediaPlayer != null) {
|
||||
try {
|
||||
// 在调用isPlaying()之前检查MediaPlayer状态,避免IllegalStateException
|
||||
if (mediaPlayer.isPlaying()) {
|
||||
Log.d(TAG, "Stopping media player");
|
||||
mediaPlayer.stop();
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
// MediaPlayer可能已经处于无效状态,忽略这个异常
|
||||
Log.w(TAG, "MediaPlayer is in invalid state, skipping stop()", e);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error stopping media player", e);
|
||||
}
|
||||
|
||||
try {
|
||||
Log.d(TAG, "Releasing media player");
|
||||
mediaPlayer.release();
|
||||
mediaPlayer = null;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error releasing media player", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (closeReceiver != null) {
|
||||
try {
|
||||
Log.d(TAG, "Unregistering receiver");
|
||||
unregisterReceiver(closeReceiver);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error unregistering receiver", e);
|
||||
}
|
||||
closeReceiver = null;
|
||||
}
|
||||
|
||||
Log.d(TAG, "onDestroy completed");
|
||||
}
|
||||
}
|
@ -0,0 +1,321 @@
|
||||
package com.ismart.ib86.common.notification;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import com.ismart.ib86.app.R;
|
||||
|
||||
/**
|
||||
* 通知弹窗DialogFragment
|
||||
*/
|
||||
public class NotificationDialogFragment extends DialogFragment {
|
||||
public static final String ACTION_NOTIFICATION_CLOSED = "com.ismart.ib86.notification.NOTIFICATION_CLOSED";
|
||||
public static final String ACTION_CLOSE_DIALOG = "com.ismart.ib86.notification.CLOSE_DIALOG";
|
||||
|
||||
private static final String ARG_MESSAGE = "message";
|
||||
private static final String ARG_ICON_RES_ID = "icon_res_id";
|
||||
private static final String ARG_DURATION = "duration";
|
||||
private static final String ARG_TYPE = "type";
|
||||
private static final String ARG_SOUND_PATH = "sound_path";
|
||||
private static final String TAG = "NotificationDialog";
|
||||
|
||||
private String message;
|
||||
private int iconResId;
|
||||
private int durationSeconds;
|
||||
private String notificationType;
|
||||
private String soundPath;
|
||||
private MediaPlayer mediaPlayer;
|
||||
private Handler handler;
|
||||
private Runnable dismissRunnable;
|
||||
|
||||
public static NotificationDialogFragment newInstance(String message, int iconResId,
|
||||
int durationSeconds, String type, String soundPath) {
|
||||
NotificationDialogFragment fragment = new NotificationDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(ARG_MESSAGE, message);
|
||||
args.putInt(ARG_ICON_RES_ID, iconResId);
|
||||
args.putInt(ARG_DURATION, durationSeconds);
|
||||
args.putString(ARG_TYPE, type);
|
||||
args.putString(ARG_SOUND_PATH, soundPath);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setStyle(DialogFragment.STYLE_NORMAL, R.style.FullScreenDialog);
|
||||
|
||||
// 设置DialogFragment的窗口标志以隐藏系统UI
|
||||
setShowsDialog(true);
|
||||
|
||||
if (getArguments() != null) {
|
||||
message = getArguments().getString(ARG_MESSAGE);
|
||||
iconResId = getArguments().getInt(ARG_ICON_RES_ID);
|
||||
durationSeconds = getArguments().getInt(ARG_DURATION);
|
||||
notificationType = getArguments().getString(ARG_TYPE);
|
||||
soundPath = getArguments().getString(ARG_SOUND_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Dialog dialog = super.onCreateDialog(savedInstanceState);
|
||||
if (dialog.getWindow() != null) {
|
||||
Window window = dialog.getWindow();
|
||||
window.requestFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
// 添加窗口标志以确保全屏并隐藏系统UI
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); // 防止导航栏显示
|
||||
|
||||
// 设置窗口布局参数
|
||||
WindowManager.LayoutParams params = window.getAttributes();
|
||||
params.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
params.height = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
window.setAttributes(params);
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.dialog_notification, container, false);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
// 设置Dialog为全屏并隐藏系统UI
|
||||
Dialog dialog = getDialog();
|
||||
if (dialog != null && dialog.getWindow() != null) {
|
||||
Window window = dialog.getWindow();
|
||||
window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
|
||||
// 设置窗口标志以确保全屏
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); // 防止导航栏显示
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
window.getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE); // 添加额外的沉浸模式标志
|
||||
}
|
||||
|
||||
// 延迟设置焦点以确保系统UI正确隐藏
|
||||
window.getDecorView().post(() -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
window.getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
// 确保在恢复时系统UI状态正确
|
||||
Dialog dialog = getDialog();
|
||||
if (dialog != null && dialog.getWindow() != null) {
|
||||
Window window = dialog.getWindow();
|
||||
|
||||
// 设置窗口标志以确保全屏
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); // 恢复焦点
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
window.getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE);
|
||||
}
|
||||
|
||||
// 延迟设置焦点以确保系统UI正确隐藏
|
||||
window.getDecorView().post(() -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
window.getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
initializeViews(view);
|
||||
playNotificationSound();
|
||||
|
||||
// 设置自动关闭
|
||||
if (durationSeconds > 0) {
|
||||
handler = new Handler();
|
||||
dismissRunnable = () -> {
|
||||
if (getDialog() != null && getDialog().isShowing()) {
|
||||
dismissAllowingStateLoss();
|
||||
}
|
||||
};
|
||||
handler.postDelayed(dismissRunnable, durationSeconds * 1000L);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeViews(View view) {
|
||||
ImageView ivIcon = view.findViewById(R.id.iv_icon);
|
||||
TextView tvMessage = view.findViewById(R.id.tv_message);
|
||||
|
||||
if (message != null) {
|
||||
tvMessage.setText(message);
|
||||
}
|
||||
|
||||
if (iconResId != 0) {
|
||||
ivIcon.setImageResource(iconResId);
|
||||
}
|
||||
}
|
||||
|
||||
private void playNotificationSound() {
|
||||
try {
|
||||
// 如果提供了自定义音效路径,则使用自定义音效
|
||||
if (soundPath != null && !soundPath.isEmpty() && getActivity() != null) {
|
||||
if (soundPath.startsWith("android.resource://")) {
|
||||
// 处理res/raw资源
|
||||
Uri uri = Uri.parse(soundPath);
|
||||
mediaPlayer = MediaPlayer.create(getActivity(), uri);
|
||||
} else {
|
||||
// 处理文件路径
|
||||
mediaPlayer = new MediaPlayer();
|
||||
mediaPlayer.setDataSource(soundPath);
|
||||
mediaPlayer.prepare();
|
||||
}
|
||||
} else if (notificationType != null && getActivity() != null) {
|
||||
// 根据通知类型使用对应的默认音效
|
||||
switch (notificationType) {
|
||||
case "NETWORK_LOST":
|
||||
mediaPlayer = MediaPlayer.create(getActivity(), R.raw.network_lost_notification);
|
||||
break;
|
||||
case "NETWORK_CONFIG":
|
||||
mediaPlayer = MediaPlayer.create(getActivity(), R.raw.network_config_notification);
|
||||
break;
|
||||
case "BATTERY_LOW":
|
||||
mediaPlayer = MediaPlayer.create(getActivity(), R.raw.battery_low_notification);
|
||||
break;
|
||||
default:
|
||||
mediaPlayer = MediaPlayer.create(getActivity(), R.raw.default_notification);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mediaPlayer != null) {
|
||||
mediaPlayer.setOnCompletionListener(mp -> {
|
||||
try {
|
||||
// 在播放完成后释放MediaPlayer资源
|
||||
mp.release();
|
||||
// 将mediaPlayer引用置为null,避免在onDestroy中重复释放
|
||||
mediaPlayer = null;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error releasing media player", e);
|
||||
}
|
||||
});
|
||||
mediaPlayer.start();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to play notification sound", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
// 在销毁视图时确保系统UI状态正确
|
||||
if (getActivity() != null && getActivity().getWindow() != null) {
|
||||
// 恢复Activity的系统UI状态
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
getActivity().getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
// 通知NotificationManager重置显示状态
|
||||
if (getActivity() != null) {
|
||||
android.content.Intent intent = new android.content.Intent(ACTION_NOTIFICATION_CLOSED);
|
||||
getActivity().sendBroadcast(intent);
|
||||
}
|
||||
|
||||
// 清理资源
|
||||
if (handler != null && dismissRunnable != null) {
|
||||
handler.removeCallbacks(dismissRunnable);
|
||||
}
|
||||
|
||||
if (mediaPlayer != null) {
|
||||
try {
|
||||
// 在调用isPlaying()之前检查MediaPlayer状态,避免IllegalStateException
|
||||
if (mediaPlayer.isPlaying()) {
|
||||
mediaPlayer.stop();
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
// MediaPlayer可能已经处于无效状态,忽略这个异常
|
||||
Log.w(TAG, "MediaPlayer is in invalid state, skipping stop()", e);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error stopping media player", e);
|
||||
}
|
||||
|
||||
try {
|
||||
mediaPlayer.release();
|
||||
mediaPlayer = null;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error releasing media player", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package com.ismart.ib86.common.notification;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.FragmentManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@ -12,6 +13,8 @@ import android.util.Log;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import com.ismart.ib86.common.notification.NotificationDialogFragment;
|
||||
|
||||
/**
|
||||
* 通知管理器
|
||||
* 专门负责通知的显示和隐藏
|
||||
@ -19,8 +22,8 @@ import java.util.Queue;
|
||||
public class NotificationManager {
|
||||
private static final String TAG = "NotificationManager";
|
||||
private Context context;
|
||||
private Activity activityContext; // 保存Activity上下文用于启动Activity
|
||||
private boolean isActivityShowing = false;
|
||||
private Activity activityContext; // 保存Activity上下文用于启动DialogFragment
|
||||
private boolean isDialogShowing = false;
|
||||
private NotificationClosedReceiver notificationClosedReceiver;
|
||||
private NotificationRequest currentNotification; // 当前正在显示的通知
|
||||
private Queue<NotificationRequest> notificationQueue = new LinkedList<>(); // 通知队列
|
||||
@ -66,7 +69,7 @@ public class NotificationManager {
|
||||
NotificationRequest request = new NotificationRequest(type, message, iconResId, durationSeconds, soundPath, priority);
|
||||
|
||||
// 如果当前没有通知在显示,则直接显示
|
||||
if (!isActivityShowing) {
|
||||
if (!isDialogShowing) {
|
||||
displayNotification(request);
|
||||
} else {
|
||||
// 如果有通知在显示,检查优先级
|
||||
@ -94,74 +97,75 @@ public class NotificationManager {
|
||||
private void displayNotification(NotificationRequest request) {
|
||||
Log.d(TAG, "Displaying notification: " + request);
|
||||
|
||||
if (isActivityShowing) {
|
||||
Log.d(TAG, "Notification activity is already showing");
|
||||
if (isDialogShowing) {
|
||||
Log.d(TAG, "Notification dialog is already showing");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否有可用的Activity上下文
|
||||
if (activityContext == null) {
|
||||
Log.w(TAG, "Activity context is null, cannot show activity");
|
||||
Log.w(TAG, "Activity context is null, cannot show dialog");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查Activity是否有效
|
||||
if (activityContext.isFinishing() || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activityContext.isDestroyed())) {
|
||||
Log.w(TAG, "Activity is not valid, cannot show activity");
|
||||
Log.w(TAG, "Activity is not valid, cannot show dialog");
|
||||
return;
|
||||
}
|
||||
|
||||
activityContext.runOnUiThread(() -> {
|
||||
try {
|
||||
if (isActivityShowing) {
|
||||
Log.d(TAG, "Notification activity is already showing");
|
||||
if (isDialogShowing) {
|
||||
Log.d(TAG, "Notification dialog is already showing");
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(activityContext, CommonNotificationActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
intent.putExtra(CommonNotificationActivity.EXTRA_MESSAGE, request.getMessage());
|
||||
intent.putExtra(CommonNotificationActivity.EXTRA_ICON, request.getIconResId());
|
||||
intent.putExtra(CommonNotificationActivity.EXTRA_DURATION, request.getDurationSeconds());
|
||||
intent.putExtra(CommonNotificationActivity.EXTRA_TYPE, request.getType().name());
|
||||
if (request.getSoundPath() != null) {
|
||||
intent.putExtra(CommonNotificationActivity.EXTRA_SOUND_PATH, request.getSoundPath());
|
||||
}
|
||||
activityContext.startActivity(intent);
|
||||
isActivityShowing = true;
|
||||
// 创建并显示NotificationDialogFragment
|
||||
NotificationDialogFragment dialogFragment = NotificationDialogFragment.newInstance(
|
||||
request.getMessage(),
|
||||
request.getIconResId(),
|
||||
request.getDurationSeconds(),
|
||||
request.getType().name(),
|
||||
request.getSoundPath()
|
||||
);
|
||||
|
||||
FragmentManager fragmentManager = activityContext.getFragmentManager();
|
||||
dialogFragment.show(fragmentManager, "NotificationDialogFragment");
|
||||
isDialogShowing = true;
|
||||
currentNotification = request;
|
||||
Log.d(TAG, "Notification activity shown for " + request.getDurationSeconds() + " seconds, type: " + request.getType() + ", priority: " + request.getPriority());
|
||||
Log.d(TAG, "Notification dialog shown for " + request.getDurationSeconds() + " seconds, type: " + request.getType() + ", priority: " + request.getPriority());
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to show notification activity", e);
|
||||
Log.e(TAG, "Failed to show notification dialog", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void hideNotification() {
|
||||
Log.d(TAG, "hideNotification called, isActivityShowing: " + isActivityShowing);
|
||||
// 通过发送广播通知CommonNotificationActivity关闭自身
|
||||
if (isActivityShowing) {
|
||||
Log.d(TAG, "hideNotification called, isDialogShowing: " + isDialogShowing);
|
||||
// 通过发送广播通知NotificationDialogFragment关闭自身
|
||||
if (isDialogShowing) {
|
||||
try {
|
||||
Intent intent = new Intent(CommonNotificationActivity.ACTION_CLOSE_ACTIVITY);
|
||||
Intent intent = new Intent(NotificationDialogFragment.ACTION_CLOSE_DIALOG);
|
||||
context.sendBroadcast(intent);
|
||||
isActivityShowing = false;
|
||||
isDialogShowing = false;
|
||||
currentNotification = null;
|
||||
Log.d(TAG, "Notification activity hidden");
|
||||
Log.d(TAG, "Notification dialog hidden");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to hide notification activity", e);
|
||||
Log.e(TAG, "Failed to hide notification dialog", e);
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Notification activity is not showing, nothing to hide");
|
||||
Log.d(TAG, "Notification dialog is not showing, nothing to hide");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isActivityShowing() {
|
||||
return isActivityShowing;
|
||||
public boolean isDialogShowing() {
|
||||
return isDialogShowing;
|
||||
}
|
||||
|
||||
// 添加一个方法用于重置显示状态
|
||||
public void resetShowingState() {
|
||||
isActivityShowing = false;
|
||||
isDialogShowing = false;
|
||||
currentNotification = null;
|
||||
processNextNotification();
|
||||
}
|
||||
@ -193,7 +197,7 @@ public class NotificationManager {
|
||||
|
||||
private void registerNotificationClosedReceiver() {
|
||||
notificationClosedReceiver = new NotificationClosedReceiver();
|
||||
IntentFilter filter = new IntentFilter(CommonNotificationActivity.ACTION_NOTIFICATION_CLOSED);
|
||||
IntentFilter filter = new IntentFilter(NotificationDialogFragment.ACTION_NOTIFICATION_CLOSED);
|
||||
context.registerReceiver(notificationClosedReceiver, filter);
|
||||
}
|
||||
|
||||
@ -211,7 +215,7 @@ public class NotificationManager {
|
||||
private class NotificationClosedReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (CommonNotificationActivity.ACTION_NOTIFICATION_CLOSED.equals(intent.getAction())) {
|
||||
if (NotificationDialogFragment.ACTION_NOTIFICATION_CLOSED.equals(intent.getAction())) {
|
||||
Log.d(TAG, "Received notification closed broadcast, resetting showing state");
|
||||
resetShowingState();
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.ismart.ib86.app.R;
|
||||
import com.ismart.ib86.common.notification.CommonNotificationActivity;
|
||||
import com.ismart.ib86.common.notification.NotificationType;
|
||||
import com.ismart.ib86.common.notification.NotificationManager;
|
||||
import com.ismart.ib86.common.notification.NotificationPriority;
|
||||
@ -121,8 +120,8 @@ public class NetworkStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isActivityShowing() {
|
||||
return notificationManager != null ? notificationManager.isActivityShowing() : false;
|
||||
public boolean isDialogShowing() {
|
||||
return notificationManager != null ? notificationManager.isDialogShowing() : false;
|
||||
}
|
||||
|
||||
public NetworkState getCurrentNetworkState() {
|
||||
|
@ -1,58 +0,0 @@
|
||||
# NetworkStateManager 设计方案比较
|
||||
|
||||
## 方案一:分离式设计(当前实现)
|
||||
|
||||
### 组件结构
|
||||
1. **NetworkStateMonitor** - 负责网络状态监听
|
||||
2. **NetworkStateManager** - 负责状态管理和通知显示
|
||||
|
||||
### 优点
|
||||
1. **职责分离**:网络监听和状态管理完全分离
|
||||
2. **高内聚低耦合**:每个类只负责单一职责
|
||||
3. **可复用性**:NetworkStateMonitor可以被其他不涉及通知显示的组件复用
|
||||
4. **可测试性**:可以独立测试网络监听功能
|
||||
5. **可扩展性**:可以在NetworkStateManager中添加更多的状态管理逻辑
|
||||
|
||||
### 缺点
|
||||
1. **复杂性**:增加了类的数量和层级
|
||||
2. **间接性**:需要通过多个类来完成完整的功能
|
||||
|
||||
## 方案二:合并式设计(简化版本)
|
||||
|
||||
### 组件结构
|
||||
1. **SimpleNetworkStateManager** - 同时负责网络状态监听和状态管理
|
||||
|
||||
### 优点
|
||||
1. **简洁性**:只有一个类负责所有功能
|
||||
2. **直接性**:减少了组件间的依赖关系
|
||||
3. **易理解**:代码结构更简单,易于理解
|
||||
|
||||
### 缺点
|
||||
1. **职责混合**:一个类承担了多个职责
|
||||
2. **可复用性差**:网络监听功能难以被其他组件复用
|
||||
3. **可测试性差**:难以独立测试网络监听功能
|
||||
4. **扩展性差**:添加新功能时可能需要修改现有代码
|
||||
|
||||
## 推荐方案
|
||||
|
||||
### 如果项目需求简单且稳定
|
||||
推荐使用**方案二(合并式设计)**,因为:
|
||||
- 代码更简洁,易于维护
|
||||
- 减少了不必要的复杂性
|
||||
- 满足当前需求即可
|
||||
|
||||
### 如果项目需求复杂或多变
|
||||
推荐使用**方案一(分离式设计)**,因为:
|
||||
- 更好的代码组织和架构
|
||||
- 更高的可扩展性和可维护性
|
||||
- 符合软件设计原则(单一职责、开闭原则等)
|
||||
|
||||
## 当前实现的选择
|
||||
|
||||
当前实现采用了方案一(分离式设计),主要考虑:
|
||||
1. 项目可能需要扩展更多的网络状态管理功能
|
||||
2. 网络监听功能可能在其他地方被复用
|
||||
3. 遵循了良好的软件设计原则
|
||||
4. 便于单元测试和维护
|
||||
|
||||
如果确定项目只需要基本的网络状态监听和通知功能,可以考虑切换到方案二以简化代码结构。
|
@ -1,213 +0,0 @@
|
||||
package com.ismart.ib86.feature.network;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkRequest;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import com.ismart.ib86.common.notification.NotificationType;
|
||||
import com.ismart.ib86.common.notification.CommonNotificationActivity;
|
||||
|
||||
/**
|
||||
* 简化版网络状态管理器
|
||||
* 将网络监听和状态管理合并为一个类
|
||||
*/
|
||||
public class SimpleNetworkStateManager {
|
||||
private static final String TAG = "SimpleNetworkStateManager";
|
||||
private Context context;
|
||||
private ConnectivityManager connectivityManager;
|
||||
private ConnectivityManager.NetworkCallback networkCallback;
|
||||
private NetworkState currentState;
|
||||
private boolean isActivityShowing = false;
|
||||
|
||||
public SimpleNetworkStateManager(Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
this.currentState = NetworkState.NO_NETWORK;
|
||||
initializeNetworkCallback();
|
||||
}
|
||||
|
||||
private void initializeNetworkCallback() {
|
||||
networkCallback = new ConnectivityManager.NetworkCallback() {
|
||||
@Override
|
||||
public void onAvailable(Network network) {
|
||||
super.onAvailable(network);
|
||||
Log.d(TAG, "Network available: " + network);
|
||||
updateNetworkState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLost(Network network) {
|
||||
super.onLost(network);
|
||||
Log.d(TAG, "Network lost: " + network);
|
||||
updateNetworkState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
|
||||
super.onCapabilitiesChanged(network, networkCapabilities);
|
||||
Log.d(TAG, "Network capabilities changed: " + network);
|
||||
updateNetworkState();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void registerNetworkCallback() {
|
||||
if (connectivityManager == null) {
|
||||
Log.e(TAG, "ConnectivityManager is null");
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkRequest networkRequest = new NetworkRequest.Builder()
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
|
||||
.build();
|
||||
|
||||
try {
|
||||
connectivityManager.registerNetworkCallback(networkRequest, networkCallback);
|
||||
updateNetworkState(); // 初始化当前状态
|
||||
Log.d(TAG, "Network callback registered");
|
||||
} catch (SecurityException e) {
|
||||
Log.e(TAG, "Failed to register network callback due to permission issue", e);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to register network callback", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterNetworkCallback() {
|
||||
if (connectivityManager == null) {
|
||||
Log.e(TAG, "ConnectivityManager is null");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
connectivityManager.unregisterNetworkCallback(networkCallback);
|
||||
Log.d(TAG, "Network callback unregistered");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to unregister network callback", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateNetworkState() {
|
||||
NetworkState newState = getNetworkState();
|
||||
if (newState != currentState) {
|
||||
Log.d(TAG, "Network state changed from " + currentState + " to " + newState);
|
||||
currentState = newState;
|
||||
|
||||
// 处理状态变化
|
||||
if (currentState == NetworkState.NO_NETWORK) {
|
||||
showNotification(NotificationType.NETWORK_LOST, "设备已断开网络连接,请检查WiFi或移动数据设置。", android.R.drawable.ic_dialog_alert, 5, null);
|
||||
} else {
|
||||
hideNotification();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public NetworkState getNetworkState() {
|
||||
if (connectivityManager != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
Network activeNetwork = connectivityManager.getActiveNetwork();
|
||||
if (activeNetwork != null) {
|
||||
NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(activeNetwork);
|
||||
if (capabilities != null) {
|
||||
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
|
||||
return NetworkState.WIFI_CONNECTED;
|
||||
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
|
||||
return NetworkState.MOBILE_CONNECTED;
|
||||
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
|
||||
return NetworkState.ETHERNET_CONNECTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 兼容旧版本API
|
||||
android.net.NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
|
||||
if (activeNetworkInfo != null && activeNetworkInfo.isConnected()) {
|
||||
int type = activeNetworkInfo.getType();
|
||||
if (type == ConnectivityManager.TYPE_WIFI) {
|
||||
return NetworkState.WIFI_CONNECTED;
|
||||
} else if (type == ConnectivityManager.TYPE_MOBILE) {
|
||||
return NetworkState.MOBILE_CONNECTED;
|
||||
} else if (type == ConnectivityManager.TYPE_ETHERNET) {
|
||||
return NetworkState.ETHERNET_CONNECTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NetworkState.NO_NETWORK;
|
||||
}
|
||||
|
||||
public void showNotification(NotificationType type, String message, int iconResId, int durationSeconds) {
|
||||
showNotification(type, message, iconResId, durationSeconds, null);
|
||||
}
|
||||
|
||||
public void showNotification(NotificationType type, String message, int iconResId, int durationSeconds, String soundPath) {
|
||||
if (isActivityShowing) {
|
||||
Log.d(TAG, "Notification activity is already showing");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(context instanceof Activity)) {
|
||||
Log.w(TAG, "Context is not an Activity, cannot show activity");
|
||||
return;
|
||||
}
|
||||
|
||||
Activity activity = (Activity) context;
|
||||
if (activity.isFinishing() || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed())) {
|
||||
Log.w(TAG, "Activity is not valid, cannot show activity");
|
||||
return;
|
||||
}
|
||||
|
||||
activity.runOnUiThread(() -> {
|
||||
try {
|
||||
if (isActivityShowing) {
|
||||
Log.d(TAG, "Notification activity is already showing");
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(activity, CommonNotificationActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
intent.putExtra(CommonNotificationActivity.EXTRA_MESSAGE, message);
|
||||
intent.putExtra(CommonNotificationActivity.EXTRA_ICON, iconResId);
|
||||
intent.putExtra(CommonNotificationActivity.EXTRA_DURATION, durationSeconds);
|
||||
intent.putExtra(CommonNotificationActivity.EXTRA_TYPE, type.name());
|
||||
if (soundPath != null) {
|
||||
intent.putExtra(CommonNotificationActivity.EXTRA_SOUND_PATH, soundPath);
|
||||
}
|
||||
activity.startActivity(intent);
|
||||
isActivityShowing = true;
|
||||
Log.d(TAG, "Notification activity shown for " + durationSeconds + " seconds, type: " + type);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to show notification activity", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void hideNotification() {
|
||||
// 通过发送广播通知CommonNotificationActivity关闭自身
|
||||
if (isActivityShowing) {
|
||||
try {
|
||||
Intent intent = new Intent(CommonNotificationActivity.ACTION_CLOSE_ACTIVITY);
|
||||
context.sendBroadcast(intent);
|
||||
isActivityShowing = false;
|
||||
Log.d(TAG, "Notification activity hidden");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to hide notification activity", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isActivityShowing() {
|
||||
return isActivityShowing;
|
||||
}
|
||||
|
||||
public NetworkState getCurrentState() {
|
||||
return currentState;
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
package com.ismart.ib86.feature.network;
|
||||
|
||||
import android.util.Log;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 测试WiFi扫描结果发送逻辑
|
||||
*/
|
||||
public class TestWiFiScanResult {
|
||||
private static final String TAG = "TestWiFiScanResult";
|
||||
|
||||
/**
|
||||
* 测试格式化WiFi扫描结果
|
||||
*/
|
||||
public static void testFormatScanResult() {
|
||||
Log.i(TAG, "=== 开始测试WiFi扫描结果格式化 ===");
|
||||
|
||||
// 模拟WiFi扫描结果
|
||||
List<String> mockSSIDs = Arrays.asList("1603", "1603-5G", "ismart_hw_2.4g");
|
||||
Log.i(TAG, "模拟WiFi扫描结果: " + mockSSIDs);
|
||||
|
||||
// 创建ATCommandProcessor实例进行测试
|
||||
ATCommandProcessor processor = new ATCommandProcessor(null, null, null);
|
||||
|
||||
// 调用格式化方法
|
||||
String result = null;
|
||||
try {
|
||||
// 使用反射调用私有方法
|
||||
java.lang.reflect.Method method = ATCommandProcessor.class.getDeclaredMethod("formatScanResult", List.class);
|
||||
method.setAccessible(true);
|
||||
result = (String) method.invoke(processor, mockSSIDs);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "调用formatScanResult方法时出错", e);
|
||||
}
|
||||
|
||||
Log.i(TAG, "格式化结果: " + result);
|
||||
|
||||
// 验证结果
|
||||
if (result != null && result.startsWith("AT+SSID=3,")) {
|
||||
Log.i(TAG, "✅ 格式化结果正确");
|
||||
} else {
|
||||
Log.e(TAG, "❌ 格式化结果不正确");
|
||||
}
|
||||
|
||||
Log.i(TAG, "=== WiFi扫描结果格式化测试完成 ===");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试完整的WiFi扫描结果发送流程
|
||||
*/
|
||||
public static void testSendScanResult() {
|
||||
Log.i(TAG, "=== 开始测试WiFi扫描结果发送 ===");
|
||||
|
||||
// 这个测试需要实际的BLE环境,所以只是模拟日志输出
|
||||
Log.i(TAG, "模拟WiFi扫描完成,找到3个网络: [1603, 1603-5G, ismart_hw_2.4g]");
|
||||
Log.i(TAG, "应该发送格式化结果: AT+SSID=3,1603,1603-5G,ismart_hw_2.4g");
|
||||
Log.i(TAG, "通过UUID 0xEA01 发送通知给小程序");
|
||||
|
||||
Log.i(TAG, "=== WiFi扫描结果发送测试完成 ===");
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
package com.ismart.ib86.feature.network;
|
||||
|
||||
import android.util.Log;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 测试WiFi扫描结果发送逻辑(包含BSSID)
|
||||
*/
|
||||
public class TestWiFiScanResultWithBSSID {
|
||||
private static final String TAG = "TestWiFiScanResultWithBSSID";
|
||||
|
||||
/**
|
||||
* 测试格式化WiFi扫描结果(包含BSSID)
|
||||
*/
|
||||
public static void testFormatScanResultWithBSSID() {
|
||||
Log.i(TAG, "=== 开始测试WiFi扫描结果格式化(包含BSSID) ===");
|
||||
|
||||
// 模拟WiFi扫描结果(包含BSSID信息)
|
||||
List<WiFiNetworkInfo> mockNetworks = Arrays.asList(
|
||||
new WiFiNetworkInfo("1603", "e0:0c:e5:13:90:00", -87, 2437),
|
||||
new WiFiNetworkInfo("1603-5G", "c4:2b:44:f1:25:80", -60, 5745),
|
||||
new WiFiNetworkInfo("ismart_hw_2.4g", "c0:61:18:c9:53:2b", -95, 2412)
|
||||
);
|
||||
Log.i(TAG, "模拟WiFi扫描结果: " + mockNetworks);
|
||||
|
||||
// 创建ATCommandProcessor实例进行测试
|
||||
ATCommandProcessor processor = new ATCommandProcessor(null, null, null);
|
||||
|
||||
// 调用新的格式化方法
|
||||
String result = null;
|
||||
try {
|
||||
// 使用反射调用私有方法
|
||||
java.lang.reflect.Method method = ATCommandProcessor.class.getDeclaredMethod("formatScanResultWithBSSID", List.class);
|
||||
method.setAccessible(true);
|
||||
result = (String) method.invoke(processor, mockNetworks);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "调用formatScanResultWithBSSID方法时出错", e);
|
||||
}
|
||||
|
||||
Log.i(TAG, "格式化结果(包含BSSID): " + result);
|
||||
|
||||
// 验证结果
|
||||
if (result != null && result.startsWith("AT+SSID=3,") && result.contains(":")) {
|
||||
Log.i(TAG, "✅ 格式化结果(包含BSSID)正确");
|
||||
} else {
|
||||
Log.e(TAG, "❌ 格式化结果(包含BSSID)不正确");
|
||||
}
|
||||
|
||||
Log.i(TAG, "=== WiFi扫描结果格式化测试(包含BSSID)完成 ===");
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试完整的WiFi扫描结果发送流程(包含BSSID)
|
||||
*/
|
||||
public static void testSendScanResultWithBSSID() {
|
||||
Log.i(TAG, "=== 开始测试WiFi扫描结果发送(包含BSSID) ===");
|
||||
|
||||
// 这个测试需要实际的BLE环境,所以只是模拟日志输出
|
||||
Log.i(TAG, "模拟WiFi扫描完成,找到3个网络:");
|
||||
Log.i(TAG, " [0] SSID: '1603-5G', BSSID: c4:2b:44:f1:25:80");
|
||||
Log.i(TAG, " [1] SSID: '1603', BSSID: e0:0c:e5:13:90:00");
|
||||
Log.i(TAG, " [2] SSID: 'ismart_hw_2.4g', BSSID: c0:61:18:c9:53:2b");
|
||||
Log.i(TAG, "应该发送格式化结果: AT+SSID=3,1603-5G:c4:2b:44:f1:25:80,1603:e0:0c:e5:13:90:00,ismart_hw_2.4g:c0:61:18:c9:53:2b");
|
||||
Log.i(TAG, "通过UUID 0xEA01 发送通知给小程序");
|
||||
|
||||
Log.i(TAG, "=== WiFi扫描结果发送测试(包含BSSID)完成 ===");
|
||||
}
|
||||
}
|
25
app/src/main/res/layout/dialog_notification.xml
Normal file
25
app/src/main/res/layout/dialog_notification.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:background="#000000"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_icon"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginTop="-80dp"
|
||||
android:layout_marginBottom="24dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="30sp"
|
||||
android:textColor="#FFFFFF"
|
||||
android:gravity="center" />
|
||||
|
||||
</LinearLayout>
|
12
app/src/main/res/values/styles.xml
Normal file
12
app/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="FullScreenDialog" parent="android:Theme.Dialog">
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
<item name="android:windowIsFloating">false</item>
|
||||
<item name="android:windowBackground">@android:color/black</item>
|
||||
<!-- 隐藏导航栏 -->
|
||||
<item name="android:windowTranslucentStatus">true</item>
|
||||
<item name="android:windowTranslucentNavigation">true</item>
|
||||
</style>
|
||||
</resources>
|
@ -1,9 +1,11 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Base.Theme.IB86" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<style name="Base.Theme.IB86" parent="Theme.AppCompat.NoActionBar">
|
||||
<!-- Customize your light theme here. -->
|
||||
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.IB86" parent="Base.Theme.IB86" />
|
||||
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user