上传文件至 /

This commit is contained in:
XuYuqi 2025-07-25 14:29:27 +08:00
parent 43acbcfd33
commit 4372a7fc16
4 changed files with 973 additions and 957 deletions

View File

@ -1,4 +1,3 @@
package com.example.myapplication.Service;
import android.annotation.SuppressLint;
@ -18,6 +17,8 @@ import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
@ -75,9 +76,11 @@ public class FloatingWindowService extends Service {
private String lastUnitName = "";
private AppDatabase database;
private TurbineDao turbineDao;
private AutoCompleteTextView actvUnit; // 替换原来的EditText
private AutoCompleteTextView actvUnit;
private ArrayAdapter<Turbine> turbineAdapter;
private String projectID;
public static boolean isFloatingWindowShowing = false;
@SuppressLint("SetTextI18n")
@Override
public void onCreate() {
@ -85,19 +88,17 @@ public class FloatingWindowService extends Service {
createNotificationChannel();
database = AppDatabase.getDatabase(this);
turbineDao = database.turbineDao();
createNotificationChannel();
showFloatingWindow();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null && intent.hasExtra(EXTRA_PROJECT_ID)) {
projectID = intent.getStringExtra(EXTRA_PROJECT_ID);
}
return super.onStartCommand(intent, flags, startId);
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
@ -112,6 +113,7 @@ public class FloatingWindowService extends Service {
private void showFloatingWindow() {
// 创建前台服务通知
isFloatingWindowShowing = true;
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("悬浮窗服务运行中")
.setContentText("正在显示悬浮提示框")
@ -120,11 +122,9 @@ public class FloatingWindowService extends Service {
.build();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Android 10+ 需要指定服务类型
startForeground(NOTIFICATION_ID, notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
} else {
// Android 8.0-9.0
startForeground(NOTIFICATION_ID, notification);
}
@ -154,14 +154,57 @@ public class FloatingWindowService extends Service {
setupTurbineDropdown();
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);
setupValueChangeListeners();
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() {
actvUnit.setFocusableInTouchMode(true); // 允许触摸获取焦点
actvUnit.setFocusableInTouchMode(true);
actvUnit.setFocusable(true);
actvUnit.setOnClickListener(v -> {
if (turbineAdapter.getCount() > 0) {
@ -190,6 +233,7 @@ public class FloatingWindowService extends Service {
return false;
});
}
private void showSoftInputDelayed(View view) {
view.postDelayed(() -> {
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
@ -205,52 +249,55 @@ public class FloatingWindowService extends Service {
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
// 移除 setupInputMethod() 方法因为功能已整合到上面的方法中
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 float initialTouchX, initialTouchY;
@Override
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()) {
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;
initialY = params.y;
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
// 隐藏键盘和下拉菜单
hideSoftInput(actvUnit);
actvUnit.dismissDropDown();
return true;
case MotionEvent.ACTION_MOVE:
// 更新悬浮窗位置
params.x = initialX + (int)(event.getRawX() - initialTouchX);
params.y = initialY + (int)(event.getRawY() - initialTouchY);
windowManager.updateViewLayout(floatingView, params);
return true;
case MotionEvent.ACTION_UP:
// 拖动结束不处理焦点
return true;
}
return false;
}
});
}
private void setupTurbineDropdown() {
turbineAdapter = new ArrayAdapter<Turbine>(this,
R.layout.dropdown_item, R.id.text1) {
@ -261,7 +308,6 @@ public class FloatingWindowService extends Service {
TextView textView = view.findViewById(R.id.text1);
Turbine turbine = getItem(position);
if (turbine != null) {
// 显示更详细的信息
textView.setText(String.format(turbine.turbineName));
textView.setSingleLine(false);
textView.setMaxLines(3);
@ -277,17 +323,14 @@ public class FloatingWindowService extends Service {
FilterResults results = new FilterResults();
List<Turbine> filtered;
if (TextUtils.isEmpty(constraint)) {
// 无输入条件时显示全部数据
filtered = turbineDao.getTurbinesByProject(projectID);
} else {
// 有输入条件时执行搜索
filtered = turbineDao.searchTurbines(projectID, "%" + constraint + "%");
}
results.values = filtered;
results.count = filtered.size();
return results;
}
@Override
@ -308,11 +351,10 @@ public class FloatingWindowService extends Service {
};
actvUnit.setAdapter(turbineAdapter);
actvUnit.setThreshold(0); // 输入1个字符后开始搜索
actvUnit.setThreshold(0);
actvUnit.setDropDownHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
actvUnit.setDropDownVerticalOffset(10); // 下拉框与输入框的垂直偏移
actvUnit.setDropDownVerticalOffset(10);
// 设置输入监听
actvUnit.addTextChangedListener(new TextWatcher() {
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@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) {
String input=s.toString().trim();
lastUnit = input;
lastUnitName = input; // 如果没有匹配项使用原始输入作为名称
lastUnitName = input;
if (turbineAdapter.getCount() > 0 && !TextUtils.isEmpty(input)) {
// 尝试查找完全匹配的项
for (int i = 0; i < turbineAdapter.getCount(); i++) {
Turbine turbine = turbineAdapter.getItem(i);
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();
executor.execute(() -> {
try {
// 先检查本地是否有数据
List<Turbine> localTurbines = turbineDao.getTurbinesByProject(projectID);
if (localTurbines.isEmpty() || isNetworkAvailable()) {
// 如果本地无数据或有网络尝试从API获取
fetchTurbinesFromApi();
} else {
// 使用本地数据更新UI
mainHandler.post(() -> updateTurbineList(localTurbines));
}
} catch (Exception e) {
@ -359,13 +397,13 @@ public class FloatingWindowService extends Service {
}
});
}
private void updateTurbineList(List<Turbine> turbines) {
turbineAdapter.clear();
if (!turbines.isEmpty()) {
turbineAdapter.addAll(turbines);
}
// 如果输入框有内容重新过滤
if (!TextUtils.isEmpty(actvUnit.getText())) {
turbineAdapter.getFilter().filter(actvUnit.getText());
}
@ -401,16 +439,14 @@ public class FloatingWindowService extends Service {
List<Turbine> turbines = apiResponse.getData();
Log.d("API_SUCCESS", "Fetched " + turbines.size() + " turbines");
// 保存到数据库子线程
new Thread(() -> {
try {
turbineDao.deleteByProjectId(projectID);
for (Turbine turbine : turbines) {
turbine.projectId = projectID; // 确保 projectId 正确
turbine.projectId = projectID;
}
turbineDao.insertAll(turbines);
// 使用 mainHandler 更新 UI主线程
mainHandler.post(() -> {
updateTurbineList(turbines);
showToast("数据加载成功");
@ -420,19 +456,14 @@ public class FloatingWindowService extends Service {
mainHandler.post(() -> showToast("保存数据失败: " + e.getMessage()));
}
}).start();
} else {
// API 返回错误 code != 200
String errorMsg = "服务器错误: " + apiResponse.getCode() + " - " + apiResponse.getMsg();
Log.e("API_ERROR", errorMsg);
mainHandler.post(() -> showToast(errorMsg));
}
} else {
// HTTP 状态码非 200 404500
String errorMsg = "请求失败: HTTP " + response.code();
if (response.errorBody() != null) {
try {
errorMsg += "\n" + response.errorBody().string();
} catch (IOException e) {
@ -447,7 +478,6 @@ public class FloatingWindowService extends Service {
@Override
public void onFailure(Call<ApiResponse<List<Turbine>>> call, Throwable t) {
// 网络请求失败如无网络超时
String errorMsg = "网络错误: " + t.getMessage();
Log.e("NETWORK_ERROR", errorMsg, t);
mainHandler.post(() -> showToast(errorMsg));
@ -455,12 +485,11 @@ public class FloatingWindowService extends Service {
});
}
// 显示 Toast 的辅助方法
private void showToast(String message) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
private void setupValueChangeListeners() {
// 监听机组号输入框变化
actvUnit.addTextChangedListener(new TextWatcher() {
@Override
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) {}
@Override
public void afterTextChanged(Editable s) {
}
public void afterTextChanged(Editable s) {}
});
// 添加下拉框项点击监听
actvUnit.setOnItemClickListener((parent, view, position, id) -> {
Turbine selected = turbineAdapter.getItem(position);
if (selected != null) {
lastUnit = selected.turbineId;
lastUnitName = selected.turbineName;
actvUnit.setText(selected.turbineName); // 显示名称
actvUnit.setText(selected.turbineName);
checkAndSendUpdate();
}
});
// 监听叶片号选择变化
radioGroup.setOnCheckedChangeListener((group, checkedId) -> {
int currentBlade = 0;
if (checkedId != -1) {
@ -499,7 +526,6 @@ public class FloatingWindowService extends Service {
}
private void checkAndSendUpdate() {
// 只有当两个值都有有效变化时才发送广播
if (!lastUnit.isEmpty() && lastBlade != -1) {
sendUpdateBroadcast();
highlightChanges();
@ -510,13 +536,12 @@ public class FloatingWindowService extends Service {
Intent intent = new Intent("com.example.myapplication.UPDATE_VALUES");
intent.putExtra("unit", lastUnit);
intent.putExtra("blade", lastBlade);
intent.putExtra("unitName", lastUnitName); // 发送机组名称
intent.putExtra("unitName", lastUnitName);
intent.setPackage(getPackageName());
sendBroadcast(intent);
}
private void highlightChanges() {
// 高亮效果 - 改变背景色然后恢复
int originalColor = actvUnit.getSolidColor();
actvUnit.setBackgroundColor(Color.YELLOW);
radioGroup.setBackgroundColor(Color.YELLOW);
@ -524,7 +549,7 @@ public class FloatingWindowService extends Service {
new Handler().postDelayed(() -> {
actvUnit.setBackgroundColor(originalColor);
radioGroup.setBackgroundColor(Color.TRANSPARENT);
}, 300); // 0.3秒后恢复
}, 300);
}
@Override
@ -532,14 +557,26 @@ public class FloatingWindowService extends Service {
super.onDestroy();
hideSoftInput(actvUnit);
actvUnit.dismissDropDown();
if (floatingView != null && windowManager != null) {
windowManager.removeView(floatingView);
}
isFloatingWindowShowing = false;
// 直接调用移除方法不再重复执行stopSelf()
removeFloatingView();
mainHandler.removeCallbacksAndMessages(null);
}
private boolean isViewAttached() {
return floatingView != null
&& floatingView.getWindowToken() != null
&& windowManager != null;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
public static void closeFloatingWindow(Context context) {
isFloatingWindowShowing = false;
Intent serviceIntent = new Intent(context, FloatingWindowService.class);
context.stopService(serviceIntent);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
package com.example.myapplication.Service;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@ -7,6 +6,7 @@ import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.database.Cursor;
import android.location.Location;
@ -23,6 +23,7 @@ import android.provider.MediaStore;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.NotificationCompat;
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.SharedDataManager;
import org.json.JSONObject;
import java.io.File;
import java.io.IOException;
import java.util.List;
@ -40,6 +43,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
@ -55,17 +59,19 @@ public class PhotoMonitoringService extends Service implements SharedDataManager
// 锁对象
private final Object monitoringLock = 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 isUploading = false;
private boolean isRecording = false;
private int successCount = 0;
private int failureCount = 0;
private int totalUploadedCount = 0; // 新增总上传成功计数
// 组件
private ContentObserver contentObserver;
private final ExecutorService executor = Executors.newFixedThreadPool(6);
private final ExecutorService executor = Executors.newFixedThreadPool(5);
private final OkHttpClient httpClient = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS) // 连接超时
.readTimeout(60, TimeUnit.SECONDS) // 读取超时
@ -94,85 +100,94 @@ public class PhotoMonitoringService extends Service implements SharedDataManager
// 其他状态
private long lastPhotoId = 0;
private String currentAudioPath="0";
private String currentAudioPath="-1";
private String imgpath="-1";
private String partid="-1";
private String UserId;
// 回调接口
private MonitoringCallback callback;
@Override
public void onImagePathChanged(String newPath) {
public void onImageIdChanged(String newId) {
}
@Override
public void onTokenChanged(String newToken) {
}
@Override
public void onUserChanged(String newUser) {
user=newUser;
user=newUser;
}
@Override
public void onAudioPathChanged(String newAudioPath) {
currentAudioPath=newAudioPath;
currentAudioPath=newAudioPath;
}
@Override
public void onProjectIdChanged(String newProjectId) {
projectId=newProjectId;
projectId=newProjectId;
}
@Override
public void onUnitChanged(String newUnit) {
unit=newUnit;
unit=newUnit;
}
@Override
public void onBladeChanged(int newBlade) {
blade=newBlade;
blade=newBlade;
}
@Override
public void onPartidChanged(String newPartid) {
partid=newPartid;
partid=newPartid;
}
@Override
public void onChooseImageSourceChanged(String newChooseImageSource) {
ChooseImageSource=newChooseImageSource;
ChooseImageSource=newChooseImageSource;
}
@Override
public void onUnitNameChanged(String newUnitName) {
unitName=newUnitName;
unitName=newUnitName;
}
@Override
public void onIsRecordingChanged(boolean newisRecording) {
isRecording=newisRecording;
isRecording=newisRecording;
}
@Override
public void onUploadingStatusChanged(boolean isUploading) {
this.isUploading=isUploading;
this.isUploading=isUploading;
}
public interface MonitoringCallback {
void onUploadSuccess(String filePath);
void onUploadFailure(String filePath, String error);
void onStatusChanged(boolean isMonitoring);
@Override
public void onLocationChanged(Location newLocation) {
lastKnownLocation=newLocation;
}
SharedDataManager dataManager ;
@Override
public void onOtherMsgChanged(String msg) {
}
SharedDataManager dataManager;
@Override
public void onCreate() {
super.onCreate();
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(),
AppDatabase.class, "image-database").build();
db2 = Room.databaseBuilder(getApplicationContext(),
@ -180,13 +195,20 @@ public class PhotoMonitoringService extends Service implements SharedDataManager
// 初始化位置管理器
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
dataManager=SharedDataManager.getInstance();
dataManager=SharedDataManager.getInstance();
dataManager.addListener(this);
updateFromSharedData();
// 初始化时从数据库获取已上传成功的数量
totalUploadedCount = 0;
updateNotification("partid"+partid);
}
private void updateFromSharedData() {
dataManager = SharedDataManager.getInstance();
dataManager = SharedDataManager.getInstance();
this.token = dataManager.getToken();
this.user = dataManager.getUser();
this.currentAudioPath = dataManager.getAudioPath();
@ -197,10 +219,11 @@ dataManager=SharedDataManager.getInstance();
this.unitName = dataManager.getUnitName();
this.partid=dataManager.getPartid();
this.isUploading=dataManager.getisUploading();
this.lastKnownLocation=dataManager.getLocation();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 从intent中获取配置参数
if (intent != null && "start_monitoring".equals(intent.getStringExtra("command"))) {
startMonitoring();
}
@ -222,9 +245,7 @@ dataManager=SharedDataManager.getInstance();
super.onDestroy();
}
// 公共接口方法
public void startMonitoring() {
synchronized (monitoringLock) {
if (isMonitoring) return;
@ -247,11 +268,7 @@ dataManager=SharedDataManager.getInstance();
);
isMonitoring = true;
if (callback != null) {
callback.onStatusChanged(true);
}
// 检查已有照片
executor.execute(() -> {
List<ImageEntity> history = db2.imageDao().getAll();
if (history != null && !history.isEmpty()) {
@ -278,25 +295,17 @@ dataManager=SharedDataManager.getInstance();
getContentResolver().unregisterContentObserver(contentObserver);
contentObserver = null;
}
if (callback != null) {
callback.onStatusChanged(false);
}
isMonitoring=false;
}
}
private String getPartName(int b)
{
private String getPartName(int b) {
return "叶片" +b;
}
public void setCallback(MonitoringCallback callback) {
this.callback = callback;
}
public boolean isMonitoring() {
return isMonitoring;
}
// 私有方法
private void checkNewPhotos() {
String[] projection = {
MediaStore.Images.Media._ID,
@ -334,21 +343,12 @@ dataManager=SharedDataManager.getInstance();
long id = cursor.getLong(0);
String path = cursor.getString(1);
imgpath = path;
dataManager.setImagePath(imgpath);
long time = cursor.getLong(2) * 1000;
double latitude = cursor.getDouble(3);
double longitude = cursor.getDouble(4);
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) {
latitude = lastKnownLocation.getLatitude();
longitude = lastKnownLocation.getLongitude();
@ -365,20 +365,17 @@ dataManager=SharedDataManager.getInstance();
Log.e("PhotoMonitoringService", "Error reading EXIF data", e);
}
String audioPath = currentAudioPath == null ? "0" : currentAudioPath;
String audioPath = currentAudioPath;
lastPhotoId = Math.max(lastPhotoId, id);
if (isNetworkAvailable() && !isRecording) {
uploadNewPhoto(path, time, audioPath, latitude, longitude, altitude);
uploadNewPhoto(path, time, audioPath);
} else {
queuePhotoForLater(path, time, audioPath, latitude, longitude, altitude);
}
currentAudioPath = null;
}
private void uploadNewPhoto(String path, long time, String audioPath,
double latitude, double longitude, double altitude) {
private void uploadNewPhoto(String path, long time, String audioPath) {
executor.execute(() -> {
List<ImageEntity> history = db2.imageDao().getAll();
if (history != null && !history.isEmpty()) {
@ -396,7 +393,6 @@ dataManager=SharedDataManager.getInstance();
db2.imageDao().deleteAll();
String partname = getPartName(blade);
if (partid == null) partid = partname;
uploadPhoto(path, time, lastKnownLocation, user,
@ -408,20 +404,16 @@ dataManager=SharedDataManager.getInstance();
double latitude, double longitude, double altitude) {
executor.execute(() -> {
String partname = getPartName(blade);
if (partid == null) partid = partname;
db2.imageDao().insert(new ImageEntity(path, time, latitude, longitude, altitude,
user, audioPath, projectId, unit, blade, false, unitName,
Temperator, Humidity, Weather, ChooseImageSource,partid));
});
}
private void uploadPhoto(String filePath, long time, Location location, String user,
String audioPath, String project, String unit, int blade,
String partid, String imageSource, String unitName) {
String partId, String imageSource, String unitName) {
synchronized (uploadLock) {
if (isUploading) {
queuePhotoForLater(filePath, time, audioPath,
@ -430,16 +422,12 @@ dataManager=SharedDataManager.getInstance();
location != null ? location.getAltitude() : 0);
return;
}
}
executor.execute(() -> {
try {
File imageFile = new File(filePath);
if (!imageFile.exists()) {
if (callback != null) {
callback.onUploadFailure(filePath, "File does not exist");
}
return;
}
@ -447,63 +435,81 @@ dataManager=SharedDataManager.getInstance();
double longitude = location != null ? location.getLongitude() : 0;
double altitude = location != null ? location.getAltitude() : 0;
MultipartBody.Builder builder = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("image", imageFile.getName(),
RequestBody.create(imageFile, MediaType.parse("image/*")))
.addFormDataPart("time", String.valueOf(time))
.addFormDataPart("latitude", String.valueOf(latitude))
.addFormDataPart("longitude", String.valueOf(longitude))
.addFormDataPart("altitude", String.valueOf(altitude))
.addFormDataPart("uploadUser", user)
.addFormDataPart("projectId", project)
.addFormDataPart("turbineId", unit)
.addFormDataPart("partId", partid)
.addFormDataPart("imageSource", imageSource);
// 构建请求URL
HttpUrl.Builder urlBuilder = HttpUrl.parse("http://pms.dtyx.net:9158/image/" + imageSource + "/upload/" + partId)
.newBuilder()
.addQueryParameter("collectorId",UserId)
.addQueryParameter("collectorName", user)
.addQueryParameter("humidness", String.valueOf(Humidity))
.addQueryParameter("shootingDistance", String.valueOf(0)) // 根据需要设置实际值
.addQueryParameter("temperatureMax", String.valueOf(Temperator))
.addQueryParameter("temperatureMin", String.valueOf(Temperator))
.addQueryParameter("weather", Weather)
.addQueryParameter("windLevel", String.valueOf(0)); // 根据需要设置实际值
// 创建请求体
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()
.url("http://pms.dtyx.net:9158/common/upload-image/file")
.url(urlBuilder.build())
.post(requestBody)
.addHeader("Authorization", token)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.build();
try (Response response = httpClient.newCall(request).execute()) {
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,
user, audioPath, project, unit, blade, true, unitName,
Temperator, Humidity, Weather, ChooseImageSource,partid));
Temperator, Humidity, Weather, ChooseImageSource, partId));
if (callback != null) {
successCount++;
Log.d("上传成功:","已经上传成功"+successCount+"张图片");
BackgroundToast.show(this,"已经上传成功"+successCount+"张图片");
totalUploadedCount++;
updateNotification(imageId);
Intent intent = new Intent("RESPONSE_ACTION");
intent.putExtra("response", response.toString());
sendBroadcast(intent);
long currentTime = System.currentTimeMillis();
if (currentTime - lastSuccessToastTime > TOAST_THROTTLE_INTERVAL) {
BackgroundToast.show(this, "图片上传成功,已上传" + totalUploadedCount + "");
lastSuccessToastTime = currentTime;
}
} else {
db2.imageDao().insert(new ImageEntity(filePath, time, latitude, longitude, altitude,
user, audioPath, project, unit, blade, false, unitName,
Temperator, Humidity, Weather, ChooseImageSource,partid));
if (callback != null) {
failureCount++;
BackgroundToast.show(this,"上传失败"+response.code()+response.message());
}
Temperator, Humidity, Weather, ChooseImageSource, partId));
dataManager.setImageId("-1");
}
}
} catch (IOException e) {
} catch (Exception e) {
db2.imageDao().insert(new ImageEntity(filePath, time, 0, 0, 0,
user, audioPath, project, unit, blade, false, unitName,
Temperator, Humidity, Weather, ChooseImageSource,partid));
if (callback != null) {
BackgroundToast.show(this,"io错误"+e.getMessage());
}
} finally {
Temperator, Humidity, Weather, ChooseImageSource, partId));
dataManager.setImageId("-1");
}
});
}
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() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm == null) return false;
@ -515,46 +521,44 @@ dataManager=SharedDataManager.getInstance();
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET));
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
"照片上传服务",
NotificationManager.IMPORTANCE_HIGH // 使用高优先级确保通知显示
NotificationManager.IMPORTANCE_HIGH
);
channel.setDescription("显示照片上传状态和进度");
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
}
private Notification buildNotification(String title, String content) {
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(title)
.setContentText(content)
.setSmallIcon(R.drawable.ic_upload) // 使用上传图标
.setSmallIcon(R.drawable.ic_upload)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setOngoing(true) // 设置为持续通知
.setOnlyAlertOnce(true) // 更新时不重复提醒
.setOngoing(true)
.setOnlyAlertOnce(true)
.addAction(createStopAction())
.build();
}
private NotificationCompat.Action createStopAction() {
Intent stopIntent = new Intent(this, NotificationReceiver.class);
stopIntent.setAction("ACTION_STOP_SERVICE");
// 修改这一行添加 FLAG_IMMUTABLE FLAG_MUTABLE
PendingIntent stopPendingIntent = PendingIntent.getBroadcast(
this,
0,
stopIntent,
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); // 添加 FLAG_IMMUTABLE
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
return new NotificationCompat.Action(
R.drawable.ic_stop, "停止", stopPendingIntent);
}
private long getLastPhotoId() {
long id = -1;
Cursor cursor = getContentResolver().query(
@ -566,10 +570,9 @@ dataManager=SharedDataManager.getInstance();
);
if (cursor != null && cursor.moveToFirst()) {
id = cursor.getLong(0); // MAX(_ID) 的结果在索引 0
id = cursor.getLong(0);
cursor.close();
}
return id;
}
}

View File

@ -7,6 +7,7 @@ import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.location.Location;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Handler;
@ -59,7 +60,7 @@ public class RecordingService extends Service implements SharedDataManager.DataC
private AppDatabase db;
private AppDatabase db2;
private final ExecutorService executor = Executors.newFixedThreadPool(4);
private String currentImagePath="-1";
private String ImageId="-1";
private String currentToken="-1";
private SharedDataManager dataManager;
@ -110,16 +111,17 @@ public class RecordingService extends Service implements SharedDataManager.DataC
dataManager=SharedDataManager.getInstance();
dataManager.addListener(this);
currentImagePath = dataManager.getImagePath();
currentToken = dataManager.getToken();
ImageId=dataManager.getImageId();
// 在onCreate()中改为使用成员变量观察
}
@Override
public void onImagePathChanged(String newPath) {
currentImagePath = newPath;
Log.d("Service", "ImagePath updated: " + newPath);
public void onImageIdChanged(String newId) {
ImageId=newId;
}
@Override
@ -177,6 +179,16 @@ public class RecordingService extends Service implements SharedDataManager.DataC
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;
}
private void startRecording() {
try {
dataManager.setIsRecording(true);
// 1. 设置录音文件路径
currentAudioPath = getExternalCacheDir().getAbsolutePath()
+ "/" + System.currentTimeMillis() + ".3gp";
@ -283,13 +296,14 @@ public class RecordingService extends Service implements SharedDataManager.DataC
if (response.isSuccessful()) {
// 上传成功
db.AudioDao().delete(new AudioEntity(audioPath, imageId, currentTime));
db2.AudioDao().insert(new AudioEntity(audioPath, imageId, currentTime));
executor.execute(()->{ db.AudioDao().deleteByPath(audioPath);
db2.AudioDao().insert(new AudioEntity(audioPath, imageId, currentTime,true));});
BackgroundToast.show(this, "录音上传成功: " + imageId);
stopSelf();
} else {
// 上传失败HTTP 错误
db.AudioDao().insert(new AudioEntity(audioPath, imageId, currentTime));
executor.execute(()->{ db.AudioDao().insert(new AudioEntity(audioPath, imageId, currentTime,false));});
BackgroundToast.show(this,
"录音上传失败: " + response.code() + " " + response.message());
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())
.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());
Log.e("录音上传失败:",e.toString());
stopSelf();
@ -314,18 +331,23 @@ public class RecordingService extends Service implements SharedDataManager.DataC
mediaRecorder.release();
mediaRecorder = null;
}
dataManager.setIsRecording(false);
// 2. 停止计时器
if (handler != null && updateTimerRunnable != null) {
handler.removeCallbacks(updateTimerRunnable);
}
// 3. 更新通知
updateNotification("录音已保存");
// 4. 上传录音文件如果有需要
if (currentAudioPath != null) {
uploadAudio(currentImagePath, currentAudioPath, currentToken);
if (currentAudioPath != null && ImageId != "-1") {
BackgroundToast.show(this,"开始上传录音了");
uploadAudio(ImageId, currentAudioPath, currentToken);
} else
{
BackgroundToast.show(this,"Imageid不存在上传录音失败");
executor.execute(() -> {
db.AudioDao().insert(new AudioEntity(currentAudioPath, ImageId, currentToken, false));
});
}
// 5. 不立即停止服务等待上传完成
@ -333,6 +355,7 @@ public class RecordingService extends Service implements SharedDataManager.DataC
} catch (Exception e) {
Toast.makeText(this, "停止录音失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
dataManager.setIsRecording(false);
stopSelf();
}
}