Browse Source

remove service from ra image

Ray Zhang 1 month ago
parent
commit
1e8fac3d98

+ 2 - 1
ApexDrivers/RAUtilsLibrary/build.gradle

@@ -58,7 +58,7 @@ android {
 
 dependencies {
 
-
+    api 'androidx.work:work-runtime:2.9.1'
     implementation 'com.google.android.material:material:1.12.0'
 
 
@@ -91,6 +91,7 @@ dependencies {
 
 //    implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))
     implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.22"))
+    implementation 'androidx.activity:activity:1.8.0'
 }
 
 

+ 25 - 26
ApexDrivers/RAUtilsLibrary/src/main/AndroidManifest.xml

@@ -1,13 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
 
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
     <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
-
     <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 写联系人权限 -->
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
     <uses-permission android:name="android.permission.GET_ACCOUNTS" />
@@ -35,10 +34,19 @@
     <application
         android:allowBackup="true"
         android:supportsRtl="true">
-<!--        <service-->
-<!--            android:name=".service.RAService3"-->
-<!--            android:enabled="true"-->
-<!--            android:exported="true"></service>-->
+        <activity
+            android:name=".newupload.NewTaskActivity"
+            android:theme="@style/Theme.AppCompat.Light"
+            android:exported="false" />
+<!--        <activity-->
+<!--            android:name=".newupload.NewUploadListActivity"-->
+<!--            android:theme="@style/Theme.AppCompat.Light"-->
+<!--            android:exported="false" /> -->
+
+        <!-- <service -->
+        <!-- android:name=".service.RAService3" -->
+        <!-- android:enabled="true" -->
+        <!-- android:exported="true"></service> -->
         <!-- android:theme="@style/AppTheme" -->
         <!-- <service -->
         <!-- android:name=".Service.RAService" -->
@@ -57,14 +65,13 @@
         <receiver
             android:name=".receiver.RABroadcastReceiver"
             android:enabled="true"
-            android:exported="false"></receiver>
+            android:exported="false" />
 
-        <activity android:name=".fileviewer.FileViewerActivity"></activity>
+        <activity android:name=".fileviewer.FileViewerActivity" />
         <activity
             android:name=".zxing.camera.PreferencesActivity"
             android:screenOrientation="portrait" />
-        <activity
-            android:name=".WebActivity"/>
+        <activity android:name=".WebActivity" />
         <activity
             android:name=".zxing.codescanner.CaptureActivity"
             android:clearTaskOnLaunch="true"
@@ -73,19 +80,15 @@
             android:stateNotNeeded="true"
             android:theme="@style/CaptureTheme"
             android:windowSoftInputMode="stateAlwaysHidden" />
-
         <activity
             android:name=".infinitephoto.InfinitePhotoActivity"
             android:theme="@style/Theme.AppCompat.Light.NoActionBar.FullScreen" />
-        <activity android:name=".signature.SignatureActivity"
-            android:theme="@style/Theme.AppCompat.Light"/>
+        <activity
+            android:name=".signature.SignatureActivity"
+            android:theme="@style/Theme.AppCompat.Light" />
         <activity
             android:name=".upload.TaskActivity"
-          android:theme="@style/Theme.AppCompat.Light" />
-
-
-
-
+            android:theme="@style/Theme.AppCompat.Light" />
         <activity
             android:name=".barcodescanner.CaptureActivityNew"
             android:clearTaskOnLaunch="true"
@@ -93,11 +96,7 @@
             android:screenOrientation="sensorLandscape"
             android:stateNotNeeded="true"
             android:theme="@style/Theme.AppCompat.Light.NoActionBar.FullScreen"
+            android:windowSoftInputMode="stateAlwaysHidden" />
+    </application>
 
-
-            android:windowSoftInputMode="stateAlwaysHidden" >
-
-        </activity>
-                </application>
-
-            </manifest>
+</manifest>

+ 247 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/newupload/NewTaskActivity.java

@@ -0,0 +1,247 @@
+package com.usai.redant.rautils.newupload;
+
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.usai.redant.rautils.R;
+import com.usai.redant.rautils.application.RedantApplication;
+import com.usai.redant.rautils.upload.RAUploadManager;
+import com.usai.redant.rautils.utils.Network;
+
+public class NewTaskActivity extends NewUploadListActivity {
+
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+
+
+//        Intent intent = getIntent();
+        registerCellResourceId(R.layout.upload_list_cell);
+
+
+//        Class cls = RedantApplication.getInstance().getServiceClass();
+//        setServiceClass(RedantApplication.getInstance().getService().getClass());
+
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public View dequeueCell(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+
+        return getView(position, convertView, parent);
+    }
+
+    private View getView(final int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+
+        final Bundle item = getItem(position);
+
+        RAUploadManager.TaskStatus istatus = RAUploadManager.TaskStatus.values()[item.getInt("status",0)];
+        String status="";
+        switch(istatus)
+        {
+            case TaskStatusStart:
+                status="uploading";
+                break;
+            case TaskStatusStop:
+                status="stop";
+                break;
+            case TaskStatusError:
+                status="warning";
+                break;
+            case TaskStatusWait:
+                status="waiting";
+                break;
+            case TaskStatusFinish:
+                status="finish";
+                break;
+
+            default:
+                status="warning";
+                break;
+        }
+        double percent = item.getDouble("progress",0.0);//item.getDouble("progress",(double)item.getInt("progress",0));
+        String err = item.getString("msg");
+        String name = item.getString("file","");
+
+        View cell;
+        ViewHolder holder;
+        if (convertView != null) {
+
+            cell = convertView;
+            holder = (ViewHolder)cell.getTag();
+            Log.d("_LIST", "getView: " + holder);
+
+        } else {
+
+            cell = View.inflate(getApplicationContext(), R.layout.upload_list_cell, null);
+            holder = new ViewHolder();
+            holder.name_tv = (TextView) cell.findViewById(R.id.upload_name_tv);
+            holder.progressBar = (ProgressBar) cell.findViewById(R.id.upload_progressBar);
+            holder.state_tv = (TextView) cell.findViewById(R.id.upload_state_tv);
+            holder.progress_tv = (TextView) cell.findViewById(R.id.upload_progress_tv);
+            holder.err_tv = (TextView) cell.findViewById(R.id.upload_err_tv);
+            holder.btn_reload = (ImageButton) cell.findViewById(R.id.btn_reload);
+            cell.setTag(holder);
+        }
+        holder.btn_reload.setImageResource(R.drawable.ic_action_reload);
+        if(istatus!= RAUploadManager.TaskStatus.TaskStatusError&& istatus!= RAUploadManager.TaskStatus.TaskStatusStop)
+        {
+            holder.btn_reload.setVisibility(View.GONE);
+        }
+        else
+        {
+            holder.btn_reload.setVisibility(View.VISIBLE);
+            Log.d("_LIST", "new listener: position" + position);
+            holder.btn_reload.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+
+
+                    Bundle task = getItem(position);
+
+                    Log.d("_LIST", "retry: " + task.getString("file","file is null") +"position" + position);
+
+
+                    ConnectivityManager connManager = (ConnectivityManager) RedantApplication.getInstance().getSystemService(Context.CONNECTIVITY_SERVICE);
+
+//                    NetworkInfo networkInfo = connManager.getActiveNetworkInfo();
+//                    if (networkInfo == null)
+//                    {
+//                        dbgUtil.Logd(
+//                                "Current Network info",
+//                                "can not get Active NetworkInfo!");
+////                                canstart = false;
+//                        Toast toast = Toast.makeText(getApplicationContext(),
+//                                "No available network. Please try upload manually later.",
+//                                Toast.LENGTH_LONG);
+//                        toast.setGravity(Gravity.CENTER, 0, 0);
+//                        toast.show();
+//                        return;
+//                    }
+//                    else
+//                    {
+//                        NetworkInfo.State netState = networkInfo
+//                                .getState();
+//                        if (netState != NetworkInfo.State.CONNECTED)
+//                        {
+//
+//                            dbgUtil.Logd(
+//                                    "Current Network info",
+//                                    "not Connected!State="
+//                                            + netState);
+////                                    canstart = false;
+//
+//                            Toast toast = Toast.makeText(getApplicationContext(),
+//                                    "App network connection. Please try upload manually later.",
+//                                    Toast.LENGTH_LONG);
+//                            toast.setGravity(Gravity.CENTER, 0, 0);
+//                            toast.show();
+//                            return;
+//                        }
+//                        else
+//                        {
+//                            int iconntype = -1;
+//                            iconntype = networkInfo
+//                                    .getType();
+//
+//                            SharedPreferences pref = RedantApplication
+//                                    .getInstance()
+//                                    .getSharedPreferences(
+//                                            "UploadManager",
+//                                            0);
+//                            boolean
+//                                    wifi_only
+//                                    =pref.getBoolean("wifi_only",
+//                                    false);
+//                            if(wifi_only == true
+//                                    && iconntype !=
+//                                    ConnectivityManager.TYPE_WIFI
+//                                    && iconntype !=
+//                                    9/* earthnet */)
+//                            {
+////                                    canstart = false;
+//
+//                                Toast toast = Toast.makeText(getApplicationContext(),
+//                                        "App is set to upload via WIFI only. Please change your setting or try upload manually later.",
+//                                        Toast.LENGTH_LONG);
+//                                toast.setGravity(Gravity.CENTER, 0, 0);
+//                                toast.show();
+//                                return;
+//                            }
+//                        }
+//
+//                    }
+                    if(Network.isNetworkAvailableToast(RedantApplication.getInstance()))
+                    {
+                        Boolean bwifi=Network.isNetworkWifi(RedantApplication.getInstance());
+
+                        SharedPreferences pref = RedantApplication
+                                .getInstance()
+                                .getSharedPreferences(
+                                        "UploadManager",
+                                        0);
+                        boolean
+                                wifi_only
+                                =pref.getBoolean("wifi_only",
+                                false);
+                        if(wifi_only
+                                && !bwifi)
+                        {
+//                                    canstart = false;
+
+                            Toast toast = Toast.makeText(getApplicationContext(),
+                                    "App is set to upload via WIFI only. Please change your setting or try upload manually later.",
+                                    Toast.LENGTH_LONG);
+                            toast.setGravity(Gravity.CENTER, 0, 0);
+                            toast.show();
+                            return;
+                        }
+                    }
+                    v.setVisibility(View.GONE);
+                    startTask(task);
+                }
+            });
+        }
+
+
+
+        holder.name_tv.setText(name);
+//            holder.progressBar.setProgress(100 *(int)percent);
+        holder.progressBar.setProgress((int) (100 * percent));
+        holder.state_tv.setText(status);
+//            holder.progress_tv.setText(100*percent+"%");
+        holder.progress_tv.setText(String.format("%.2f%%",100*percent));
+        holder.err_tv.setText(err);
+
+
+
+        Log.d("_LIST", "getView(): POSITION   "+position+" file   " + item.getString("file","no file"));
+        return cell;
+    }
+
+    private class ViewHolder {
+        public TextView name_tv;
+        public ProgressBar progressBar;
+        public TextView state_tv;
+        public TextView progress_tv;
+        public TextView err_tv;
+        public ImageButton btn_reload;
+    }
+
+
+
+}

+ 270 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/newupload/NewUploadListActivity.java

@@ -0,0 +1,270 @@
+package com.usai.redant.rautils.newupload;
+
+import static com.usai.redant.rautils.upload.RAUploadManager.UPLOAD_COUNT_CHANGED_NOTIFICATION;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Insets;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import androidx.annotation.LayoutRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.usai.redant.rautils.R;
+import com.usai.redant.rautils.upload.RAUploadManager;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class NewUploadListActivity extends AppCompatActivity {
+
+    ArrayList<Bundle> task_arr = new ArrayList<>();
+    ListView taskList;
+
+    private RAUploadManager uploadManager;
+
+    // [改动] 删除了以下字段:
+    // - RAService3.Service3Binder binder
+    // - RAService3 uploadService
+    // - ServiceConnection serviceConnection
+    // - boolean serviceConnectionFlag
+
+    private int mUploadCellResID;
+    private Context mCtx = this;
+
+    private BroadcastReceiver uploadReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+
+            String action = intent.getAction();
+            if (action.equals(UPLOAD_COUNT_CHANGED_NOTIFICATION)) {
+
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        notifyDataChanged();
+                    }
+                });
+            }
+        }
+    };
+
+    public void notifyDataChanged() {
+
+        if (uploadManager != null && taskList != null) {
+            task_arr = new ArrayList<>(uploadManager.get_arr_queue());
+            Collections.reverse(task_arr);  // [改动] 保持与原来 onServiceConnected 中一致的倒序
+            TaskAdapter adapter = (TaskAdapter) taskList.getAdapter();
+            if (adapter != null) {
+                adapter.notifyDataSetChanged();
+            }
+        }
+    }
+
+    private void registerBroadcastReceiver() {
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(UPLOAD_COUNT_CHANGED_NOTIFICATION);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+            registerReceiver(uploadReceiver, intentFilter, RECEIVER_NOT_EXPORTED);
+        } else {
+            registerReceiver(uploadReceiver, intentFilter);
+        }
+    }
+
+    /**
+     * subClass invoke
+     */
+
+    public Bundle getItem(int position) {
+        Bundle item = task_arr.get(position);
+        return item;
+    }
+
+    public void startTask(Bundle task) {
+        if (task != null && uploadManager != null) {
+            uploadManager.startTask(task);
+        }
+    }
+
+    public void registerCellResourceId(int resId) {
+        mUploadCellResID = resId;
+    }
+
+    public View dequeueCell(final int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+        return null;
+    }
+
+    /***/
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.upload_list_activity);
+        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
+
+            ViewGroup c = findViewById(R.id.container_root);
+            c.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+                @NonNull
+                @Override
+                public WindowInsets onApplyWindowInsets(@NonNull View view, @NonNull WindowInsets windowInsets) {
+                    Insets innerPadding = null;
+
+                    innerPadding = windowInsets.getInsets(WindowInsets.Type.statusBars());
+
+                    view.setPadding(
+                            innerPadding.left,
+                            innerPadding.top,
+                            innerPadding.right,
+                            innerPadding.bottom);
+
+                    return windowInsets;
+                }
+            });
+        }
+
+        // 设置返回键
+        ActionBar mActionBar = getSupportActionBar();
+        mActionBar.setHomeButtonEnabled(true);
+        mActionBar.setDisplayHomeAsUpEnabled(true);
+        mActionBar.setTitle(getString(R.string.ra_upload_title));
+
+        taskList = (ListView) findViewById(R.id.task_list);
+        taskList.setAdapter(new TaskAdapter(this, mUploadCellResID));
+
+        // [改动] 直接获取 RAUploadManager 单例,替代 bindService
+        uploadManager = RAUploadManager.sharedManager(getApplicationContext());
+
+        if (uploadManager != null) {
+            task_arr = new ArrayList<>(uploadManager.get_arr_queue());
+            Collections.reverse(task_arr);
+
+            final TaskAdapter adapter = (TaskAdapter) taskList.getAdapter();
+            adapter.notifyDataSetChanged();
+
+            uploadManager.uiUpdateListener = new RAUploadManager.UIUpdateListener() {
+                @Override
+                public void updateCell(long index, Bundle taskinfo) {
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            adapter.notifyDataSetChanged();
+                        }
+                    });
+                }
+
+                @Override
+                public void updateList(final ArrayList<Bundle> newlist) {
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            task_arr = new ArrayList<>(newlist);
+                            Collections.reverse(task_arr);
+                            adapter.notifyDataSetChanged();
+                        }
+                    });
+                }
+            };
+        }
+
+        registerBroadcastReceiver();
+    }
+
+    @Override
+    protected void onDestroy() {
+        // [改动] 删除 unbindService(serviceConnection)
+        // 清除 UI 监听,防止 Activity 销毁后回调
+        if (uploadManager != null) {
+            uploadManager.uiUpdateListener = null;
+        }
+
+        unregisterReceiver(uploadReceiver);
+
+        super.onDestroy();
+    }
+
+    // [改动] 删除了 bindService() 方法
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.ra_upload_list_menu, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        int i = item.getItemId();
+        if (i == android.R.id.home) {
+            finish();
+
+        } else if (i == R.id.clear_upload_list_btn) {
+            clearUploadList();
+
+        }
+        return true;
+    }
+
+    private void clearUploadList() {
+        if (task_arr.size() == 0) {
+            new AlertDialog.Builder(this)
+                    .setTitle(getString(R.string.ra_title_warning))
+                    .setMessage(getString(R.string.ra_upload_empty))
+                    .setPositiveButton(getString(R.string.ra_btn_ok), null)
+                    .show();
+            return;
+        }
+
+        new AlertDialog.Builder(this)
+                .setTitle(getString(R.string.ra_clear_title))
+                .setMessage(getString(R.string.ra_clear_ask))
+                .setPositiveButton(getString(R.string.ra_btn_ok), new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        // [改动] 不再检查 serviceConnectionFlag,直接用 uploadManager
+                        if (uploadManager != null) {
+                            uploadManager.clearTask();
+                        }
+                    }
+                })
+                .setNegativeButton(getString(R.string.ra_btn_cancel), new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+
+                    }
+                })
+                .show();
+    }
+
+    class TaskAdapter extends ArrayAdapter {
+
+        public TaskAdapter(@NonNull Context context, @LayoutRes int resource) {
+            super(context, resource);
+        }
+
+        @Override
+        public int getCount() {
+            return task_arr.size();
+        }
+
+        @Override
+        public View getView(final int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+            return dequeueCell(position, convertView, parent);
+        }
+    }
+}

+ 220 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/newupload/RAUploadWorker.java

@@ -0,0 +1,220 @@
+package com.usai.redant.rautils.newupload;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.work.Constraints;
+import androidx.work.ExistingPeriodicWorkPolicy;
+import androidx.work.NetworkType;
+import androidx.work.OneTimeWorkRequest;
+import androidx.work.PeriodicWorkRequest;
+import androidx.work.WorkManager;
+import androidx.work.Worker;
+import androidx.work.WorkerParameters;
+
+import com.usai.redant.rautils.upload.RAUploadManager;
+
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 用 WorkManager 替代 RAImageBackgroundService3 前台服务。
+ *
+ * 解决 Android 15 对 dataSync 前台服务 6 小时超时限制导致的
+ * ForegroundServiceDidNotStopInTimeException 崩溃问题。
+ *
+ * 使用方式:
+ * 1. Application.onCreate() 或登录成功后调用 RAUploadWorker.schedulePeriodic(context)
+ * 2. 拍照/添加上传任务后调用 RAUploadWorker.triggerNow(context)
+ * 3. 删除 RAImageBackgroundService3、RATask3Upload、RAImageBootCompleteReceiver
+ * 4. 删除 Manifest 中对应的 <service> 和 <receiver> 声明
+ * 5. 删除 FOREGROUND_SERVICE_DATA_SYNC、REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 权限
+ */
+public class RAUploadWorker extends Worker {
+
+
+
+    private static Constraints buildConstraints(@NonNull Context context) {
+        SharedPreferences pref = context.getApplicationContext()
+                .getSharedPreferences("UploadManager", 0);
+        boolean wifiOnly = pref.getBoolean("wifi_only", false);
+
+        return new Constraints.Builder()
+                .setRequiredNetworkType(wifiOnly
+                        ? NetworkType.UNMETERED
+                        : NetworkType.CONNECTED)
+                .build();
+    }
+
+
+    private static final String TAG = "RAUploadWorker";
+
+    /**
+     * WorkManager 唯一任务名称
+     */
+    private static final String UNIQUE_PERIODIC_WORK = "raimage_upload_periodic";
+
+    public RAUploadWorker(@NonNull Context context, @NonNull WorkerParameters params) {
+        super(context, params);
+    }
+
+    @NonNull
+    @Override
+    public Result doWork() {
+        Log.d(TAG, "doWork: start");
+
+        try {
+            Context appCtx = getApplicationContext();
+
+            // 1. 初始化 RAUploadManager 单例
+            //    如果进程被杀过重启,sharedManager() 内部会重新创建实例,
+            //    构造函数中的 loadTask() 会从 SharedPreferences 恢复任务队列,
+            //    并自动 startTask() 所有未完成的任务。
+            RAUploadManager manager = RAUploadManager.sharedManager(appCtx);
+
+            // 2. 配置(与原 RAImageBackgroundService3.onCreate 中的配置一致)
+            RAUploadManager.configureUploadManager(appCtx, new RAUploadManager.configureBlock() {
+                @Override
+                public void configure(RAUploadManager.RAUploadManagerConfigure configure) {
+                    configure.removeFinish = true;
+                }
+            });
+
+            // 3. 检查并重启所有处于 Wait 或 Stop 状态的任务
+            //    覆盖以下场景:
+            //    - 网络恢复后 WorkManager 触发,但 ConnectionStateMonitor 还未回调
+            //    - 进程未被杀,但任务因网络断开被 stop 了
+            //    - 任务在等待重试期间进程被杀,重启后需要重新启动
+            ArrayList<Bundle> queue = manager.get_arr_queue();
+            if (queue != null && !queue.isEmpty()) {
+                int restartCount = 0;
+                for (Bundle task : queue) {
+                    int status = task.getInt("status", 0);
+                    if (status == RAUploadManager.TaskStatus.TaskStatusWait.ordinal()
+                            || status == RAUploadManager.TaskStatus.TaskStatusStop.ordinal()) {
+                        manager.startTask(task);
+                        restartCount++;
+                    }
+                }
+                Log.d(TAG, "doWork: queue size=" + queue.size()
+                        + ", restarted=" + restartCount);
+            } else {
+                Log.d(TAG, "doWork: queue is empty, nothing to do");
+            }
+
+        } catch (Exception e) {
+            Log.e(TAG, "doWork: error", e);
+            // 返回 retry,WorkManager 会按退避策略重试
+            return Result.retry();
+        }
+
+        Log.d(TAG, "doWork: done");
+        return Result.success();
+    }
+
+    // ========================================================================
+    // 静态调度方法 - 替代原来的 startService / bindService
+    // ========================================================================
+
+    /**
+     * 注册周期性上传任务(每 15 分钟检查一次)。
+     * 在 Application.onCreate() 或用户登录成功后调用一次即可。
+     * WorkManager 自动跨进程重启和设备重启持久化,不需要 BOOT_COMPLETED。
+     *
+     * @param context Application 或 Activity context
+     */
+    public static void schedulePeriodic(@NonNull Context context) {
+//        Constraints constraints = new Constraints.Builder()
+//                .setRequiredNetworkType(NetworkType.CONNECTED)  // 有网才执行
+//                .build();
+//
+//        PeriodicWorkRequest request = new PeriodicWorkRequest.Builder(
+//                RAUploadWorker.class,
+//                15, TimeUnit.MINUTES)          // 最小周期 15 分钟
+//                .setConstraints(constraints)
+//                .build();
+//
+//        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
+//                UNIQUE_PERIODIC_WORK,
+//                ExistingPeriodicWorkPolicy.KEEP,   // 已存在则保留,不重复创建
+//                request);
+
+
+
+
+
+            PeriodicWorkRequest request = new PeriodicWorkRequest.Builder(
+                    RAUploadWorker.class,
+                    15, TimeUnit.MINUTES)
+                    .setConstraints(buildConstraints(context))  // 用公共方法
+                    .build();
+
+            WorkManager.getInstance(context).enqueueUniquePeriodicWork(
+                    UNIQUE_PERIODIC_WORK,
+                    ExistingPeriodicWorkPolicy.UPDATE,
+                    request);
+
+
+        Log.d(TAG, "schedulePeriodic: registered");
+    }
+
+    /**
+     * 立即触发一次上传。
+     * 适用于拍照后、手动添加任务后,需要尽快上传的场景。
+     * 如果当前无网络,WorkManager 会等到网络恢复后自动执行。
+     *
+     * @param context Application 或 Activity context
+     */
+    public static void triggerNow(@NonNull Context context) {
+//        Constraints constraints = new Constraints.Builder()
+//                .setRequiredNetworkType(NetworkType.CONNECTED)
+//                .build();
+//
+//        OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(RAUploadWorker.class)
+//                .setConstraints(constraints)
+//                .build();
+//
+//        WorkManager.getInstance(context).enqueue(request);
+
+
+        OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(RAUploadWorker.class)
+                .setConstraints(buildConstraints(context))  // 同样读设置
+                .build();
+
+        WorkManager.getInstance(context).enqueue(request);
+
+
+        Log.d(TAG, "triggerNow: enqueued");
+    }
+
+    /**
+     * 通过广播添加上传任务并立即触发上传。
+     * 替代原来通过 Service 中的 BroadcastReceiver 添加任务的方式。
+     * 可以直接在需要添加任务的地方调用此方法,不再需要发广播。
+     *
+     * @param context Application 或 Activity context
+     * @param tasks   要添加的上传任务列表
+     */
+    public static void addTasksAndUpload(@NonNull Context context, @NonNull ArrayList<Bundle> tasks) {
+        RAUploadManager manager = RAUploadManager.sharedManager(context.getApplicationContext());
+        if (manager != null) {
+            manager.addTasks(tasks);
+        }
+        // 立即触发一次 Worker 确保上传启动
+        triggerNow(context);
+    }
+
+    /**
+     * 取消所有周期性上传任务。
+     * 在用户登出或不再需要后台上传时调用。
+     *
+     * @param context Application 或 Activity context
+     */
+    public static void cancelAll(@NonNull Context context) {
+        WorkManager.getInstance(context).cancelUniqueWork(UNIQUE_PERIODIC_WORK);
+        Log.d(TAG, "cancelAll: cancelled periodic work");
+    }
+}

+ 464 - 0
ApexDrivers/RAUtilsLibrary/src/main/java/com/usai/redant/rautils/utils/RACrashHandler28.java

@@ -0,0 +1,464 @@
+package com.usai.redant.rautils.utils;
+
+
+
+
+import android.app.ActivityManager;
+import android.app.Application;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Process;
+import android.provider.MediaStore;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * 崩溃捕获工具类 (Min SDK 28+)
+ *
+ * 功能特性:
+ * 1. 自动捕获未处理异常
+ * 2. 无需运行时权限即可写入日志
+ * 3. 智能存储策略:
+ *    - Android 10+ (Q): Downloads/CrashLogs (无需权限,其他App可见,卸载保留)
+ *    - Android 9 (P):   ExternalFiles/CrashLogs (无需权限,PC/文件管理器可见)
+ *
+ * 使用方法:
+ * Application.onCreate() 中调用 CrashHandler.init(this);
+ */
+public class RACrashHandler28 implements Thread.UncaughtExceptionHandler {
+
+    private static final String TAG = "CrashHandler";
+    private static final String CRASH_DIR = "CrashLogs";
+    private static final String CRASH_FILE_PREFIX = "crash_";
+    private static final String CRASH_FILE_SUFFIX = ".txt";
+
+    private static volatile RACrashHandler28 sInstance;
+    private Application mApplication;
+    private Thread.UncaughtExceptionHandler mDefaultHandler;
+
+    // 配置项
+    private boolean mKillProcessAfterCrash = true;
+    private CrashCallback mCrashCallback;
+
+    // 【修复1】添加 volatile,保证多线程可见性
+    private volatile boolean mIsCrashing = false;
+
+    public interface CrashCallback {
+        void onCrash(@Nullable String crashFile, @NonNull Throwable throwable);
+    }
+
+    private RACrashHandler28() {}
+
+    public static void init(@NonNull Application application) {
+        if (sInstance == null) {
+            synchronized (RACrashHandler28.class) {
+                if (sInstance == null) {
+                    sInstance = new RACrashHandler28();
+                    sInstance.setup(application);
+                }
+            }
+        }
+    }
+
+    public static RACrashHandler28 getInstance() {
+        if (sInstance == null) {
+            throw new IllegalStateException("CrashHandler not initialized. Call init() first.");
+        }
+        return sInstance;
+    }
+
+    public RACrashHandler28 setKillProcessAfterCrash(boolean kill) {
+        mKillProcessAfterCrash = kill;
+        return this;
+    }
+
+    public RACrashHandler28 setCrashCallback(CrashCallback callback) {
+        mCrashCallback = callback;
+        return this;
+    }
+
+    private void setup(Application application) {
+        mApplication = application;
+        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+        Thread.setDefaultUncaughtExceptionHandler(this);
+        Log.i(TAG, "CrashHandler initialized (MinSDK 28)");
+    }
+
+    @Override
+    public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) {
+        // 防死循环:如果正在处理崩溃,直接交给系统默认处理
+        if (mIsCrashing) {
+            if (mDefaultHandler != null) {
+                mDefaultHandler.uncaughtException(thread, throwable);
+            }
+            return;
+        }
+        mIsCrashing = true;
+
+        String crashFilePath = null;
+
+        try {
+            String crashInfo = collectCrashInfo(thread, throwable);
+            crashFilePath = saveCrashLog(crashInfo);
+            safeLog("Crash log saved: " + crashFilePath);
+        } catch (Throwable e) {
+            safeLog("Failed to save crash log: " + e.getMessage());
+        }
+
+        // 回调通知
+        if (mCrashCallback != null) {
+            try {
+                mCrashCallback.onCrash(crashFilePath, throwable);
+            } catch (Throwable e) {
+                safeLog("CrashCallback error: " + e.getMessage());
+            }
+        }
+
+        // 交给默认处理器或杀进程
+        if (mKillProcessAfterCrash) {
+            if (mDefaultHandler != null) {
+                mDefaultHandler.uncaughtException(thread, throwable);
+            } else {
+                Process.killProcess(Process.myPid());
+                System.exit(1);
+            }
+        }
+    }
+
+    // ==================== 信息收集 ====================
+
+    private String collectCrashInfo(Thread thread, Throwable throwable) {
+        // 【优化】预分配容量,减少扩容
+        StringBuilder sb = new StringBuilder(4096);
+        // 【修复3】使用 Locale.US 确保日期格式一致,避免特殊字符
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
+
+        sb.append("═══════════════════════════════════════════════════════════════\n");
+        sb.append("                        CRASH REPORT                           \n");
+        sb.append("═══════════════════════════════════════════════════════════════\n\n");
+
+        // 1. 时间
+        sb.append("【Time】\n");
+        sb.append("Crash Time: ").append(sdf.format(new Date())).append("\n");
+        sb.append("Timezone: ").append(java.util.TimeZone.getDefault().getID()).append("\n\n");
+
+        // 2. 应用信息
+        sb.append("【Application】\n");
+        try {
+            PackageInfo pi = mApplication.getPackageManager()
+                    .getPackageInfo(mApplication.getPackageName(), 0);
+            sb.append("Package: ").append(pi.packageName).append("\n");
+            sb.append("Version: ").append(pi.versionName)
+                    .append(" (").append(pi.getLongVersionCode()).append(")\n");
+        } catch (Exception e) {
+            sb.append("Failed to get app info\n");
+        }
+        sb.append("\n");
+
+        // 3. 进程/线程
+        sb.append("【Process & Thread】\n");
+        sb.append("PID: ").append(Process.myPid()).append("\n");
+        sb.append("Process: ").append(Application.getProcessName()).append("\n");
+        sb.append("Thread: ").append(thread.getName())
+                .append(" (id=").append(thread.getId())
+                .append(", priority=").append(thread.getPriority()).append(")\n\n");
+
+        // 4. 设备信息
+        sb.append("【Device】\n");
+        sb.append("Brand: ").append(Build.BRAND).append("\n");
+        sb.append("Model: ").append(Build.MODEL).append("\n");
+        sb.append("Manufacturer: ").append(Build.MANUFACTURER).append("\n");
+        sb.append("Android: ").append(Build.VERSION.RELEASE)
+                .append(" (API ").append(Build.VERSION.SDK_INT).append(")\n");
+        sb.append("ABI: ").append(String.join(", ", Build.SUPPORTED_ABIS)).append("\n");
+        sb.append("Fingerprint: ").append(Build.FINGERPRINT).append("\n\n");
+
+        // 5. 内存信息
+        sb.append("【Memory】\n");
+        Runtime runtime = Runtime.getRuntime();
+        long maxMB = runtime.maxMemory() >> 20;
+        long totalMB = runtime.totalMemory() >> 20;
+        long freeMB = runtime.freeMemory() >> 20;
+        sb.append("Heap: ").append(totalMB - freeMB).append("/").append(maxMB).append(" MB\n");
+
+        try {
+            ActivityManager am = (ActivityManager) mApplication.getSystemService(Context.ACTIVITY_SERVICE);
+            ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
+            am.getMemoryInfo(memInfo);
+            sb.append("System: ").append(memInfo.availMem >> 20).append("/").append(memInfo.totalMem >> 20).append(" MB");
+            if (memInfo.lowMemory) {
+                sb.append(" [LOW MEMORY!]");
+            }
+            sb.append("\n");
+        } catch (Exception ignored) {}
+        sb.append("\n");
+
+        // 6. 异常堆栈
+        sb.append("═══════════════════════════════════════════════════════════════\n");
+        sb.append("                     EXCEPTION DETAILS                         \n");
+        sb.append("═══════════════════════════════════════════════════════════════\n\n");
+
+        sb.append("Type: ").append(throwable.getClass().getName()).append("\n");
+        sb.append("Message: ").append(throwable.getMessage()).append("\n\n");
+
+        sb.append("【Stack Trace】\n");
+        // 【修复2】printStackTrace 已包含完整 cause 链,无需再手动遍历
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        throwable.printStackTrace(pw);
+        sb.append(sw.toString());
+
+        sb.append("\n═══════════════════════════════════════════════════════════════\n");
+        sb.append("                        END OF REPORT                          \n");
+        sb.append("═══════════════════════════════════════════════════════════════\n");
+
+        return sb.toString();
+    }
+
+    // ==================== 文件存储 ====================
+
+    private String saveCrashLog(String crashInfo) {
+        // 【修复3】文件名也使用 Locale.US
+        String timestamp = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.US).format(new Date());
+        String fileName = CRASH_FILE_PREFIX + timestamp + CRASH_FILE_SUFFIX;
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            return saveToDownloads(fileName, crashInfo);
+        } else {
+            return saveToExternalFiles(fileName, crashInfo);
+        }
+    }
+
+    /**
+     * Android 10+ 写入 Downloads/CrashLogs
+     */
+    private String saveToDownloads(String fileName, String content) {
+        ContentValues values = new ContentValues();
+        values.put(MediaStore.Downloads.DISPLAY_NAME, fileName);
+        values.put(MediaStore.Downloads.MIME_TYPE, "text/plain");
+        values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/" + CRASH_DIR);
+
+        Uri uri = null;
+        boolean success = false;
+
+        try {
+            uri = mApplication.getContentResolver().insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
+            if (uri != null) {
+                try (OutputStream os = mApplication.getContentResolver().openOutputStream(uri)) {
+                    if (os != null) {
+                        os.write(content.getBytes());
+                        // 【修复4】显式 flush
+                        os.flush();
+                        success = true;
+                        return uri.toString();
+                    }
+                }
+            }
+        } catch (Exception e) {
+            safeLog("MediaStore save failed: " + e.getMessage());
+        } finally {
+            // 失败时清理已创建的 Uri
+            if (!success && uri != null) {
+                try {
+                    mApplication.getContentResolver().delete(uri, null, null);
+                } catch (Exception ignored) {}
+            }
+        }
+
+        // 降级到 ExternalFiles
+        return saveToExternalFiles(fileName, content);
+    }
+
+    /**
+     * Android 9 写入 ExternalFiles/CrashLogs
+     */
+    private String saveToExternalFiles(String fileName, String content) {
+        File dir = mApplication.getExternalFilesDir(null);
+        if (dir == null) {
+            dir = new File(mApplication.getFilesDir(), CRASH_DIR);
+        } else {
+            dir = new File(dir, CRASH_DIR);
+        }
+
+        if (!dir.exists() && !dir.mkdirs()) {
+            // 最后尝试内部存储
+            dir = new File(mApplication.getFilesDir(), CRASH_DIR);
+            if (!dir.exists() && !dir.mkdirs()) {
+                return null;
+            }
+        }
+
+        File file = new File(dir, fileName);
+        try (FileOutputStream fos = new FileOutputStream(file)) {
+            fos.write(content.getBytes());
+            fos.flush();
+            fos.getFD().sync();
+
+            // Android 9 触发媒体扫描
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+                triggerMediaScan(file);
+            }
+            return file.getAbsolutePath();
+        } catch (Exception e) {
+            safeLog("File save failed: " + e.getMessage());
+        }
+        return null;
+    }
+
+    private void triggerMediaScan(File file) {
+        try {
+            Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+            intent.setData(Uri.fromFile(file));
+            mApplication.sendBroadcast(intent);
+        } catch (Exception ignored) {}
+    }
+
+    private void safeLog(String msg) {
+        try {
+            Log.e(TAG, msg);
+        } catch (Throwable ignored) {}
+    }
+
+    // ==================== 文件管理(供外部调用)====================
+
+    /**
+     * 获取崩溃日志目录(仅 ExternalFiles 目录)
+     * 注意:Android 10+ 存入 Downloads 的文件需通过 {@link #getDownloadsCrashLogs()} 获取
+     */
+    @NonNull
+    public File getCrashLogDir() {
+        File dir = mApplication.getExternalFilesDir(null);
+        if (dir == null) {
+            dir = mApplication.getFilesDir();
+        }
+        return new File(dir, CRASH_DIR);
+    }
+
+    /**
+     * 获取 ExternalFiles 目录下的崩溃日志
+     */
+    @NonNull
+    public File[] getCrashLogFiles() {
+        File crashDir = getCrashLogDir();
+        if (crashDir.exists()) {
+            File[] files = crashDir.listFiles((d, name) -> name.startsWith(CRASH_FILE_PREFIX));
+            return files != null ? files : new File[0];
+        }
+        return new File[0];
+    }
+
+    /**
+     * 【修复5】获取 Android 10+ Downloads 目录下的崩溃日志 Uri 列表
+     * @return Uri 列表,可用于读取或删除
+     */
+    @NonNull
+    public List<Uri> getDownloadsCrashLogs() {
+        List<Uri> result = new ArrayList<>();
+
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+            return result;
+        }
+
+        String[] projection = {MediaStore.Downloads._ID, MediaStore.Downloads.DISPLAY_NAME};
+        String selection = MediaStore.Downloads.RELATIVE_PATH + "=? AND " +
+                MediaStore.Downloads.DISPLAY_NAME + " LIKE ?";
+        String[] selectionArgs = {
+                Environment.DIRECTORY_DOWNLOADS + "/" + CRASH_DIR + "/",
+                CRASH_FILE_PREFIX + "%"
+        };
+
+        try (Cursor cursor = mApplication.getContentResolver().query(
+                MediaStore.Downloads.EXTERNAL_CONTENT_URI,
+                projection,
+                selection,
+                selectionArgs,
+                MediaStore.Downloads.DATE_MODIFIED + " DESC")) {
+
+            if (cursor != null) {
+                int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Downloads._ID);
+                while (cursor.moveToNext()) {
+                    long id = cursor.getLong(idColumn);
+                    Uri uri = Uri.withAppendedPath(MediaStore.Downloads.EXTERNAL_CONTENT_URI, String.valueOf(id));
+                    result.add(uri);
+                }
+            }
+        } catch (Exception e) {
+            safeLog("Query Downloads failed: " + e.getMessage());
+        }
+
+        return result;
+    }
+
+    /**
+     * 清除 ExternalFiles 目录下的所有崩溃日志
+     * @return 删除的文件数量
+     */
+    public int clearCrashLogs() {
+        File[] files = getCrashLogFiles();
+        int count = 0;
+        for (File file : files) {
+            if (file.delete()) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    /**
+     * 清除超过指定天数的崩溃日志(仅 ExternalFiles 目录)
+     * @param days 保留天数
+     * @return 删除的文件数量
+     */
+    public int clearOldCrashLogs(int days) {
+        File[] files = getCrashLogFiles();
+        int count = 0;
+        long threshold = System.currentTimeMillis() - (long) days * 24 * 60 * 60 * 1000;
+
+        for (File file : files) {
+            if (file.lastModified() < threshold && file.delete()) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    /**
+     * 清除 Downloads 目录下的所有崩溃日志(Android 10+)
+     * @return 删除的文件数量
+     */
+    public int clearDownloadsCrashLogs() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+            return 0;
+        }
+
+        List<Uri> uris = getDownloadsCrashLogs();
+        int count = 0;
+        for (Uri uri : uris) {
+            try {
+                if (mApplication.getContentResolver().delete(uri, null, null) > 0) {
+                    count++;
+                }
+            } catch (Exception ignored) {}
+        }
+        return count;
+    }
+}

+ 14 - 0
ApexDrivers/RAUtilsLibrary/src/main/res/xml/crash_file_paths.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+    
+    <!-- 外部私有目录下的崩溃日志 -->
+    <external-files-path
+        name="crash_logs"
+        path="CrashLogs/" />
+    
+    <!-- 内部存储的崩溃日志(备用) -->
+    <files-path
+        name="crash_logs_internal"
+        path="CrashLogs/" />
+
+</paths>

+ 1 - 1
ApexDrivers/apexcrm/src/main/java/com/usai/apex/apexcrm/service/CRMService3.java

@@ -11,7 +11,7 @@ import com.usai.redant.rautils.service.RATask3Notifiacation;
 //import com.usai.redant.rautils.service.RATask3Upload;
 
 public class CRMService3 extends RAService3 {
-    String TAG="RAImageBackgroundService3";
+    String TAG="CRMService3";
     public CRMService3() {
         CHANNEL_ID = RACRMApp.CHANNEL_ID;
         CHANNEL_NAME = RACRMApp.CHANNEL_NAME;

+ 4 - 3
ApexDrivers/raimage/build.gradle

@@ -9,15 +9,16 @@ android {
 //            include 'armeabi-v7a',"arm64-v8a"
 //            universalApk true
 //        }
-//    }
+//    }333
     compileSdk = 36
     defaultConfig {
         applicationId "com.usai.redant.raimage"
         minSdk  = 28
         targetSdk = 36
 
-        versionCode 15
-        versionName "1.28.57582"
+        versionCode 16
+
+        versionName "1.28.58162"
      //   ndk.abiFilters 'armeabi-v7a',"arm64-v8a"
     }
     buildTypes {

+ 17 - 16
ApexDrivers/raimage/src/main/AndroidManifest.xml

@@ -60,26 +60,27 @@
 <!--                android:value="" />-->
 <!--        </activity>-->
 
-        <service
-            android:name="com.usai.service.RAImageBackgroundService3"
-            android:enabled="true"
-            android:foregroundServiceType="dataSync"
-            android:exported="false" /> <!-- <service -->
+<!--        <service-->
+<!--            android:name="com.usai.service.RAImageBackgroundService3"-->
+<!--            android:enabled="true"-->
+<!--            android:foregroundServiceType="dataSync"-->
+<!--            android:exported="false" />-->
+        <!-- <service -->
         <!-- android:name="com.usai.service.RAImageBackgroundService" -->
         <!-- android:enabled="true" -->
         <!-- android:exported="true" -->
         <!-- android:label="ApexDrivers background service"></service> -->
-        <receiver
-            android:name="com.usai.receiver.RAImageBootCompleteReceiver"
-            android:directBootAware="true"
-            android:enabled="true"
-            android:exported="false"
-            android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
-            <intent-filter>
-                <action android:name="android.intent.action.BOOT_COMPLETED" />
-                <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
-            </intent-filter>
-        </receiver> <!-- Activity -->
+<!--        <receiver-->
+<!--            android:name="com.usai.receiver.RAImageBootCompleteReceiver"-->
+<!--            android:directBootAware="true"-->
+<!--            android:enabled="true"-->
+<!--            android:exported="false"-->
+<!--            android:permission="android.permission.RECEIVE_BOOT_COMPLETED">-->
+<!--            <intent-filter>-->
+<!--                <action android:name="android.intent.action.BOOT_COMPLETED" />-->
+<!--                <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />-->
+<!--            </intent-filter>-->
+<!--        </receiver> -->
         <activity
             android:name=".MainActivity"
             android:stateNotNeeded="true"

+ 15 - 10
ApexDrivers/raimage/src/main/java/com/usai/redant/raimage/MainActivity.java

@@ -54,9 +54,10 @@ import androidx.core.content.FileProvider;
 import com.usai.redant.raimage.Model.ManufactureListActivity;
 import com.usai.redant.raimage.PhotoList.PhotoGridActivity;
 import com.usai.redant.rautils.barcodescanner.CaptureActivityNew;
+import com.usai.redant.rautils.newupload.NewTaskActivity;
+import com.usai.redant.rautils.newupload.RAUploadWorker;
 import com.usai.redant.rautils.receiver.RABroadcast;
 import com.usai.redant.rautils.signature.SignatureActivity;
-import com.usai.redant.rautils.upload.TaskActivity;
 import com.usai.redant.rautils.utils.RAUtil;
 import com.usai.util.AES;
 import com.usai.util.MD5;
@@ -117,7 +118,7 @@ public class MainActivity extends AppCompatActivity {
 
                 if (finalResult_code == Network.RESULT_TRUE) {
                     //验证成功
-                    Intent intent = new Intent(RABroadcast.ACTION_UPLOAD_ADD_TASK);
+//                    Intent intent = new Intent(RABroadcast.ACTION_UPLOAD_ADD_TASK);
                     ArrayList<Bundle> taskArr = new ArrayList<Bundle>();
 
                     String encryptUser = AES.encrypt("usai", RAImageApplication.user);
@@ -186,9 +187,11 @@ public class MainActivity extends AppCompatActivity {
                         }
                     }
 
-                    intent.putParcelableArrayListExtra("tasks", taskArr);
-                    intent.setPackage(getPackageName());
-                    sendBroadcast(intent);
+//                    intent.putParcelableArrayListExtra("tasks", taskArr);
+//                    intent.setPackage(getPackageName());
+//                    sendBroadcast(intent);
+
+                    RAUploadWorker.addTasksAndUpload(MainActivity.this, taskArr);
                     clearfornew();
                 } else {
                     switch (finalResult_code) {
@@ -227,7 +230,7 @@ public class MainActivity extends AppCompatActivity {
                                         @Override
                                         public void onClick(DialogInterface dialog, int which) {
 
-                                            Intent intent = new Intent(RABroadcast.ACTION_UPLOAD_ADD_TASK);
+//                                            Intent intent = new Intent(RABroadcast.ACTION_UPLOAD_ADD_TASK);
                                             ArrayList<Bundle> taskArr = new ArrayList<Bundle>();
 
                                             for (String path : photoList) {
@@ -263,9 +266,11 @@ public class MainActivity extends AppCompatActivity {
                                                 taskArr.add(task);
                                             }
 
-                                            intent.putParcelableArrayListExtra("tasks", taskArr);
-                                            intent.setPackage(getPackageName());
-                                            sendBroadcast(intent);
+//                                            intent.putParcelableArrayListExtra("tasks", taskArr);
+//                                            intent.setPackage(getPackageName());
+//                                            sendBroadcast(intent);
+
+                                            RAUploadWorker.addTasksAndUpload(MainActivity.this, taskArr);
                                             clearfornew();
                                         }
 
@@ -1190,7 +1195,7 @@ public class MainActivity extends AppCompatActivity {
         if (newVersion) {
             switch (item.getItemId()) {
                 case R.id.upload_list_btn: {
-                    Intent intent = new Intent(this, TaskActivity.class);
+                    Intent intent = new Intent(this, NewTaskActivity.class);
                     startActivity(intent);
                 }
                 break;

+ 231 - 238
ApexDrivers/raimage/src/main/java/com/usai/redant/raimage/RAImageApplication.java

@@ -1,35 +1,13 @@
 package com.usai.redant.raimage;
 
-import static android.app.PendingIntent.FLAG_IMMUTABLE;
-
-import android.app.AlarmManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
+import android.app.Application;
 import android.content.SharedPreferences;
-import android.os.Build;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.SystemClock;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 
-import androidx.core.app.NotificationCompat;
-
-import com.usai.redant.rautils.application.RedantApplication;
-import com.usai.redant.rautils.upload.TaskActivity;
-import com.usai.redant.rautils.utils.RACrashHandler;
-import com.usai.redant.rautils.utils.RACrashHandler26;
-import com.usai.service.RAImageBackgroundService3;
+import com.usai.redant.rautils.newupload.RAUploadWorker;
+import com.usai.redant.rautils.utils.RACrashHandler28;
 
 import org.json.JSONObject;
 
@@ -37,8 +15,15 @@ import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
-public class RAImageApplication extends RedantApplication
+public class RAImageApplication extends Application
 {
+    private static RAImageApplication instance;
+
+
+    public static RAImageApplication getInstance() {
+        return instance;
+    }
+
 
     public static  String CHANNEL_ID = "RAImage";
     public static  String CHANNEL_NAME = "RAImage";
@@ -55,166 +40,169 @@ public class RAImageApplication extends RedantApplication
     public static String device_id;
     public static JSONObject server_info;
 
-    private ServiceConnection mServiceConnection;
-    private RAImageBackgroundService3 mService;
+//    private ServiceConnection mServiceConnection;
+//    private RAImageBackgroundService3 mService;
 
 //    public static RedAntApplication getInstance() {
 //        return instance;
 //
 
 
-    private final BroadcastReceiver ApplicaitonReceiver = new BroadcastReceiver()
-    {
-
-        public void onReceive(
-                Context context,
-                Intent intent)
-        {
-
-
-
-            int count = intent.getIntExtra("count",-1);
-
-            String action = intent
-                    .getAction();
-            if ("REDANT.RAImage.UPLOAD_UPDATE_COUNT".equals(action))
-
-            {
-                // [修改点 1] ------------ 修复启动时序问题 ------------
-                // 如果 mService 还没绑定好(为空),则延迟 1 秒后重新执行此方法
-                if (mService == null) {
-                    Log.w("RAImage", "mService is null in onReceive, retrying in 1s...");
-                    new Handler(Looper.getMainLooper()).postDelayed(() -> {
-                        onReceive(context, intent); // 重新投递当前的任务
-                    }, 1000);
-                    return; // 暂停本次执行,等待重试
-                }
-                // [结束] ---------------------------------------
-
-                TaskActivity t;
-                PendingIntent contentIntent=PendingIntent.getActivity(getApplicationContext(),
-                        0,new Intent(getApplicationContext(), TaskActivity.class),FLAG_IMMUTABLE);
-                if (count>0)
-                {
-
-
-//              new Intent(getApplicationContext(), TaskActivity.class);
-
-
-//              new Intent (getApplicationContext(),TaskActivity.class);
-                    NotificationChannel channel = new NotificationChannel("Service Start",
-                            "Service Start", NotificationManager.IMPORTANCE_HIGH);
-                    ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
-
-                    // [修改点 2] 将 mService 改为 context,防止 Builder 初始化崩溃
-                    Notification notification = new NotificationCompat.Builder(context, "Service Start")
-//                    .setContentTitle("Apex Land is running.")
-                            .setContentText(count + " files uploading...")
-                            .setContentIntent(contentIntent)
-                            .setCategory(Notification.CATEGORY_SERVICE).setSmallIcon(com.usai.redant.rautils.R.drawable.ic_launcher_foreground).setPriority(1000).build();
-
-                    // [修改点 3] 增加 try-catch 防止 Service 状态异常导致的闪退
-                    try {
-                        mService.startForeground(101, notification);
-                    } catch (Exception e) {
-                        Log.e("RAImage", "Failed to startForeground in onReceive", e);
-                    }
-                }
-                else
-                if(count==0)
-                {
-
-
-
-                    NotificationChannel channel = new NotificationChannel("Service Start",
-                            "Service Start", NotificationManager.IMPORTANCE_HIGH);
-                    ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
-
-                    // [修改点 2] 将 mService 改为 context
-                    Notification notification = new NotificationCompat.Builder(context, "Service Start")
-//                    .setContentTitle("Apex Land is running.")
-                            .setContentText("Upload complete.")
-                            .setContentIntent(contentIntent)
-
-                            .setCategory(Notification.CATEGORY_SERVICE).setSmallIcon(com.usai.redant.rautils.R.drawable.ic_launcher_foreground).setPriority(1000).build();
-
-                    // [修改点 3] 增加 try-catch 防止 Service 状态异常导致的闪退
-                    try {
-                        mService.startForeground(101, notification);
-                    } catch (Exception e) {
-                        Log.e("RAImage", "Failed to startForeground in onReceive", e);
-                    }
-                }
-
-
-
+//    private final BroadcastReceiver ApplicaitonReceiver = new BroadcastReceiver()
+//    {
 //
-                Log.e("Finish_msg", "Application onReceive: ");
-            }
-        }
-    };
-
-
-
-    public static void startalarm()
-    {
-
-        Intent iAlarm = new Intent("REDANT.POP.RETRY_UPLOAD");
-        //iAlarm.putExtra("caller", caller);
-//     iAlarm.setAction("com.usai.apex.push");
-        PendingIntent sender = PendingIntent.getBroadcast(getInstance(), 0,
-                iAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
-
-        long firstime = SystemClock.elapsedRealtime();
-//     UpdateLastAlermTime();
-        AlarmManager am = (AlarmManager) getInstance().getSystemService(
-                Context.ALARM_SERVICE);
-
-        // 5分钟一个周期,不停的发送广播
-        am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, firstime,
-                300 * 1000, sender);
-        Log.d("redant pop","start alarm");
-    }
-
-    public static void cancelalarm()
-    {
-
-        // 启动完成
-        Intent iAlarm = new Intent("REDANT.POP.RETRY_UPLOAD");
-//     iAlarm.setAction("com.usai.apex.push");
-        PendingIntent sender = PendingIntent.getBroadcast(getInstance(), 0,
-                iAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
+//        public void onReceive(
+//                Context context,
+//                Intent intent)
+//        {
+//
+//
+//
+//            int count = intent.getIntExtra("count",-1);
+//
+//            String action = intent
+//                    .getAction();
+//            if ("REDANT.RAImage.UPLOAD_UPDATE_COUNT".equals(action))
+//
+//            {
+//                // [修改点 1] ------------ 修复启动时序问题 ------------
+//                // 如果 mService 还没绑定好(为空),则延迟 1 秒后重新执行此方法
+////                if (mService == null) {
+////                    Log.w("RAImage", "mService is null in onReceive, retrying in 1s...");
+////                    new Handler(Looper.getMainLooper()).postDelayed(() -> {
+////                        onReceive(context, intent); // 重新投递当前的任务
+////                    }, 1000);
+////                    return; // 暂停本次执行,等待重试
+////                }
+//                // [结束] ---------------------------------------
+//
+//                NewTaskActivity t;
+//                PendingIntent contentIntent=PendingIntent.getActivity(getApplicationContext(),
+//                        0,new Intent(getApplicationContext(), NewTaskActivity.class),FLAG_IMMUTABLE);
+////                if (count>0)
+////                {
+////
+////
+//////              new Intent(getApplicationContext(), TaskActivity.class);
+////
+////
+//////              new Intent (getApplicationContext(),TaskActivity.class);
+////                    NotificationChannel channel = new NotificationChannel("Service Start",
+////                            "Service Start", NotificationManager.IMPORTANCE_HIGH);
+////                    ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
+////
+////                    // [修改点 2] 将 mService 改为 context,防止 Builder 初始化崩溃
+////                    Notification notification = new NotificationCompat.Builder(context, "Service Start")
+//////                    .setContentTitle("Apex Land is running.")
+////                            .setContentText(count + " files uploading...")
+////                            .setContentIntent(contentIntent)
+////                            .setCategory(Notification.CATEGORY_SERVICE).setSmallIcon(com.usai.redant.rautils.R.drawable.ic_launcher_foreground).setPriority(1000).build();
+////
+////                    // [修改点 3] 增加 try-catch 防止 Service 状态异常导致的闪退
+////                    try {
+////                        mService.startForeground(101, notification);
+////                    } catch (Exception e) {
+////                        Log.e("RAImage", "Failed to startForeground in onReceive", e);
+////                    }
+////                }
+////                else
+////                if(count==0)
+////                {
+////
+////
+////
+////                    NotificationChannel channel = new NotificationChannel("Service Start",
+////                            "Service Start", NotificationManager.IMPORTANCE_HIGH);
+////                    ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
+////
+////                    // [修改点 2] 将 mService 改为 context
+////                    Notification notification = new NotificationCompat.Builder(context, "Service Start")
+//////                    .setContentTitle("Apex Land is running.")
+////                            .setContentText("Upload complete.")
+////                            .setContentIntent(contentIntent)
+////
+////                            .setCategory(Notification.CATEGORY_SERVICE).setSmallIcon(com.usai.redant.rautils.R.drawable.ic_launcher_foreground).setPriority(1000).build();
+////
+////                    // [修改点 3] 增加 try-catch 防止 Service 状态异常导致的闪退
+////                    try {
+////                        mService.startForeground(101, notification);
+////                    } catch (Exception e) {
+////                        Log.e("RAImage", "Failed to startForeground in onReceive", e);
+////                    }
+////                }
+//
+//
+//
+////
+//                Log.e("Finish_msg", "Application onReceive: ");
+//            }
+//        }
+//    };
 
-        AlarmManager am = (AlarmManager) getInstance().getSystemService(
-                Context.ALARM_SERVICE);
 
-        am.cancel(sender);
-        Log.d("redant pop","cancel alarm");
-    }
+//
+//    public static void startalarm()
+//    {
+//
+//        Intent iAlarm = new Intent("REDANT.POP.RETRY_UPLOAD");
+//        //iAlarm.putExtra("caller", caller);
+////     iAlarm.setAction("com.usai.apex.push");
+//        PendingIntent sender = PendingIntent.getBroadcast(getInstance(), 0,
+//                iAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
+//
+//        long firstime = SystemClock.elapsedRealtime();
+////     UpdateLastAlermTime();
+//        AlarmManager am = (AlarmManager) getInstance().getSystemService(
+//                Context.ALARM_SERVICE);
+//
+//        // 5分钟一个周期,不停的发送广播
+//        am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, firstime,
+//                300 * 1000, sender);
+//        Log.d("redant pop","start alarm");
+//    }
+//
+//    public static void cancelalarm()
+//    {
+//
+//        // 启动完成
+//        Intent iAlarm = new Intent("REDANT.POP.RETRY_UPLOAD");
+////     iAlarm.setAction("com.usai.apex.push");
+//        PendingIntent sender = PendingIntent.getBroadcast(getInstance(), 0,
+//                iAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
+//
+//        AlarmManager am = (AlarmManager) getInstance().getSystemService(
+//                Context.ALARM_SERVICE);
+//
+//        am.cancel(sender);
+//        Log.d("redant pop","cancel alarm");
+//    }
 
     @Override
     public void onCreate() {
 
-        RACrashHandler26.init(this);
+        RACrashHandler28.init(this);
+
         // TODO Auto-generated method stub
         Log.d("_RAIMAGE", "onCreate: RedAntApplication");
         super.onCreate();
 
-        IntentFilter msgFilter = new IntentFilter();
-        msgFilter.addAction("REDANT.RAImage.UPLOAD_UPDATE_COUNT");
-
-        msgFilter.setPriority(2147483647);
-
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
-        {
-            registerReceiver(ApplicaitonReceiver, msgFilter,RECEIVER_NOT_EXPORTED);
-        }
-        else
-        {
-
+        instance = this;
 
-            registerReceiver(ApplicaitonReceiver, msgFilter);
-        }
+//        IntentFilter msgFilter = new IntentFilter();
+//        msgFilter.addAction("REDANT.RAImage.UPLOAD_UPDATE_COUNT");
+//
+//        msgFilter.setPriority(2147483647);
+//
+//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+//        {
+//            registerReceiver(ApplicaitonReceiver, msgFilter,RECEIVER_NOT_EXPORTED);
+//        }
+//        else
+//        {
+//
+//
+//            registerReceiver(ApplicaitonReceiver, msgFilter);
+//        }
 
 
 //     setInstance(this);
@@ -234,84 +222,89 @@ public class RAImageApplication extends RedantApplication
             station_name = name;
 
         /** Service */
-        mServiceConnection = new ServiceConnection() {
-            @Override
-            public void onServiceConnected(ComponentName name, IBinder service) {
-
-                RAImageBackgroundService3.Service3Binder binder = (RAImageBackgroundService3.Service3Binder)service;
-                mService = (RAImageBackgroundService3)binder.getService();
-
-            }
-
-            @Override
-            public void onServiceDisconnected(ComponentName name) {
-
-                mService = null;
-            }
-        };
-
-
-
-        Intent serviceIntent = new Intent();
-        serviceIntent.setClass(this, RAImageBackgroundService3.class);
-
-        // [修改点 4] ------------ 修复 ForegroundServiceDidNotStopInTimeException ------------
-        // 策略:尽量使用普通的 startService,只有在后台被系统拦截时(IllegalStateException),
-        // 才不得已使用 startForegroundService。这能避免系统因 Service 启动慢而直接杀死 App。
-        try {
-            // 1. 优先尝试普通启动(安全,不会崩)
-            this.startService(serviceIntent);
-        } catch (IllegalStateException e) {
-            // 2. 捕获到 IllegalStateException 说明 App 在后台,必须用 Foreground Service
-            try {
-                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-                    this.startForegroundService(serviceIntent);
-                }
-            } catch (Exception innerE) {
-                // 如果还是起不来(比如 Android 14 后台限制),记录日志,但不要让 App 闪退
-                Log.e("RAImage", "Failed to startForegroundService fallback", innerE);
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        // [修改结束] -------------------------------------------------------------------
-
-        Intent intent = new Intent(getApplicationContext(),RAImageBackgroundService3.class);
-        bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);
+//        mServiceConnection = new ServiceConnection() {
+//            @Override
+//            public void onServiceConnected(ComponentName name, IBinder service) {
+//
+//                RAImageBackgroundService3.Service3Binder binder = (RAImageBackgroundService3.Service3Binder)service;
+//                mService = (RAImageBackgroundService3)binder.getService();
+//
+//            }
+//
+//            @Override
+//            public void onServiceDisconnected(ComponentName name) {
+//
+//                mService = null;
+//            }
+//        };
+//
+//
+//
+//        Intent serviceIntent = new Intent();
+//        serviceIntent.setClass(this, RAImageBackgroundService3.class);
+//
+//        // [修改点 4] ------------ 修复 ForegroundServiceDidNotStopInTimeException ------------
+//        // 策略:尽量使用普通的 startService,只有在后台被系统拦截时(IllegalStateException),
+//        // 才不得已使用 startForegroundService。这能避免系统因 Service 启动慢而直接杀死 App。
+//        try {
+//            // 1. 优先尝试普通启动(安全,不会崩)
+//            this.startService(serviceIntent);
+//        } catch (IllegalStateException e) {
+//            // 2. 捕获到 IllegalStateException 说明 App 在后台,必须用 Foreground Service
+//            try {
+//                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+//                    this.startForegroundService(serviceIntent);
+//                }
+//            } catch (Exception innerE) {
+//                // 如果还是起不来(比如 Android 14 后台限制),记录日志,但不要让 App 闪退
+//                Log.e("RAImage", "Failed to startForegroundService fallback", innerE);
+//            }
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//        }
+//        // [修改结束] -------------------------------------------------------------------
+//
+//        Intent intent = new Intent(getApplicationContext(),RAImageBackgroundService3.class);
+//        bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);
 
         /***/
 
-        RACrashHandler.init(this);
-    }
-
-    @Override
-    public Class getServiceClass() {
-        return mService.getClass();
-    }
-
-    @Override
-    public boolean useFakeData()
-    {
-        return false;
-    }
-
-
+//        RACrashHandler.init(this);
 
-    @Override
-    public void onTerminate() {
-        Log.d("_RAIMAGE", "onTerminate: RedAntApplication");
-        super.onTerminate();
 
-        unbindService(mServiceConnection);
 
 
-        unregisterReceiver(ApplicaitonReceiver);
+        RAUploadWorker.schedulePeriodic(this);
     }
+//
+//    @Override
+//    public Class getServiceClass() {
+//        return mService.getClass();
+//    }
+//
+//    @Override
+//    public boolean useFakeData()
+//    {
+//        return false;
+//    }
+//
 
-    @Override
-    protected void finalize() throws Throwable {
 
-        Log.d("_RAIMAGE", "finalize: RedAntApplication");
-        super.finalize();
-    }
+//    @Override
+//    public void onTerminate() {
+//        Log.d("_RAIMAGE", "onTerminate: RedAntApplication");
+//        super.onTerminate();
+//
+////        unbindService(mServiceConnection);
+//
+//
+////        unregisterReceiver(ApplicaitonReceiver);
+//    }
+//
+//    @Override
+//    protected void finalize() throws Throwable {
+//
+//        Log.d("_RAIMAGE", "finalize: RedAntApplication");
+//        super.finalize();
+//    }
 }

+ 4 - 0
ApexDrivers/raimage/src/main/java/com/usai/redant/raimage/uploadSettingActivity.java

@@ -15,6 +15,7 @@ import androidx.annotation.NonNull;
 import androidx.appcompat.app.ActionBar;
 import androidx.appcompat.app.AppCompatActivity;
 
+import com.usai.redant.rautils.newupload.RAUploadWorker;
 import com.usai.redant.rautils.upload.RAUploadManager;
 
 public class uploadSettingActivity extends AppCompatActivity {
@@ -96,6 +97,9 @@ public class uploadSettingActivity extends AppCompatActivity {
                 configure.retryWaiting = retry_waiting;
             }
         });
+
+        RAUploadWorker.schedulePeriodic(getApplicationContext());
+
     }
 
     @Override