上传文件至 /
This commit is contained in:
parent
43acbcfd33
commit
4372a7fc16
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package com.example.myapplication.Service;
|
package com.example.myapplication.Service;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
@ -18,6 +17,8 @@ import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.os.VibrationEffect;
|
||||||
|
import android.os.Vibrator;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
|
@ -75,9 +76,11 @@ public class FloatingWindowService extends Service {
|
||||||
private String lastUnitName = "";
|
private String lastUnitName = "";
|
||||||
private AppDatabase database;
|
private AppDatabase database;
|
||||||
private TurbineDao turbineDao;
|
private TurbineDao turbineDao;
|
||||||
private AutoCompleteTextView actvUnit; // 替换原来的EditText
|
private AutoCompleteTextView actvUnit;
|
||||||
private ArrayAdapter<Turbine> turbineAdapter;
|
private ArrayAdapter<Turbine> turbineAdapter;
|
||||||
private String projectID;
|
private String projectID;
|
||||||
|
public static boolean isFloatingWindowShowing = false;
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
|
@ -85,19 +88,17 @@ public class FloatingWindowService extends Service {
|
||||||
createNotificationChannel();
|
createNotificationChannel();
|
||||||
database = AppDatabase.getDatabase(this);
|
database = AppDatabase.getDatabase(this);
|
||||||
turbineDao = database.turbineDao();
|
turbineDao = database.turbineDao();
|
||||||
createNotificationChannel();
|
|
||||||
showFloatingWindow();
|
showFloatingWindow();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
if (intent != null && intent.hasExtra(EXTRA_PROJECT_ID)) {
|
if (intent != null && intent.hasExtra(EXTRA_PROJECT_ID)) {
|
||||||
projectID = intent.getStringExtra(EXTRA_PROJECT_ID);
|
projectID = intent.getStringExtra(EXTRA_PROJECT_ID);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return super.onStartCommand(intent, flags, startId);
|
return super.onStartCommand(intent, flags, startId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createNotificationChannel() {
|
private void createNotificationChannel() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
NotificationChannel channel = new NotificationChannel(
|
NotificationChannel channel = new NotificationChannel(
|
||||||
|
@ -112,6 +113,7 @@ public class FloatingWindowService extends Service {
|
||||||
|
|
||||||
private void showFloatingWindow() {
|
private void showFloatingWindow() {
|
||||||
// 创建前台服务通知
|
// 创建前台服务通知
|
||||||
|
isFloatingWindowShowing = true;
|
||||||
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
|
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
|
||||||
.setContentTitle("悬浮窗服务运行中")
|
.setContentTitle("悬浮窗服务运行中")
|
||||||
.setContentText("正在显示悬浮提示框")
|
.setContentText("正在显示悬浮提示框")
|
||||||
|
@ -120,11 +122,9 @@ public class FloatingWindowService extends Service {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
// Android 10+ 需要指定服务类型
|
|
||||||
startForeground(NOTIFICATION_ID, notification,
|
startForeground(NOTIFICATION_ID, notification,
|
||||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
|
ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
|
||||||
} else {
|
} else {
|
||||||
// Android 8.0-9.0
|
|
||||||
startForeground(NOTIFICATION_ID, notification);
|
startForeground(NOTIFICATION_ID, notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,14 +154,57 @@ public class FloatingWindowService extends Service {
|
||||||
setupTurbineDropdown();
|
setupTurbineDropdown();
|
||||||
radioGroup = floatingView.findViewById(R.id.radioGroup);
|
radioGroup = floatingView.findViewById(R.id.radioGroup);
|
||||||
|
|
||||||
|
// 设置关闭按钮点击事件
|
||||||
|
View btnClose = floatingView.findViewById(R.id.btnClose);
|
||||||
|
btnClose.setOnClickListener(v -> {
|
||||||
|
// 震动反馈
|
||||||
|
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
|
||||||
|
if (vibrator != null && vibrator.hasVibrator()) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
vibrator.vibrate(VibrationEffect.createOneShot(50, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||||
|
} else {
|
||||||
|
vibrator.vibrate(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closeFloatingWindow();
|
||||||
|
});
|
||||||
|
|
||||||
setupDragListener(params);
|
setupDragListener(params);
|
||||||
setupValueChangeListeners();
|
setupValueChangeListeners();
|
||||||
|
|
||||||
setupAutoCompleteBehavior();
|
setupAutoCompleteBehavior();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeFloatingWindow() {
|
||||||
|
if (floatingView != null && floatingView.getWindowToken() != null) {
|
||||||
|
// 添加淡出动画
|
||||||
|
floatingView.animate()
|
||||||
|
.alpha(0f)
|
||||||
|
.setDuration(200)
|
||||||
|
.withEndAction(() -> {
|
||||||
|
removeFloatingView();
|
||||||
|
stopSelf();
|
||||||
|
})
|
||||||
|
.start();
|
||||||
|
} else {
|
||||||
|
removeFloatingView();
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
isFloatingWindowShowing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void removeFloatingView() {
|
||||||
|
try {
|
||||||
|
if (windowManager != null && floatingView != null && floatingView.getWindowToken() != null) {
|
||||||
|
windowManager.removeView(floatingView);
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log.e("FloatingWindow", "View already removed", e);
|
||||||
|
} finally {
|
||||||
|
floatingView = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private void setupAutoCompleteBehavior() {
|
private void setupAutoCompleteBehavior() {
|
||||||
actvUnit.setFocusableInTouchMode(true); // 允许触摸获取焦点
|
actvUnit.setFocusableInTouchMode(true);
|
||||||
actvUnit.setFocusable(true);
|
actvUnit.setFocusable(true);
|
||||||
actvUnit.setOnClickListener(v -> {
|
actvUnit.setOnClickListener(v -> {
|
||||||
if (turbineAdapter.getCount() > 0) {
|
if (turbineAdapter.getCount() > 0) {
|
||||||
|
@ -190,6 +233,7 @@ public class FloatingWindowService extends Service {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showSoftInputDelayed(View view) {
|
private void showSoftInputDelayed(View view) {
|
||||||
view.postDelayed(() -> {
|
view.postDelayed(() -> {
|
||||||
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||||
|
@ -205,52 +249,55 @@ public class FloatingWindowService extends Service {
|
||||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 移除 setupInputMethod() 方法,因为功能已整合到上面的方法中
|
|
||||||
|
|
||||||
|
|
||||||
private void setupDragListener(WindowManager.LayoutParams params) {
|
private void setupDragListener(WindowManager.LayoutParams params) {
|
||||||
floatingView.findViewById(R.id.floatingContainer).setOnTouchListener(new View.OnTouchListener() {
|
View dragHandle = floatingView.findViewById(R.id.floatingContainer);
|
||||||
|
|
||||||
|
dragHandle.setOnTouchListener(new View.OnTouchListener() {
|
||||||
private int initialX, initialY;
|
private int initialX, initialY;
|
||||||
private float initialTouchX, initialTouchY;
|
private float initialTouchX, initialTouchY;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouch(View v, MotionEvent event) {
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
|
// 检查是否点击在关闭按钮上
|
||||||
|
if (!isViewAttached()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Rect closeButtonRect = new Rect();
|
||||||
|
floatingView.findViewById(R.id.btnClose).getGlobalVisibleRect(closeButtonRect);
|
||||||
|
if (closeButtonRect.contains((int)event.getRawX(), (int)event.getRawY())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否点击在输入区域
|
||||||
|
Rect inputRect = new Rect();
|
||||||
|
actvUnit.getGlobalVisibleRect(inputRect);
|
||||||
|
if (inputRect.contains((int)event.getRawX(), (int)event.getRawY())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
switch (event.getAction()) {
|
switch (event.getAction()) {
|
||||||
case MotionEvent.ACTION_DOWN:
|
case MotionEvent.ACTION_DOWN:
|
||||||
// 检查是否点击在输入区域
|
|
||||||
Rect rect = new Rect();
|
|
||||||
actvUnit.getGlobalVisibleRect(rect);
|
|
||||||
if (rect.contains((int)event.getRawX(), (int)event.getRawY())) {
|
|
||||||
// 如果是点击输入区域,不处理拖动,让输入框获取焦点
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录初始位置
|
|
||||||
initialX = params.x;
|
initialX = params.x;
|
||||||
initialY = params.y;
|
initialY = params.y;
|
||||||
initialTouchX = event.getRawX();
|
initialTouchX = event.getRawX();
|
||||||
initialTouchY = event.getRawY();
|
initialTouchY = event.getRawY();
|
||||||
|
|
||||||
// 隐藏键盘和下拉菜单
|
|
||||||
hideSoftInput(actvUnit);
|
|
||||||
actvUnit.dismissDropDown();
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MotionEvent.ACTION_MOVE:
|
case MotionEvent.ACTION_MOVE:
|
||||||
// 更新悬浮窗位置
|
|
||||||
params.x = initialX + (int)(event.getRawX() - initialTouchX);
|
params.x = initialX + (int)(event.getRawX() - initialTouchX);
|
||||||
params.y = initialY + (int)(event.getRawY() - initialTouchY);
|
params.y = initialY + (int)(event.getRawY() - initialTouchY);
|
||||||
windowManager.updateViewLayout(floatingView, params);
|
windowManager.updateViewLayout(floatingView, params);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MotionEvent.ACTION_UP:
|
case MotionEvent.ACTION_UP:
|
||||||
// 拖动结束,不处理焦点
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupTurbineDropdown() {
|
private void setupTurbineDropdown() {
|
||||||
turbineAdapter = new ArrayAdapter<Turbine>(this,
|
turbineAdapter = new ArrayAdapter<Turbine>(this,
|
||||||
R.layout.dropdown_item, R.id.text1) {
|
R.layout.dropdown_item, R.id.text1) {
|
||||||
|
@ -261,7 +308,6 @@ public class FloatingWindowService extends Service {
|
||||||
TextView textView = view.findViewById(R.id.text1);
|
TextView textView = view.findViewById(R.id.text1);
|
||||||
Turbine turbine = getItem(position);
|
Turbine turbine = getItem(position);
|
||||||
if (turbine != null) {
|
if (turbine != null) {
|
||||||
// 显示更详细的信息
|
|
||||||
textView.setText(String.format(turbine.turbineName));
|
textView.setText(String.format(turbine.turbineName));
|
||||||
textView.setSingleLine(false);
|
textView.setSingleLine(false);
|
||||||
textView.setMaxLines(3);
|
textView.setMaxLines(3);
|
||||||
|
@ -277,17 +323,14 @@ public class FloatingWindowService extends Service {
|
||||||
FilterResults results = new FilterResults();
|
FilterResults results = new FilterResults();
|
||||||
List<Turbine> filtered;
|
List<Turbine> filtered;
|
||||||
if (TextUtils.isEmpty(constraint)) {
|
if (TextUtils.isEmpty(constraint)) {
|
||||||
// 无输入条件时显示全部数据
|
|
||||||
filtered = turbineDao.getTurbinesByProject(projectID);
|
filtered = turbineDao.getTurbinesByProject(projectID);
|
||||||
} else {
|
} else {
|
||||||
// 有输入条件时执行搜索
|
|
||||||
filtered = turbineDao.searchTurbines(projectID, "%" + constraint + "%");
|
filtered = turbineDao.searchTurbines(projectID, "%" + constraint + "%");
|
||||||
}
|
}
|
||||||
|
|
||||||
results.values = filtered;
|
results.values = filtered;
|
||||||
results.count = filtered.size();
|
results.count = filtered.size();
|
||||||
return results;
|
return results;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -308,11 +351,10 @@ public class FloatingWindowService extends Service {
|
||||||
};
|
};
|
||||||
|
|
||||||
actvUnit.setAdapter(turbineAdapter);
|
actvUnit.setAdapter(turbineAdapter);
|
||||||
actvUnit.setThreshold(0); // 输入1个字符后开始搜索
|
actvUnit.setThreshold(0);
|
||||||
actvUnit.setDropDownHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
|
actvUnit.setDropDownHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
actvUnit.setDropDownVerticalOffset(10); // 下拉框与输入框的垂直偏移
|
actvUnit.setDropDownVerticalOffset(10);
|
||||||
|
|
||||||
// 设置输入监听
|
|
||||||
actvUnit.addTextChangedListener(new TextWatcher() {
|
actvUnit.addTextChangedListener(new TextWatcher() {
|
||||||
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
@Override public void afterTextChanged(Editable s) {}
|
@Override public void afterTextChanged(Editable s) {}
|
||||||
|
@ -321,9 +363,8 @@ public class FloatingWindowService extends Service {
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
String input=s.toString().trim();
|
String input=s.toString().trim();
|
||||||
lastUnit = input;
|
lastUnit = input;
|
||||||
lastUnitName = input; // 如果没有匹配项,使用原始输入作为名称
|
lastUnitName = input;
|
||||||
if (turbineAdapter.getCount() > 0 && !TextUtils.isEmpty(input)) {
|
if (turbineAdapter.getCount() > 0 && !TextUtils.isEmpty(input)) {
|
||||||
// 尝试查找完全匹配的项
|
|
||||||
for (int i = 0; i < turbineAdapter.getCount(); i++) {
|
for (int i = 0; i < turbineAdapter.getCount(); i++) {
|
||||||
Turbine turbine = turbineAdapter.getItem(i);
|
Turbine turbine = turbineAdapter.getItem(i);
|
||||||
if (turbine != null && turbine.turbineName.equalsIgnoreCase(input)) {
|
if (turbine != null && turbine.turbineName.equalsIgnoreCase(input)) {
|
||||||
|
@ -333,7 +374,7 @@ public class FloatingWindowService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
checkAndSendUpdate(); // 实时触发检查
|
checkAndSendUpdate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -344,14 +385,11 @@ public class FloatingWindowService extends Service {
|
||||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
// 先检查本地是否有数据
|
|
||||||
List<Turbine> localTurbines = turbineDao.getTurbinesByProject(projectID);
|
List<Turbine> localTurbines = turbineDao.getTurbinesByProject(projectID);
|
||||||
|
|
||||||
if (localTurbines.isEmpty() || isNetworkAvailable()) {
|
if (localTurbines.isEmpty() || isNetworkAvailable()) {
|
||||||
// 如果本地无数据或有网络,尝试从API获取
|
|
||||||
fetchTurbinesFromApi();
|
fetchTurbinesFromApi();
|
||||||
} else {
|
} else {
|
||||||
// 使用本地数据更新UI
|
|
||||||
mainHandler.post(() -> updateTurbineList(localTurbines));
|
mainHandler.post(() -> updateTurbineList(localTurbines));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -359,13 +397,13 @@ public class FloatingWindowService extends Service {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTurbineList(List<Turbine> turbines) {
|
private void updateTurbineList(List<Turbine> turbines) {
|
||||||
turbineAdapter.clear();
|
turbineAdapter.clear();
|
||||||
if (!turbines.isEmpty()) {
|
if (!turbines.isEmpty()) {
|
||||||
turbineAdapter.addAll(turbines);
|
turbineAdapter.addAll(turbines);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果输入框有内容,重新过滤
|
|
||||||
if (!TextUtils.isEmpty(actvUnit.getText())) {
|
if (!TextUtils.isEmpty(actvUnit.getText())) {
|
||||||
turbineAdapter.getFilter().filter(actvUnit.getText());
|
turbineAdapter.getFilter().filter(actvUnit.getText());
|
||||||
}
|
}
|
||||||
|
@ -401,16 +439,14 @@ public class FloatingWindowService extends Service {
|
||||||
List<Turbine> turbines = apiResponse.getData();
|
List<Turbine> turbines = apiResponse.getData();
|
||||||
Log.d("API_SUCCESS", "Fetched " + turbines.size() + " turbines");
|
Log.d("API_SUCCESS", "Fetched " + turbines.size() + " turbines");
|
||||||
|
|
||||||
// 保存到数据库(子线程)
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
turbineDao.deleteByProjectId(projectID);
|
turbineDao.deleteByProjectId(projectID);
|
||||||
for (Turbine turbine : turbines) {
|
for (Turbine turbine : turbines) {
|
||||||
turbine.projectId = projectID; // 确保 projectId 正确
|
turbine.projectId = projectID;
|
||||||
}
|
}
|
||||||
turbineDao.insertAll(turbines);
|
turbineDao.insertAll(turbines);
|
||||||
|
|
||||||
// 使用 mainHandler 更新 UI(主线程)
|
|
||||||
mainHandler.post(() -> {
|
mainHandler.post(() -> {
|
||||||
updateTurbineList(turbines);
|
updateTurbineList(turbines);
|
||||||
showToast("数据加载成功");
|
showToast("数据加载成功");
|
||||||
|
@ -420,19 +456,14 @@ public class FloatingWindowService extends Service {
|
||||||
mainHandler.post(() -> showToast("保存数据失败: " + e.getMessage()));
|
mainHandler.post(() -> showToast("保存数据失败: " + e.getMessage()));
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// API 返回错误(如 code != 200)
|
|
||||||
String errorMsg = "服务器错误: " + apiResponse.getCode() + " - " + apiResponse.getMsg();
|
String errorMsg = "服务器错误: " + apiResponse.getCode() + " - " + apiResponse.getMsg();
|
||||||
Log.e("API_ERROR", errorMsg);
|
Log.e("API_ERROR", errorMsg);
|
||||||
mainHandler.post(() -> showToast(errorMsg));
|
mainHandler.post(() -> showToast(errorMsg));
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// HTTP 状态码非 200(如 404、500)
|
|
||||||
String errorMsg = "请求失败: HTTP " + response.code();
|
String errorMsg = "请求失败: HTTP " + response.code();
|
||||||
if (response.errorBody() != null) {
|
if (response.errorBody() != null) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
errorMsg += "\n" + response.errorBody().string();
|
errorMsg += "\n" + response.errorBody().string();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -447,7 +478,6 @@ public class FloatingWindowService extends Service {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Call<ApiResponse<List<Turbine>>> call, Throwable t) {
|
public void onFailure(Call<ApiResponse<List<Turbine>>> call, Throwable t) {
|
||||||
// 网络请求失败(如无网络、超时)
|
|
||||||
String errorMsg = "网络错误: " + t.getMessage();
|
String errorMsg = "网络错误: " + t.getMessage();
|
||||||
Log.e("NETWORK_ERROR", errorMsg, t);
|
Log.e("NETWORK_ERROR", errorMsg, t);
|
||||||
mainHandler.post(() -> showToast(errorMsg));
|
mainHandler.post(() -> showToast(errorMsg));
|
||||||
|
@ -455,12 +485,11 @@ public class FloatingWindowService extends Service {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示 Toast 的辅助方法
|
|
||||||
private void showToast(String message) {
|
private void showToast(String message) {
|
||||||
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupValueChangeListeners() {
|
private void setupValueChangeListeners() {
|
||||||
// 监听机组号输入框变化
|
|
||||||
actvUnit.addTextChangedListener(new TextWatcher() {
|
actvUnit.addTextChangedListener(new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
@ -469,21 +498,19 @@ public class FloatingWindowService extends Service {
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) {}
|
public void onTextChanged(CharSequence s, int start, int before, int count) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterTextChanged(Editable s) {
|
public void afterTextChanged(Editable s) {}
|
||||||
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
// 添加下拉框项点击监听
|
|
||||||
actvUnit.setOnItemClickListener((parent, view, position, id) -> {
|
actvUnit.setOnItemClickListener((parent, view, position, id) -> {
|
||||||
Turbine selected = turbineAdapter.getItem(position);
|
Turbine selected = turbineAdapter.getItem(position);
|
||||||
if (selected != null) {
|
if (selected != null) {
|
||||||
lastUnit = selected.turbineId;
|
lastUnit = selected.turbineId;
|
||||||
lastUnitName = selected.turbineName;
|
lastUnitName = selected.turbineName;
|
||||||
actvUnit.setText(selected.turbineName); // 显示名称
|
actvUnit.setText(selected.turbineName);
|
||||||
checkAndSendUpdate();
|
checkAndSendUpdate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 监听叶片号选择变化
|
|
||||||
radioGroup.setOnCheckedChangeListener((group, checkedId) -> {
|
radioGroup.setOnCheckedChangeListener((group, checkedId) -> {
|
||||||
int currentBlade = 0;
|
int currentBlade = 0;
|
||||||
if (checkedId != -1) {
|
if (checkedId != -1) {
|
||||||
|
@ -499,7 +526,6 @@ public class FloatingWindowService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkAndSendUpdate() {
|
private void checkAndSendUpdate() {
|
||||||
// 只有当两个值都有有效变化时才发送广播
|
|
||||||
if (!lastUnit.isEmpty() && lastBlade != -1) {
|
if (!lastUnit.isEmpty() && lastBlade != -1) {
|
||||||
sendUpdateBroadcast();
|
sendUpdateBroadcast();
|
||||||
highlightChanges();
|
highlightChanges();
|
||||||
|
@ -510,13 +536,12 @@ public class FloatingWindowService extends Service {
|
||||||
Intent intent = new Intent("com.example.myapplication.UPDATE_VALUES");
|
Intent intent = new Intent("com.example.myapplication.UPDATE_VALUES");
|
||||||
intent.putExtra("unit", lastUnit);
|
intent.putExtra("unit", lastUnit);
|
||||||
intent.putExtra("blade", lastBlade);
|
intent.putExtra("blade", lastBlade);
|
||||||
intent.putExtra("unitName", lastUnitName); // 发送机组名称
|
intent.putExtra("unitName", lastUnitName);
|
||||||
intent.setPackage(getPackageName());
|
intent.setPackage(getPackageName());
|
||||||
sendBroadcast(intent);
|
sendBroadcast(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void highlightChanges() {
|
private void highlightChanges() {
|
||||||
// 高亮效果 - 改变背景色然后恢复
|
|
||||||
int originalColor = actvUnit.getSolidColor();
|
int originalColor = actvUnit.getSolidColor();
|
||||||
actvUnit.setBackgroundColor(Color.YELLOW);
|
actvUnit.setBackgroundColor(Color.YELLOW);
|
||||||
radioGroup.setBackgroundColor(Color.YELLOW);
|
radioGroup.setBackgroundColor(Color.YELLOW);
|
||||||
|
@ -524,7 +549,7 @@ public class FloatingWindowService extends Service {
|
||||||
new Handler().postDelayed(() -> {
|
new Handler().postDelayed(() -> {
|
||||||
actvUnit.setBackgroundColor(originalColor);
|
actvUnit.setBackgroundColor(originalColor);
|
||||||
radioGroup.setBackgroundColor(Color.TRANSPARENT);
|
radioGroup.setBackgroundColor(Color.TRANSPARENT);
|
||||||
}, 300); // 0.3秒后恢复
|
}, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -532,14 +557,26 @@ public class FloatingWindowService extends Service {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
hideSoftInput(actvUnit);
|
hideSoftInput(actvUnit);
|
||||||
actvUnit.dismissDropDown();
|
actvUnit.dismissDropDown();
|
||||||
if (floatingView != null && windowManager != null) {
|
isFloatingWindowShowing = false;
|
||||||
windowManager.removeView(floatingView);
|
|
||||||
}
|
// 直接调用移除方法,不再重复执行stopSelf()
|
||||||
|
removeFloatingView();
|
||||||
|
|
||||||
mainHandler.removeCallbacksAndMessages(null);
|
mainHandler.removeCallbacksAndMessages(null);
|
||||||
}
|
}
|
||||||
|
private boolean isViewAttached() {
|
||||||
|
return floatingView != null
|
||||||
|
&& floatingView.getWindowToken() != null
|
||||||
|
&& windowManager != null;
|
||||||
|
}
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind(Intent intent) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void closeFloatingWindow(Context context) {
|
||||||
|
isFloatingWindowShowing = false;
|
||||||
|
Intent serviceIntent = new Intent(context, FloatingWindowService.class);
|
||||||
|
context.stopService(serviceIntent);
|
||||||
|
}
|
||||||
}
|
}
|
1471
MainActivity.java
1471
MainActivity.java
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,4 @@
|
||||||
package com.example.myapplication.Service;
|
package com.example.myapplication.Service;
|
||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
|
@ -7,6 +6,7 @@ import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
|
@ -23,6 +23,7 @@ import android.provider.MediaStore;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import androidx.room.Room;
|
import androidx.room.Room;
|
||||||
|
|
||||||
|
@ -33,6 +34,8 @@ import com.example.myapplication.Tool.NotificationReceiver;
|
||||||
import com.example.myapplication.model.ImageEntity;
|
import com.example.myapplication.model.ImageEntity;
|
||||||
import com.example.myapplication.model.SharedDataManager;
|
import com.example.myapplication.model.SharedDataManager;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -40,6 +43,7 @@ import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
import okhttp3.MediaType;
|
import okhttp3.MediaType;
|
||||||
import okhttp3.MultipartBody;
|
import okhttp3.MultipartBody;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
|
@ -55,17 +59,19 @@ public class PhotoMonitoringService extends Service implements SharedDataManager
|
||||||
// 锁对象
|
// 锁对象
|
||||||
private final Object monitoringLock = new Object();
|
private final Object monitoringLock = new Object();
|
||||||
private final Object uploadLock = new Object();
|
private final Object uploadLock = new Object();
|
||||||
|
private long lastSuccessToastTime = 0;
|
||||||
|
private static final long TOAST_THROTTLE_INTERVAL = 1500;
|
||||||
|
|
||||||
// 状态标志
|
// 状态标志
|
||||||
private boolean isMonitoring = false;
|
private boolean isMonitoring = false;
|
||||||
private boolean isUploading = false;
|
private boolean isUploading = false;
|
||||||
private boolean isRecording = false;
|
private boolean isRecording = false;
|
||||||
private int successCount = 0;
|
private int successCount = 0;
|
||||||
private int failureCount = 0;
|
private int totalUploadedCount = 0; // 新增:总上传成功计数
|
||||||
|
|
||||||
// 组件
|
// 组件
|
||||||
private ContentObserver contentObserver;
|
private ContentObserver contentObserver;
|
||||||
private final ExecutorService executor = Executors.newFixedThreadPool(6);
|
private final ExecutorService executor = Executors.newFixedThreadPool(5);
|
||||||
private final OkHttpClient httpClient = new OkHttpClient.Builder()
|
private final OkHttpClient httpClient = new OkHttpClient.Builder()
|
||||||
.connectTimeout(30, TimeUnit.SECONDS) // 连接超时
|
.connectTimeout(30, TimeUnit.SECONDS) // 连接超时
|
||||||
.readTimeout(60, TimeUnit.SECONDS) // 读取超时
|
.readTimeout(60, TimeUnit.SECONDS) // 读取超时
|
||||||
|
@ -94,85 +100,94 @@ public class PhotoMonitoringService extends Service implements SharedDataManager
|
||||||
|
|
||||||
// 其他状态
|
// 其他状态
|
||||||
private long lastPhotoId = 0;
|
private long lastPhotoId = 0;
|
||||||
private String currentAudioPath="0";
|
private String currentAudioPath="-1";
|
||||||
private String imgpath="-1";
|
private String imgpath="-1";
|
||||||
private String partid="-1";
|
private String partid="-1";
|
||||||
|
private String UserId;
|
||||||
|
|
||||||
// 回调接口
|
|
||||||
private MonitoringCallback callback;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onImagePathChanged(String newPath) {
|
public void onImageIdChanged(String newId) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTokenChanged(String newToken) {
|
public void onTokenChanged(String newToken) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUserChanged(String newUser) {
|
public void onUserChanged(String newUser) {
|
||||||
user=newUser;
|
user=newUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAudioPathChanged(String newAudioPath) {
|
public void onAudioPathChanged(String newAudioPath) {
|
||||||
currentAudioPath=newAudioPath;
|
currentAudioPath=newAudioPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProjectIdChanged(String newProjectId) {
|
public void onProjectIdChanged(String newProjectId) {
|
||||||
projectId=newProjectId;
|
projectId=newProjectId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUnitChanged(String newUnit) {
|
public void onUnitChanged(String newUnit) {
|
||||||
unit=newUnit;
|
unit=newUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBladeChanged(int newBlade) {
|
public void onBladeChanged(int newBlade) {
|
||||||
blade=newBlade;
|
blade=newBlade;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPartidChanged(String newPartid) {
|
public void onPartidChanged(String newPartid) {
|
||||||
partid=newPartid;
|
partid=newPartid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChooseImageSourceChanged(String newChooseImageSource) {
|
public void onChooseImageSourceChanged(String newChooseImageSource) {
|
||||||
ChooseImageSource=newChooseImageSource;
|
ChooseImageSource=newChooseImageSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUnitNameChanged(String newUnitName) {
|
public void onUnitNameChanged(String newUnitName) {
|
||||||
unitName=newUnitName;
|
unitName=newUnitName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onIsRecordingChanged(boolean newisRecording) {
|
public void onIsRecordingChanged(boolean newisRecording) {
|
||||||
isRecording=newisRecording;
|
isRecording=newisRecording;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUploadingStatusChanged(boolean isUploading) {
|
public void onUploadingStatusChanged(boolean isUploading) {
|
||||||
this.isUploading=isUploading;
|
this.isUploading=isUploading;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface MonitoringCallback {
|
@Override
|
||||||
void onUploadSuccess(String filePath);
|
public void onLocationChanged(Location newLocation) {
|
||||||
void onUploadFailure(String filePath, String error);
|
lastKnownLocation=newLocation;
|
||||||
void onStatusChanged(boolean isMonitoring);
|
|
||||||
}
|
}
|
||||||
SharedDataManager dataManager ;
|
|
||||||
|
@Override
|
||||||
|
public void onOtherMsgChanged(String msg) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedDataManager dataManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
createNotificationChannel();
|
createNotificationChannel();
|
||||||
|
SharedPreferences prefs = getSharedPreferences("WeatherPrefs", MODE_PRIVATE);
|
||||||
|
Weather = prefs.getString("weather", "0");
|
||||||
|
Temperator = prefs.getString("temperature", "0");
|
||||||
|
Humidity = prefs.getString("humidity", "0");
|
||||||
|
SharedPreferences sharedPreferences = getSharedPreferences("LoginPrefs", Context.MODE_PRIVATE);
|
||||||
// 初始化数据库
|
// 初始化数据库
|
||||||
|
UserId=sharedPreferences.getString("userid","-1");
|
||||||
db = Room.databaseBuilder(getApplicationContext(),
|
db = Room.databaseBuilder(getApplicationContext(),
|
||||||
AppDatabase.class, "image-database").build();
|
AppDatabase.class, "image-database").build();
|
||||||
db2 = Room.databaseBuilder(getApplicationContext(),
|
db2 = Room.databaseBuilder(getApplicationContext(),
|
||||||
|
@ -180,13 +195,20 @@ public class PhotoMonitoringService extends Service implements SharedDataManager
|
||||||
|
|
||||||
// 初始化位置管理器
|
// 初始化位置管理器
|
||||||
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
||||||
dataManager=SharedDataManager.getInstance();
|
dataManager=SharedDataManager.getInstance();
|
||||||
|
|
||||||
dataManager.addListener(this);
|
dataManager.addListener(this);
|
||||||
updateFromSharedData();
|
updateFromSharedData();
|
||||||
|
|
||||||
|
// 初始化时从数据库获取已上传成功的数量
|
||||||
|
|
||||||
|
totalUploadedCount = 0;
|
||||||
|
updateNotification("partid"+partid);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateFromSharedData() {
|
private void updateFromSharedData() {
|
||||||
dataManager = SharedDataManager.getInstance();
|
dataManager = SharedDataManager.getInstance();
|
||||||
this.token = dataManager.getToken();
|
this.token = dataManager.getToken();
|
||||||
this.user = dataManager.getUser();
|
this.user = dataManager.getUser();
|
||||||
this.currentAudioPath = dataManager.getAudioPath();
|
this.currentAudioPath = dataManager.getAudioPath();
|
||||||
|
@ -197,10 +219,11 @@ dataManager=SharedDataManager.getInstance();
|
||||||
this.unitName = dataManager.getUnitName();
|
this.unitName = dataManager.getUnitName();
|
||||||
this.partid=dataManager.getPartid();
|
this.partid=dataManager.getPartid();
|
||||||
this.isUploading=dataManager.getisUploading();
|
this.isUploading=dataManager.getisUploading();
|
||||||
|
this.lastKnownLocation=dataManager.getLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
// 从intent中获取配置参数
|
|
||||||
if (intent != null && "start_monitoring".equals(intent.getStringExtra("command"))) {
|
if (intent != null && "start_monitoring".equals(intent.getStringExtra("command"))) {
|
||||||
startMonitoring();
|
startMonitoring();
|
||||||
}
|
}
|
||||||
|
@ -222,9 +245,7 @@ dataManager=SharedDataManager.getInstance();
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 公共接口方法
|
|
||||||
public void startMonitoring() {
|
public void startMonitoring() {
|
||||||
|
|
||||||
synchronized (monitoringLock) {
|
synchronized (monitoringLock) {
|
||||||
if (isMonitoring) return;
|
if (isMonitoring) return;
|
||||||
|
|
||||||
|
@ -247,11 +268,7 @@ dataManager=SharedDataManager.getInstance();
|
||||||
);
|
);
|
||||||
|
|
||||||
isMonitoring = true;
|
isMonitoring = true;
|
||||||
if (callback != null) {
|
|
||||||
callback.onStatusChanged(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查已有照片
|
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
List<ImageEntity> history = db2.imageDao().getAll();
|
List<ImageEntity> history = db2.imageDao().getAll();
|
||||||
if (history != null && !history.isEmpty()) {
|
if (history != null && !history.isEmpty()) {
|
||||||
|
@ -278,25 +295,17 @@ dataManager=SharedDataManager.getInstance();
|
||||||
getContentResolver().unregisterContentObserver(contentObserver);
|
getContentResolver().unregisterContentObserver(contentObserver);
|
||||||
contentObserver = null;
|
contentObserver = null;
|
||||||
}
|
}
|
||||||
if (callback != null) {
|
|
||||||
callback.onStatusChanged(false);
|
|
||||||
}
|
|
||||||
isMonitoring=false;
|
isMonitoring=false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private String getPartName(int b)
|
|
||||||
{
|
private String getPartName(int b) {
|
||||||
return "叶片" +b;
|
return "叶片" +b;
|
||||||
}
|
}
|
||||||
public void setCallback(MonitoringCallback callback) {
|
|
||||||
this.callback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isMonitoring() {
|
|
||||||
return isMonitoring;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 私有方法
|
|
||||||
private void checkNewPhotos() {
|
private void checkNewPhotos() {
|
||||||
String[] projection = {
|
String[] projection = {
|
||||||
MediaStore.Images.Media._ID,
|
MediaStore.Images.Media._ID,
|
||||||
|
@ -334,21 +343,12 @@ dataManager=SharedDataManager.getInstance();
|
||||||
long id = cursor.getLong(0);
|
long id = cursor.getLong(0);
|
||||||
String path = cursor.getString(1);
|
String path = cursor.getString(1);
|
||||||
imgpath = path;
|
imgpath = path;
|
||||||
dataManager.setImagePath(imgpath);
|
|
||||||
long time = cursor.getLong(2) * 1000;
|
long time = cursor.getLong(2) * 1000;
|
||||||
double latitude = cursor.getDouble(3);
|
double latitude = cursor.getDouble(3);
|
||||||
double longitude = cursor.getDouble(4);
|
double longitude = cursor.getDouble(4);
|
||||||
double altitude = 0.0;
|
double altitude = 0.0;
|
||||||
|
|
||||||
try {
|
|
||||||
Location lastLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
|
|
||||||
if (lastLocation != null) {
|
|
||||||
lastKnownLocation = lastLocation;
|
|
||||||
}
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
Log.w("PhotoMonitoringService", "Location permission not granted");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastKnownLocation != null) {
|
if (lastKnownLocation != null) {
|
||||||
latitude = lastKnownLocation.getLatitude();
|
latitude = lastKnownLocation.getLatitude();
|
||||||
longitude = lastKnownLocation.getLongitude();
|
longitude = lastKnownLocation.getLongitude();
|
||||||
|
@ -365,20 +365,17 @@ dataManager=SharedDataManager.getInstance();
|
||||||
Log.e("PhotoMonitoringService", "Error reading EXIF data", e);
|
Log.e("PhotoMonitoringService", "Error reading EXIF data", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
String audioPath = currentAudioPath == null ? "0" : currentAudioPath;
|
String audioPath = currentAudioPath;
|
||||||
lastPhotoId = Math.max(lastPhotoId, id);
|
lastPhotoId = Math.max(lastPhotoId, id);
|
||||||
|
|
||||||
if (isNetworkAvailable() && !isRecording) {
|
if (isNetworkAvailable() && !isRecording) {
|
||||||
uploadNewPhoto(path, time, audioPath, latitude, longitude, altitude);
|
uploadNewPhoto(path, time, audioPath);
|
||||||
} else {
|
} else {
|
||||||
queuePhotoForLater(path, time, audioPath, latitude, longitude, altitude);
|
queuePhotoForLater(path, time, audioPath, latitude, longitude, altitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentAudioPath = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uploadNewPhoto(String path, long time, String audioPath,
|
private void uploadNewPhoto(String path, long time, String audioPath) {
|
||||||
double latitude, double longitude, double altitude) {
|
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
List<ImageEntity> history = db2.imageDao().getAll();
|
List<ImageEntity> history = db2.imageDao().getAll();
|
||||||
if (history != null && !history.isEmpty()) {
|
if (history != null && !history.isEmpty()) {
|
||||||
|
@ -396,7 +393,6 @@ dataManager=SharedDataManager.getInstance();
|
||||||
db2.imageDao().deleteAll();
|
db2.imageDao().deleteAll();
|
||||||
|
|
||||||
String partname = getPartName(blade);
|
String partname = getPartName(blade);
|
||||||
|
|
||||||
if (partid == null) partid = partname;
|
if (partid == null) partid = partname;
|
||||||
|
|
||||||
uploadPhoto(path, time, lastKnownLocation, user,
|
uploadPhoto(path, time, lastKnownLocation, user,
|
||||||
|
@ -408,20 +404,16 @@ dataManager=SharedDataManager.getInstance();
|
||||||
double latitude, double longitude, double altitude) {
|
double latitude, double longitude, double altitude) {
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
String partname = getPartName(blade);
|
String partname = getPartName(blade);
|
||||||
|
|
||||||
if (partid == null) partid = partname;
|
if (partid == null) partid = partname;
|
||||||
|
|
||||||
db2.imageDao().insert(new ImageEntity(path, time, latitude, longitude, altitude,
|
db2.imageDao().insert(new ImageEntity(path, time, latitude, longitude, altitude,
|
||||||
user, audioPath, projectId, unit, blade, false, unitName,
|
user, audioPath, projectId, unit, blade, false, unitName,
|
||||||
Temperator, Humidity, Weather, ChooseImageSource,partid));
|
Temperator, Humidity, Weather, ChooseImageSource,partid));
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uploadPhoto(String filePath, long time, Location location, String user,
|
private void uploadPhoto(String filePath, long time, Location location, String user,
|
||||||
String audioPath, String project, String unit, int blade,
|
String audioPath, String project, String unit, int blade,
|
||||||
String partid, String imageSource, String unitName) {
|
String partId, String imageSource, String unitName) {
|
||||||
synchronized (uploadLock) {
|
synchronized (uploadLock) {
|
||||||
if (isUploading) {
|
if (isUploading) {
|
||||||
queuePhotoForLater(filePath, time, audioPath,
|
queuePhotoForLater(filePath, time, audioPath,
|
||||||
|
@ -430,16 +422,12 @@ dataManager=SharedDataManager.getInstance();
|
||||||
location != null ? location.getAltitude() : 0);
|
location != null ? location.getAltitude() : 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
File imageFile = new File(filePath);
|
File imageFile = new File(filePath);
|
||||||
if (!imageFile.exists()) {
|
if (!imageFile.exists()) {
|
||||||
if (callback != null) {
|
|
||||||
callback.onUploadFailure(filePath, "File does not exist");
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,63 +435,81 @@ dataManager=SharedDataManager.getInstance();
|
||||||
double longitude = location != null ? location.getLongitude() : 0;
|
double longitude = location != null ? location.getLongitude() : 0;
|
||||||
double altitude = location != null ? location.getAltitude() : 0;
|
double altitude = location != null ? location.getAltitude() : 0;
|
||||||
|
|
||||||
MultipartBody.Builder builder = new MultipartBody.Builder()
|
// 构建请求URL
|
||||||
.setType(MultipartBody.FORM)
|
HttpUrl.Builder urlBuilder = HttpUrl.parse("http://pms.dtyx.net:9158/image/" + imageSource + "/upload/" + partId)
|
||||||
.addFormDataPart("image", imageFile.getName(),
|
.newBuilder()
|
||||||
RequestBody.create(imageFile, MediaType.parse("image/*")))
|
.addQueryParameter("collectorId",UserId)
|
||||||
.addFormDataPart("time", String.valueOf(time))
|
.addQueryParameter("collectorName", user)
|
||||||
.addFormDataPart("latitude", String.valueOf(latitude))
|
.addQueryParameter("humidness", String.valueOf(Humidity))
|
||||||
.addFormDataPart("longitude", String.valueOf(longitude))
|
.addQueryParameter("shootingDistance", String.valueOf(0)) // 根据需要设置实际值
|
||||||
.addFormDataPart("altitude", String.valueOf(altitude))
|
.addQueryParameter("temperatureMax", String.valueOf(Temperator))
|
||||||
.addFormDataPart("uploadUser", user)
|
.addQueryParameter("temperatureMin", String.valueOf(Temperator))
|
||||||
.addFormDataPart("projectId", project)
|
.addQueryParameter("weather", Weather)
|
||||||
.addFormDataPart("turbineId", unit)
|
.addQueryParameter("windLevel", String.valueOf(0)); // 根据需要设置实际值
|
||||||
.addFormDataPart("partId", partid)
|
|
||||||
.addFormDataPart("imageSource", imageSource);
|
// 创建请求体
|
||||||
|
RequestBody requestBody = new MultipartBody.Builder()
|
||||||
|
.setType(MultipartBody.FORM)
|
||||||
|
.addFormDataPart("file", imageFile.getName(),
|
||||||
|
RequestBody.create(imageFile, MediaType.parse("image/*")))
|
||||||
|
.build();
|
||||||
|
|
||||||
RequestBody requestBody = builder.build();
|
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url("http://pms.dtyx.net:9158/common/upload-image/file")
|
.url(urlBuilder.build())
|
||||||
.post(requestBody)
|
.post(requestBody)
|
||||||
.addHeader("Authorization", token)
|
.addHeader("Authorization", token)
|
||||||
|
.addHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (Response response = httpClient.newCall(request).execute()) {
|
try (Response response = httpClient.newCall(request).execute()) {
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
|
// 解析响应数据
|
||||||
|
String responseData = response.body().string();
|
||||||
|
JSONObject jsonResponse = new JSONObject(responseData);
|
||||||
|
JSONObject data = jsonResponse.optJSONObject("data");
|
||||||
|
System.out.println(responseData);
|
||||||
|
String imageId = data != null ? data.optString("imageId") : "";
|
||||||
|
dataManager.setImageId(imageId);
|
||||||
|
dataManager.setOthermsg("partid:"+partid+"\n"+"imageid:"+imageId+"\n"+"response:"+responseData);
|
||||||
db.imageDao().insert(new ImageEntity(filePath, time, latitude, longitude, altitude,
|
db.imageDao().insert(new ImageEntity(filePath, time, latitude, longitude, altitude,
|
||||||
user, audioPath, project, unit, blade, true, unitName,
|
user, audioPath, project, unit, blade, true, unitName,
|
||||||
Temperator, Humidity, Weather, ChooseImageSource,partid));
|
Temperator, Humidity, Weather, ChooseImageSource, partId));
|
||||||
|
|
||||||
if (callback != null) {
|
totalUploadedCount++;
|
||||||
successCount++;
|
updateNotification(imageId);
|
||||||
Log.d("上传成功:","已经上传成功"+successCount+"张图片");
|
Intent intent = new Intent("RESPONSE_ACTION");
|
||||||
BackgroundToast.show(this,"已经上传成功"+successCount+"张图片");
|
intent.putExtra("response", response.toString());
|
||||||
|
sendBroadcast(intent);
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
if (currentTime - lastSuccessToastTime > TOAST_THROTTLE_INTERVAL) {
|
||||||
|
BackgroundToast.show(this, "图片上传成功,已上传" + totalUploadedCount + "张");
|
||||||
|
lastSuccessToastTime = currentTime;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
db2.imageDao().insert(new ImageEntity(filePath, time, latitude, longitude, altitude,
|
db2.imageDao().insert(new ImageEntity(filePath, time, latitude, longitude, altitude,
|
||||||
user, audioPath, project, unit, blade, false, unitName,
|
user, audioPath, project, unit, blade, false, unitName,
|
||||||
Temperator, Humidity, Weather, ChooseImageSource,partid));
|
Temperator, Humidity, Weather, ChooseImageSource, partId));
|
||||||
|
dataManager.setImageId("-1");
|
||||||
if (callback != null) {
|
|
||||||
failureCount++;
|
|
||||||
BackgroundToast.show(this,"上传失败"+response.code()+response.message());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (Exception e) {
|
||||||
db2.imageDao().insert(new ImageEntity(filePath, time, 0, 0, 0,
|
db2.imageDao().insert(new ImageEntity(filePath, time, 0, 0, 0,
|
||||||
user, audioPath, project, unit, blade, false, unitName,
|
user, audioPath, project, unit, blade, false, unitName,
|
||||||
Temperator, Humidity, Weather, ChooseImageSource,partid));
|
Temperator, Humidity, Weather, ChooseImageSource, partId));
|
||||||
|
dataManager.setImageId("-1");
|
||||||
if (callback != null) {
|
|
||||||
BackgroundToast.show(this,"io错误"+e.getMessage());
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateNotification(String msg) {
|
||||||
|
String content = "已成功上传 " + totalUploadedCount + " 张照片"+msg;
|
||||||
|
Notification notification = buildNotification("照片上传服务运行中", content);
|
||||||
|
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
if (manager != null) {
|
||||||
|
manager.notify(NOTIFICATION_ID, notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isNetworkAvailable() {
|
private boolean isNetworkAvailable() {
|
||||||
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
if (cm == null) return false;
|
if (cm == null) return false;
|
||||||
|
@ -515,46 +521,44 @@ dataManager=SharedDataManager.getInstance();
|
||||||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET));
|
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void createNotificationChannel() {
|
private void createNotificationChannel() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
NotificationChannel channel = new NotificationChannel(
|
NotificationChannel channel = new NotificationChannel(
|
||||||
CHANNEL_ID,
|
CHANNEL_ID,
|
||||||
"照片上传服务",
|
"照片上传服务",
|
||||||
NotificationManager.IMPORTANCE_HIGH // 使用高优先级确保通知显示
|
NotificationManager.IMPORTANCE_HIGH
|
||||||
);
|
);
|
||||||
channel.setDescription("显示照片上传状态和进度");
|
channel.setDescription("显示照片上传状态和进度");
|
||||||
NotificationManager manager = getSystemService(NotificationManager.class);
|
NotificationManager manager = getSystemService(NotificationManager.class);
|
||||||
manager.createNotificationChannel(channel);
|
manager.createNotificationChannel(channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Notification buildNotification(String title, String content) {
|
private Notification buildNotification(String title, String content) {
|
||||||
return new NotificationCompat.Builder(this, CHANNEL_ID)
|
return new NotificationCompat.Builder(this, CHANNEL_ID)
|
||||||
.setContentTitle(title)
|
.setContentTitle(title)
|
||||||
.setContentText(content)
|
.setContentText(content)
|
||||||
.setSmallIcon(R.drawable.ic_upload) // 使用上传图标
|
.setSmallIcon(R.drawable.ic_upload)
|
||||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
.setOngoing(true) // 设置为持续通知
|
.setOngoing(true)
|
||||||
.setOnlyAlertOnce(true) // 更新时不重复提醒
|
.setOnlyAlertOnce(true)
|
||||||
.addAction(createStopAction())
|
.addAction(createStopAction())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private NotificationCompat.Action createStopAction() {
|
private NotificationCompat.Action createStopAction() {
|
||||||
Intent stopIntent = new Intent(this, NotificationReceiver.class);
|
Intent stopIntent = new Intent(this, NotificationReceiver.class);
|
||||||
stopIntent.setAction("ACTION_STOP_SERVICE");
|
stopIntent.setAction("ACTION_STOP_SERVICE");
|
||||||
// 修改这一行,添加 FLAG_IMMUTABLE 或 FLAG_MUTABLE
|
|
||||||
PendingIntent stopPendingIntent = PendingIntent.getBroadcast(
|
PendingIntent stopPendingIntent = PendingIntent.getBroadcast(
|
||||||
this,
|
this,
|
||||||
0,
|
0,
|
||||||
stopIntent,
|
stopIntent,
|
||||||
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); // 添加 FLAG_IMMUTABLE
|
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
return new NotificationCompat.Action(
|
return new NotificationCompat.Action(
|
||||||
R.drawable.ic_stop, "停止", stopPendingIntent);
|
R.drawable.ic_stop, "停止", stopPendingIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private long getLastPhotoId() {
|
private long getLastPhotoId() {
|
||||||
long id = -1;
|
long id = -1;
|
||||||
Cursor cursor = getContentResolver().query(
|
Cursor cursor = getContentResolver().query(
|
||||||
|
@ -566,10 +570,9 @@ dataManager=SharedDataManager.getInstance();
|
||||||
);
|
);
|
||||||
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
id = cursor.getLong(0); // MAX(_ID) 的结果在索引 0
|
id = cursor.getLong(0);
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -7,6 +7,7 @@ import android.app.Service;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ServiceInfo;
|
import android.content.pm.ServiceInfo;
|
||||||
|
import android.location.Location;
|
||||||
import android.media.MediaRecorder;
|
import android.media.MediaRecorder;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
@ -59,7 +60,7 @@ public class RecordingService extends Service implements SharedDataManager.DataC
|
||||||
private AppDatabase db;
|
private AppDatabase db;
|
||||||
private AppDatabase db2;
|
private AppDatabase db2;
|
||||||
private final ExecutorService executor = Executors.newFixedThreadPool(4);
|
private final ExecutorService executor = Executors.newFixedThreadPool(4);
|
||||||
private String currentImagePath="-1";
|
private String ImageId="-1";
|
||||||
private String currentToken="-1";
|
private String currentToken="-1";
|
||||||
private SharedDataManager dataManager;
|
private SharedDataManager dataManager;
|
||||||
|
|
||||||
|
@ -110,16 +111,17 @@ public class RecordingService extends Service implements SharedDataManager.DataC
|
||||||
|
|
||||||
dataManager=SharedDataManager.getInstance();
|
dataManager=SharedDataManager.getInstance();
|
||||||
dataManager.addListener(this);
|
dataManager.addListener(this);
|
||||||
currentImagePath = dataManager.getImagePath();
|
|
||||||
currentToken = dataManager.getToken();
|
currentToken = dataManager.getToken();
|
||||||
|
ImageId=dataManager.getImageId();
|
||||||
// 在onCreate()中改为使用成员变量观察
|
// 在onCreate()中改为使用成员变量观察
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onImagePathChanged(String newPath) {
|
public void onImageIdChanged(String newId) {
|
||||||
currentImagePath = newPath;
|
ImageId=newId;
|
||||||
Log.d("Service", "ImagePath updated: " + newPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -177,6 +179,16 @@ public class RecordingService extends Service implements SharedDataManager.DataC
|
||||||
public void onUploadingStatusChanged(boolean isUploading) {
|
public void onUploadingStatusChanged(boolean isUploading) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLocationChanged(Location newLocation) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOtherMsgChanged(String msg) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -192,9 +204,10 @@ public class RecordingService extends Service implements SharedDataManager.DataC
|
||||||
}
|
}
|
||||||
return START_REDELIVER_INTENT;
|
return START_REDELIVER_INTENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startRecording() {
|
private void startRecording() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
dataManager.setIsRecording(true);
|
||||||
// 1. 设置录音文件路径
|
// 1. 设置录音文件路径
|
||||||
currentAudioPath = getExternalCacheDir().getAbsolutePath()
|
currentAudioPath = getExternalCacheDir().getAbsolutePath()
|
||||||
+ "/" + System.currentTimeMillis() + ".3gp";
|
+ "/" + System.currentTimeMillis() + ".3gp";
|
||||||
|
@ -283,13 +296,14 @@ public class RecordingService extends Service implements SharedDataManager.DataC
|
||||||
|
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
// 上传成功
|
// 上传成功
|
||||||
db.AudioDao().delete(new AudioEntity(audioPath, imageId, currentTime));
|
executor.execute(()->{ db.AudioDao().deleteByPath(audioPath);
|
||||||
db2.AudioDao().insert(new AudioEntity(audioPath, imageId, currentTime));
|
|
||||||
|
db2.AudioDao().insert(new AudioEntity(audioPath, imageId, currentTime,true));});
|
||||||
BackgroundToast.show(this, "录音上传成功: " + imageId);
|
BackgroundToast.show(this, "录音上传成功: " + imageId);
|
||||||
stopSelf();
|
stopSelf();
|
||||||
} else {
|
} else {
|
||||||
// 上传失败(HTTP 错误)
|
// 上传失败(HTTP 错误)
|
||||||
db.AudioDao().insert(new AudioEntity(audioPath, imageId, currentTime));
|
executor.execute(()->{ db.AudioDao().insert(new AudioEntity(audioPath, imageId, currentTime,false));});
|
||||||
BackgroundToast.show(this,
|
BackgroundToast.show(this,
|
||||||
"录音上传失败: " + response.code() + " " + response.message());
|
"录音上传失败: " + response.code() + " " + response.message());
|
||||||
stopSelf();
|
stopSelf();
|
||||||
|
@ -298,7 +312,10 @@ public class RecordingService extends Service implements SharedDataManager.DataC
|
||||||
// 网络异常
|
// 网络异常
|
||||||
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
|
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
|
||||||
.format(new Date());
|
.format(new Date());
|
||||||
db.AudioDao().insert(new AudioEntity(audioPath, imageId, currentTime));
|
|
||||||
|
executor.execute(() -> {
|
||||||
|
db.AudioDao().insert(new AudioEntity(currentAudioPath, ImageId, currentToken, false));
|
||||||
|
});
|
||||||
BackgroundToast.show(this, "录音上传失败: " + e.getMessage());
|
BackgroundToast.show(this, "录音上传失败: " + e.getMessage());
|
||||||
Log.e("录音上传失败:",e.toString());
|
Log.e("录音上传失败:",e.toString());
|
||||||
stopSelf();
|
stopSelf();
|
||||||
|
@ -314,18 +331,23 @@ public class RecordingService extends Service implements SharedDataManager.DataC
|
||||||
mediaRecorder.release();
|
mediaRecorder.release();
|
||||||
mediaRecorder = null;
|
mediaRecorder = null;
|
||||||
}
|
}
|
||||||
|
dataManager.setIsRecording(false);
|
||||||
// 2. 停止计时器
|
// 2. 停止计时器
|
||||||
if (handler != null && updateTimerRunnable != null) {
|
if (handler != null && updateTimerRunnable != null) {
|
||||||
handler.removeCallbacks(updateTimerRunnable);
|
handler.removeCallbacks(updateTimerRunnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 更新通知
|
// 3. 更新通知
|
||||||
updateNotification("录音已保存");
|
updateNotification("录音已保存");
|
||||||
|
|
||||||
// 4. 上传录音文件(如果有需要)
|
// 4. 上传录音文件(如果有需要)
|
||||||
if (currentAudioPath != null) {
|
if (currentAudioPath != null && ImageId != "-1") {
|
||||||
uploadAudio(currentImagePath, currentAudioPath, currentToken);
|
BackgroundToast.show(this,"开始上传录音了");
|
||||||
|
uploadAudio(ImageId, currentAudioPath, currentToken);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
BackgroundToast.show(this,"Imageid不存在,上传录音失败");
|
||||||
|
executor.execute(() -> {
|
||||||
|
db.AudioDao().insert(new AudioEntity(currentAudioPath, ImageId, currentToken, false));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 不立即停止服务,等待上传完成
|
// 5. 不立即停止服务,等待上传完成
|
||||||
|
@ -333,6 +355,7 @@ public class RecordingService extends Service implements SharedDataManager.DataC
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Toast.makeText(this, "停止录音失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, "停止录音失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
dataManager.setIsRecording(false);
|
||||||
stopSelf();
|
stopSelf();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue