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:name="com.ismart.ib86.feature.network.TestBSSIDActivity"
|
||||||
android:exported="true" />
|
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 -->
|
||||||
<activity
|
<activity
|
||||||
|
@ -224,7 +224,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
if (permissionsNeeded.isEmpty()) {
|
if (permissionsNeeded.isEmpty()) {
|
||||||
// 所有权限已授予
|
// 所有权限已授予
|
||||||
initializeParams();
|
initializeParams();
|
||||||
//initializeSDK();
|
initializeSDK();
|
||||||
} else {
|
} else {
|
||||||
// 显示权限说明对话框
|
// 显示权限说明对话框
|
||||||
showPermissionRationaleDialog(permissionsNeeded);
|
showPermissionRationaleDialog(permissionsNeeded);
|
||||||
@ -259,7 +259,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
Toast.makeText(this, "权限不足,应用可能无法正常运行", Toast.LENGTH_LONG).show();
|
Toast.makeText(this, "权限不足,应用可能无法正常运行", Toast.LENGTH_LONG).show();
|
||||||
// 即使权限不足也尝试初始化SDK,让应用尽可能运行
|
// 即使权限不足也尝试初始化SDK,让应用尽可能运行
|
||||||
initializeParams();
|
initializeParams();
|
||||||
//initializeSDK();
|
initializeSDK();
|
||||||
})
|
})
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
@ -284,7 +284,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
// 所有权限已授予
|
// 所有权限已授予
|
||||||
Log.d(TAG, "所有权限已授予");
|
Log.d(TAG, "所有权限已授予");
|
||||||
initializeParams();
|
initializeParams();
|
||||||
//initializeSDK();
|
initializeSDK();
|
||||||
} else {
|
} else {
|
||||||
// 有权限被拒绝
|
// 有权限被拒绝
|
||||||
Log.w(TAG, "部分权限被拒绝: " + deniedPermissions);
|
Log.w(TAG, "部分权限被拒绝: " + deniedPermissions);
|
||||||
@ -325,7 +325,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
Toast.makeText(this, "权限不足,应用可能无法正常运行", Toast.LENGTH_LONG).show();
|
Toast.makeText(this, "权限不足,应用可能无法正常运行", Toast.LENGTH_LONG).show();
|
||||||
// 即使权限不足也尝试初始化SDK,让应用尽可能运行
|
// 即使权限不足也尝试初始化SDK,让应用尽可能运行
|
||||||
initializeParams();
|
initializeParams();
|
||||||
//initializeSDK();
|
initializeSDK();
|
||||||
})
|
})
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
@ -351,7 +351,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
Toast.makeText(this, "权限不足,应用可能无法正常运行", Toast.LENGTH_LONG).show();
|
Toast.makeText(this, "权限不足,应用可能无法正常运行", Toast.LENGTH_LONG).show();
|
||||||
// 即使权限不足也尝试初始化SDK,让应用尽可能运行
|
// 即使权限不足也尝试初始化SDK,让应用尽可能运行
|
||||||
initializeParams();
|
initializeParams();
|
||||||
//initializeSDK();
|
initializeSDK();
|
||||||
})
|
})
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
@ -825,7 +825,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
authParams.setApiKey("sk-e5fe272222374ffca089a27cd1a75d11");
|
authParams.setApiKey("sk-e5fe272222374ffca089a27cd1a75d11");
|
||||||
authParams.setWorkspaceId("llm-ux9gbx66pglk2h3j");
|
authParams.setWorkspaceId("llm-ux9gbx66pglk2h3j");
|
||||||
authParams.setVqaImageLink("https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/7043267371/p909896.png");
|
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.setModelId("multimodal-dialog");
|
||||||
authParams.setEnableDebug(true);
|
authParams.setEnableDebug(true);
|
||||||
authParams.setChainMode(Constant.ChainMode.WEBSOCKET);
|
authParams.setChainMode(Constant.ChainMode.WEBSOCKET);
|
||||||
@ -1009,7 +1009,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
audioRecorder.pause();
|
audioRecorder.pause();
|
||||||
}
|
}
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
//initializeSDK();
|
initializeSDK();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@ -1244,7 +1244,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
String cmdName = new JSONArray(command).getJSONObject(0).getString("name");
|
String cmdName = new JSONArray(command).getJSONObject(0).getString("name");
|
||||||
|
Log.d(TAG, "执行命令: " + cmdName);
|
||||||
switch (cmdName) {
|
switch (cmdName) {
|
||||||
case "check_battery":
|
case "check_battery":
|
||||||
multiModalDialog.requestToRespond("transcript", "当前电量为87%", null);
|
multiModalDialog.requestToRespond("transcript", "当前电量为87%", null);
|
||||||
@ -1252,6 +1252,12 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
case "self_health_get_bloodoxygen":
|
case "self_health_get_bloodoxygen":
|
||||||
multiModalDialog.requestToRespond("transcript", "查询到最近一个月的平均血氧饱和度为93%", null);
|
multiModalDialog.requestToRespond("transcript", "查询到最近一个月的平均血氧饱和度为93%", null);
|
||||||
break;
|
break;
|
||||||
|
//增大音量
|
||||||
|
case "increase_volume_default":
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "decrease_volume_default":
|
||||||
|
break;
|
||||||
// case "visual_qa":
|
// case "visual_qa":
|
||||||
// executeVQACommand();
|
// executeVQACommand();
|
||||||
// break;
|
// 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;
|
package com.ismart.ib86.common.notification;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.app.FragmentManager;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -12,6 +13,8 @@ import android.util.Log;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
|
||||||
|
import com.ismart.ib86.common.notification.NotificationDialogFragment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通知管理器
|
* 通知管理器
|
||||||
* 专门负责通知的显示和隐藏
|
* 专门负责通知的显示和隐藏
|
||||||
@ -19,8 +22,8 @@ import java.util.Queue;
|
|||||||
public class NotificationManager {
|
public class NotificationManager {
|
||||||
private static final String TAG = "NotificationManager";
|
private static final String TAG = "NotificationManager";
|
||||||
private Context context;
|
private Context context;
|
||||||
private Activity activityContext; // 保存Activity上下文用于启动Activity
|
private Activity activityContext; // 保存Activity上下文用于启动DialogFragment
|
||||||
private boolean isActivityShowing = false;
|
private boolean isDialogShowing = false;
|
||||||
private NotificationClosedReceiver notificationClosedReceiver;
|
private NotificationClosedReceiver notificationClosedReceiver;
|
||||||
private NotificationRequest currentNotification; // 当前正在显示的通知
|
private NotificationRequest currentNotification; // 当前正在显示的通知
|
||||||
private Queue<NotificationRequest> notificationQueue = new LinkedList<>(); // 通知队列
|
private Queue<NotificationRequest> notificationQueue = new LinkedList<>(); // 通知队列
|
||||||
@ -66,7 +69,7 @@ public class NotificationManager {
|
|||||||
NotificationRequest request = new NotificationRequest(type, message, iconResId, durationSeconds, soundPath, priority);
|
NotificationRequest request = new NotificationRequest(type, message, iconResId, durationSeconds, soundPath, priority);
|
||||||
|
|
||||||
// 如果当前没有通知在显示,则直接显示
|
// 如果当前没有通知在显示,则直接显示
|
||||||
if (!isActivityShowing) {
|
if (!isDialogShowing) {
|
||||||
displayNotification(request);
|
displayNotification(request);
|
||||||
} else {
|
} else {
|
||||||
// 如果有通知在显示,检查优先级
|
// 如果有通知在显示,检查优先级
|
||||||
@ -94,74 +97,75 @@ public class NotificationManager {
|
|||||||
private void displayNotification(NotificationRequest request) {
|
private void displayNotification(NotificationRequest request) {
|
||||||
Log.d(TAG, "Displaying notification: " + request);
|
Log.d(TAG, "Displaying notification: " + request);
|
||||||
|
|
||||||
if (isActivityShowing) {
|
if (isDialogShowing) {
|
||||||
Log.d(TAG, "Notification activity is already showing");
|
Log.d(TAG, "Notification dialog is already showing");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否有可用的Activity上下文
|
// 检查是否有可用的Activity上下文
|
||||||
if (activityContext == null) {
|
if (activityContext == null) {
|
||||||
Log.w(TAG, "Activity context is null, cannot show activity");
|
Log.w(TAG, "Activity context is null, cannot show dialog");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查Activity是否有效
|
// 检查Activity是否有效
|
||||||
if (activityContext.isFinishing() || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activityContext.isDestroyed())) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
activityContext.runOnUiThread(() -> {
|
activityContext.runOnUiThread(() -> {
|
||||||
try {
|
try {
|
||||||
if (isActivityShowing) {
|
if (isDialogShowing) {
|
||||||
Log.d(TAG, "Notification activity is already showing");
|
Log.d(TAG, "Notification dialog is already showing");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent intent = new Intent(activityContext, CommonNotificationActivity.class);
|
// 创建并显示NotificationDialogFragment
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
NotificationDialogFragment dialogFragment = NotificationDialogFragment.newInstance(
|
||||||
intent.putExtra(CommonNotificationActivity.EXTRA_MESSAGE, request.getMessage());
|
request.getMessage(),
|
||||||
intent.putExtra(CommonNotificationActivity.EXTRA_ICON, request.getIconResId());
|
request.getIconResId(),
|
||||||
intent.putExtra(CommonNotificationActivity.EXTRA_DURATION, request.getDurationSeconds());
|
request.getDurationSeconds(),
|
||||||
intent.putExtra(CommonNotificationActivity.EXTRA_TYPE, request.getType().name());
|
request.getType().name(),
|
||||||
if (request.getSoundPath() != null) {
|
request.getSoundPath()
|
||||||
intent.putExtra(CommonNotificationActivity.EXTRA_SOUND_PATH, request.getSoundPath());
|
);
|
||||||
}
|
|
||||||
activityContext.startActivity(intent);
|
FragmentManager fragmentManager = activityContext.getFragmentManager();
|
||||||
isActivityShowing = true;
|
dialogFragment.show(fragmentManager, "NotificationDialogFragment");
|
||||||
|
isDialogShowing = true;
|
||||||
currentNotification = request;
|
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) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Failed to show notification activity", e);
|
Log.e(TAG, "Failed to show notification dialog", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hideNotification() {
|
public void hideNotification() {
|
||||||
Log.d(TAG, "hideNotification called, isActivityShowing: " + isActivityShowing);
|
Log.d(TAG, "hideNotification called, isDialogShowing: " + isDialogShowing);
|
||||||
// 通过发送广播通知CommonNotificationActivity关闭自身
|
// 通过发送广播通知NotificationDialogFragment关闭自身
|
||||||
if (isActivityShowing) {
|
if (isDialogShowing) {
|
||||||
try {
|
try {
|
||||||
Intent intent = new Intent(CommonNotificationActivity.ACTION_CLOSE_ACTIVITY);
|
Intent intent = new Intent(NotificationDialogFragment.ACTION_CLOSE_DIALOG);
|
||||||
context.sendBroadcast(intent);
|
context.sendBroadcast(intent);
|
||||||
isActivityShowing = false;
|
isDialogShowing = false;
|
||||||
currentNotification = null;
|
currentNotification = null;
|
||||||
Log.d(TAG, "Notification activity hidden");
|
Log.d(TAG, "Notification dialog hidden");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Failed to hide notification activity", e);
|
Log.e(TAG, "Failed to hide notification dialog", e);
|
||||||
}
|
}
|
||||||
} else {
|
} 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() {
|
public boolean isDialogShowing() {
|
||||||
return isActivityShowing;
|
return isDialogShowing;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加一个方法用于重置显示状态
|
// 添加一个方法用于重置显示状态
|
||||||
public void resetShowingState() {
|
public void resetShowingState() {
|
||||||
isActivityShowing = false;
|
isDialogShowing = false;
|
||||||
currentNotification = null;
|
currentNotification = null;
|
||||||
processNextNotification();
|
processNextNotification();
|
||||||
}
|
}
|
||||||
@ -193,7 +197,7 @@ public class NotificationManager {
|
|||||||
|
|
||||||
private void registerNotificationClosedReceiver() {
|
private void registerNotificationClosedReceiver() {
|
||||||
notificationClosedReceiver = new NotificationClosedReceiver();
|
notificationClosedReceiver = new NotificationClosedReceiver();
|
||||||
IntentFilter filter = new IntentFilter(CommonNotificationActivity.ACTION_NOTIFICATION_CLOSED);
|
IntentFilter filter = new IntentFilter(NotificationDialogFragment.ACTION_NOTIFICATION_CLOSED);
|
||||||
context.registerReceiver(notificationClosedReceiver, filter);
|
context.registerReceiver(notificationClosedReceiver, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +215,7 @@ public class NotificationManager {
|
|||||||
private class NotificationClosedReceiver extends BroadcastReceiver {
|
private class NotificationClosedReceiver extends BroadcastReceiver {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
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");
|
Log.d(TAG, "Received notification closed broadcast, resetting showing state");
|
||||||
resetShowingState();
|
resetShowingState();
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import android.os.Build;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.ismart.ib86.app.R;
|
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.NotificationType;
|
||||||
import com.ismart.ib86.common.notification.NotificationManager;
|
import com.ismart.ib86.common.notification.NotificationManager;
|
||||||
import com.ismart.ib86.common.notification.NotificationPriority;
|
import com.ismart.ib86.common.notification.NotificationPriority;
|
||||||
@ -121,8 +120,8 @@ public class NetworkStateManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isActivityShowing() {
|
public boolean isDialogShowing() {
|
||||||
return notificationManager != null ? notificationManager.isActivityShowing() : false;
|
return notificationManager != null ? notificationManager.isDialogShowing() : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetworkState getCurrentNetworkState() {
|
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">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
<!-- 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. -->
|
<!-- Customize your light theme here. -->
|
||||||
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
|
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
|
||||||
|
<item name="android:windowFullscreen">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Theme.IB86" parent="Base.Theme.IB86" />
|
<style name="Theme.IB86" parent="Base.Theme.IB86" />
|
||||||
|
|
||||||
</resources>
|
</resources>
|
Loading…
x
Reference in New Issue
Block a user