Commit 4a984f16 authored by 姜天宇's avatar 姜天宇

feat(v1.0.2): 完善AIDL接口

parent 5d915c1d
...@@ -59,8 +59,8 @@ android { ...@@ -59,8 +59,8 @@ android {
//如果合并不能解决问题就选择其中一个 //如果合并不能解决问题就选择其中一个
merge 'META-INF/proguard/androidx-annotations.pro' merge 'META-INF/proguard/androidx-annotations.pro'
merge 'META-INF/proguard/coroutines.pro' merge 'META-INF/proguard/coroutines.pro'
// merge 'lib/arm64-v8a/libc++_shared.so' merge 'lib/arm64-v8a/libc++_shared.so'
// merge 'lib/armeabi-v7a/libc++_shared.so' merge 'lib/armeabi-v7a/libc++_shared.so'
pickFirst 'lib/x86/libc++_shared.so' pickFirst 'lib/x86/libc++_shared.so'
pickFirst 'lib/x86_64/libc++_shared.so' pickFirst 'lib/x86_64/libc++_shared.so'
pickFirst 'lib/arm64-v8a/libc++_shared.so' pickFirst 'lib/arm64-v8a/libc++_shared.so'
......
...@@ -12,10 +12,17 @@ ...@@ -12,10 +12,17 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" /> tools:ignore="ScopedStorage" />
<!--摄像头相关权限-->
<uses-feature android:name="android.hardware.camera.any" /> <uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application <application
android:name=".Application" android:name=".Application"
......
...@@ -5,6 +5,7 @@ import com.wmdigit.NetworkModule; ...@@ -5,6 +5,7 @@ import com.wmdigit.NetworkModule;
import com.wmdigit.common.CommonModule; import com.wmdigit.common.CommonModule;
import com.wmdigit.core.CoreModule; import com.wmdigit.core.CoreModule;
import com.wmdigit.data.LocalDataModule; import com.wmdigit.data.LocalDataModule;
import com.wmdigit.service.ServiceModule;
/** /**
* @author dizi * @author dizi
...@@ -45,5 +46,7 @@ public class Application extends android.app.Application { ...@@ -45,5 +46,7 @@ public class Application extends android.app.Application {
CoreModule.init(this); CoreModule.init(this);
// 初始化网络模块 // 初始化网络模块
NetworkModule.init(this); NetworkModule.init(this);
// 初始化服务模块
ServiceModule.init(this);
} }
} }
package com.wmdigit.common.constants;
/**
* 错误码
* @author dizi
*/
public class ErrorCode {
// 成功的错误码
public static final int ERROR_CODE_SUCCESS = 0;
// 摄像头不可用的错误码
public static final int ERROR_CODE_CAMERA_NOT_AVAILABLE = 2000;
// 未激活的错误码
public static final int ERROR_CODE_NOT_ACTIVATED = 2001;
// 学习数据初始化未完成的错误码
public static final int ERROR_CODE_LEARNING_DATA_INIT_NOT_COMPLETED = 2002;
// 摄像头未裁剪的错误码
public static final int ERROR_CODE_CAMERA_NOT_CROPPED = 2003;
// 区域内无对象的错误码
public static final int ERROR_CODE_NO_OBJECT_IN_AREA = 2004;
}
package com.wmdigit.common.model; package com.wmdigit.common.model;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
/** /**
* 商品DTO * 商品DTO
* @author dizi * @author dizi
*/ */
public class ProductsDTO { public class ProductsDTO implements Parcelable {
/** /**
* 品名 * 品名
...@@ -40,6 +43,22 @@ public class ProductsDTO { ...@@ -40,6 +43,22 @@ public class ProductsDTO {
this.onSale = onSale; this.onSale = onSale;
} }
protected ProductsDTO(Parcel in) {
readFromParcel(in);
}
public static final Creator<ProductsDTO> CREATOR = new Creator<ProductsDTO>() {
@Override
public ProductsDTO createFromParcel(Parcel in) {
return new ProductsDTO(in);
}
@Override
public ProductsDTO[] newArray(int size) {
return new ProductsDTO[size];
}
};
@NonNull @NonNull
@Override @Override
public String toString() { public String toString() {
...@@ -85,4 +104,35 @@ public class ProductsDTO { ...@@ -85,4 +104,35 @@ public class ProductsDTO {
public void setUnitPrice(String unitPrice) { public void setUnitPrice(String unitPrice) {
this.unitPrice = unitPrice; this.unitPrice = unitPrice;
} }
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(productName);
dest.writeString(productCode);
dest.writeString(productMnemonicCode);
dest.writeString(unitPrice);
if (onSale == null) {
dest.writeByte((byte) 0);
} else {
dest.writeByte((byte) 1);
dest.writeInt(onSale);
}
}
protected void readFromParcel(Parcel in){
productName = in.readString();
productCode = in.readString();
productMnemonicCode = in.readString();
unitPrice = in.readString();
if (in.readByte() == 0) {
onSale = null;
} else {
onSale = in.readInt();
}
}
} }
...@@ -11,7 +11,7 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { ...@@ -11,7 +11,7 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR; return JNI_ERR;
} }
java_class_video_pipe_manager = (jclass)env->NewGlobalRef(env->FindClass("com/wmdigit/core/videopipe/VideoPipeManager")); java_class_video_pipe_manager = (jclass)env->NewGlobalRef(env->FindClass("com/wmdigit/core/videopipe/VideoPipeRepository"));
return JNI_VERSION_1_6; return JNI_VERSION_1_6;
} }
...@@ -21,9 +21,9 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { ...@@ -21,9 +21,9 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
*/ */
extern "C" extern "C"
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_wmdigit_core_videopipe_VideoPipeManager_init(JNIEnv *env, jobject thiz, Java_com_wmdigit_core_videopipe_VideoPipeRepository_init(JNIEnv *env, jobject thiz,
jstring modelBackBonePath, jstring modelBackBonePath,
jstring modelC3dPath) { jstring modelC3dPath) {
int size_back_bone, size_c3d; int size_back_bone, size_c3d;
unsigned char* data_back_bone = read_file_byte(env, modelBackBonePath, size_back_bone); unsigned char* data_back_bone = read_file_byte(env, modelBackBonePath, size_back_bone);
unsigned char* data_c3d = read_file_byte(env, modelC3dPath, size_c3d); unsigned char* data_c3d = read_file_byte(env, modelC3dPath, size_c3d);
...@@ -50,8 +50,8 @@ Java_com_wmdigit_core_videopipe_VideoPipeManager_init(JNIEnv *env, jobject thiz, ...@@ -50,8 +50,8 @@ Java_com_wmdigit_core_videopipe_VideoPipeManager_init(JNIEnv *env, jobject thiz,
*/ */
extern "C" extern "C"
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_wmdigit_core_videopipe_VideoPipeManager_setMarkMat(JNIEnv *env, jobject thiz, Java_com_wmdigit_core_videopipe_VideoPipeRepository_setMarkMat(JNIEnv *env, jobject thiz,
jobject bitmap) { jobject bitmap) {
// Bitmap转mat // Bitmap转mat
cv::Mat mat = convert_bitmap_to_mat(env, bitmap); cv::Mat mat = convert_bitmap_to_mat(env, bitmap);
// RGBA转RGB // RGBA转RGB
...@@ -63,8 +63,8 @@ Java_com_wmdigit_core_videopipe_VideoPipeManager_setMarkMat(JNIEnv *env, jobject ...@@ -63,8 +63,8 @@ Java_com_wmdigit_core_videopipe_VideoPipeManager_setMarkMat(JNIEnv *env, jobject
extern "C" extern "C"
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_com_wmdigit_core_videopipe_VideoPipeManager_feedFrame(JNIEnv *env, jobject thiz, Java_com_wmdigit_core_videopipe_VideoPipeRepository_feedFrame(JNIEnv *env, jobject thiz,
jobject bitmap) { jobject bitmap) {
// Bitmap转mat // Bitmap转mat
cv::Mat mat = convert_bitmap_to_mat(env, bitmap); cv::Mat mat = convert_bitmap_to_mat(env, bitmap);
// rgba转rgb // rgba转rgb
...@@ -114,7 +114,7 @@ void feed_frame_callback(int state){ ...@@ -114,7 +114,7 @@ void feed_frame_callback(int state){
jmethodID j_method_instance = env->GetStaticMethodID( jmethodID j_method_instance = env->GetStaticMethodID(
java_class_video_pipe_manager, java_class_video_pipe_manager,
"getInstance", "getInstance",
"()Lcom/wmdigit/core/videopipe/VideoPipeManager;" "()Lcom/wmdigit/core/videopipe/VideoPipeRepository;"
); );
jobject instance=env->CallStaticObjectMethod(java_class_video_pipe_manager, j_method_instance); jobject instance=env->CallStaticObjectMethod(java_class_video_pipe_manager, j_method_instance);
jmethodID j_method_in_out; jmethodID j_method_in_out;
......
...@@ -3,10 +3,9 @@ package com.wmdigit.core; ...@@ -3,10 +3,9 @@ package com.wmdigit.core;
import android.content.Context; import android.content.Context;
import com.wmdigit.core.catering.TargetDetectionRepository; import com.wmdigit.core.catering.TargetDetectionRepository;
import com.wmdigit.core.hnsw.Hnsw;
import com.wmdigit.core.hnsw.HnswRepository; import com.wmdigit.core.hnsw.HnswRepository;
import com.wmdigit.core.opencv.OpencvRepository; import com.wmdigit.core.opencv.OpencvRepository;
import com.wmdigit.core.videopipe.VideoPipeManager; import com.wmdigit.core.videopipe.VideoPipeRepository;
/** /**
* Core模块初始化类 * Core模块初始化类
...@@ -35,7 +34,7 @@ public class CoreModule { ...@@ -35,7 +34,7 @@ public class CoreModule {
// 初始化目标检测 // 初始化目标检测
TargetDetectionRepository.getInstance().initTargetDetection(); TargetDetectionRepository.getInstance().initTargetDetection();
// 初始化视频流 // 初始化视频流
VideoPipeManager.getInstance().initVideoPipe(); VideoPipeRepository.getInstance().initVideoPipe();
// 初始化索引库 // 初始化索引库
HnswRepository.getInstance().init(); HnswRepository.getInstance().init();
} }
......
...@@ -42,6 +42,7 @@ public class HnswRepository { ...@@ -42,6 +42,7 @@ public class HnswRepository {
* 记录初始化完成情况 * 记录初始化完成情况
*/ */
private boolean initComplete = false; private boolean initComplete = false;
private OnHnswInitListener listener;
public HnswRepository() { public HnswRepository() {
hnsw = new Hnsw(); hnsw = new Hnsw();
...@@ -63,6 +64,9 @@ public class HnswRepository { ...@@ -63,6 +64,9 @@ public class HnswRepository {
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(o -> { .subscribe(o -> {
initComplete = true; initComplete = true;
if (listener != null){
listener.onInitSuccess();
}
}); });
compositeDisposable.add(disposable); compositeDisposable.add(disposable);
} }
...@@ -157,7 +161,23 @@ public class HnswRepository { ...@@ -157,7 +161,23 @@ public class HnswRepository {
hnsw.initHnsw(); hnsw.initHnsw();
} }
public void setListener(OnHnswInitListener listener) {
this.listener = listener;
if (initComplete && listener != null){
listener.onInitSuccess();
}
}
public boolean isInitComplete() {
return initComplete;
}
public void close(){ public void close(){
compositeDisposable.clear(); compositeDisposable.clear();
listener = null;
}
public static interface OnHnswInitListener{
void onInitSuccess();
} }
} }
...@@ -11,13 +11,11 @@ import com.wmdigit.data.mmkv.repository.CropLocalRepository; ...@@ -11,13 +11,11 @@ import com.wmdigit.data.mmkv.repository.CropLocalRepository;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import io.reactivex.disposables.Disposable;
/** /**
* 视频流管理类 * 视频流管理类
* @author dizi * @author dizi
*/ */
public class VideoPipeManager { public class VideoPipeRepository {
static { static {
System.loadLibrary("video_pipe"); System.loadLibrary("video_pipe");
} }
...@@ -42,13 +40,13 @@ public class VideoPipeManager { ...@@ -42,13 +40,13 @@ public class VideoPipeManager {
*/ */
private native void feedFrame(Bitmap bitmap); private native void feedFrame(Bitmap bitmap);
private static VideoPipeManager instance; private static VideoPipeRepository instance;
public static VideoPipeManager getInstance() { public static VideoPipeRepository getInstance() {
if (instance == null){ if (instance == null){
synchronized (VideoPipeManager.class){ synchronized (VideoPipeRepository.class){
if (instance == null){ if (instance == null){
instance = new VideoPipeManager(); instance = new VideoPipeRepository();
} }
} }
} }
......
...@@ -30,6 +30,13 @@ public interface ProductsDao { ...@@ -30,6 +30,13 @@ public interface ProductsDao {
@Query("SELECT * FROM Products") @Query("SELECT * FROM Products")
List<ProductsPO> getAll(); List<ProductsPO> getAll();
/**
* 查询商品总数
* @return
*/
@Query("SELECT COUNT(*) FROM Products")
int getCount();
/** /**
* 根据关键词获取总数 * 根据关键词获取总数
* @param keywords * @param keywords
...@@ -55,4 +62,5 @@ public interface ProductsDao { ...@@ -55,4 +62,5 @@ public interface ProductsDao {
*/ */
@Query("SELECT * FROM Products WHERE productCode = :productCode") @Query("SELECT * FROM Products WHERE productCode = :productCode")
ProductsPO getByProductCode(String productCode); ProductsPO getByProductCode(String productCode);
} }
...@@ -59,6 +59,14 @@ public class ProductsRepository { ...@@ -59,6 +59,14 @@ public class ProductsRepository {
return getProductsDao().getCountByKeywords(keywords); return getProductsDao().getCountByKeywords(keywords);
} }
/**
* 查询商品总数
* @return
*/
public int queryCount(){
return getProductsDao().getCount();
}
/** /**
* 根据关键词查询 * 根据关键词查询
* @param keywords * @param keywords
......
...@@ -7,14 +7,12 @@ import androidx.recyclerview.widget.GridLayoutManager; ...@@ -7,14 +7,12 @@ import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import com.wmdigit.camera.CameraxController; import com.wmdigit.camera.CameraxController;
import com.wmdigit.camera.listener.OnImageAnalyzeListener;
import com.wmdigit.cateringdetect.demo.R; import com.wmdigit.cateringdetect.demo.R;
import com.wmdigit.cateringdetect.demo.databinding.FragmentDemoHomeBinding; import com.wmdigit.cateringdetect.demo.databinding.FragmentDemoHomeBinding;
import com.wmdigit.cateringdetect.demo.ui.adapter.DemoImagesAdapter; import com.wmdigit.cateringdetect.demo.ui.adapter.DemoImagesAdapter;
import com.wmdigit.cateringdetect.demo.ui.adapter.ShoppingCartAdapter; import com.wmdigit.cateringdetect.demo.ui.adapter.ShoppingCartAdapter;
import com.wmdigit.cateringdetect.demo.ui.viewmodel.DemoHomeViewModel; import com.wmdigit.cateringdetect.demo.ui.viewmodel.DemoHomeViewModel;
import com.wmdigit.common.base.mvvm.BaseMvvmFragment; import com.wmdigit.common.base.mvvm.BaseMvvmFragment;
import com.wmdigit.core.videopipe.VideoPipeManager;
/** /**
......
...@@ -18,13 +18,12 @@ import com.wmdigit.core.catering.TargetDetectionRepository; ...@@ -18,13 +18,12 @@ import com.wmdigit.core.catering.TargetDetectionRepository;
import com.wmdigit.core.catering.model.TargetDetectResult; import com.wmdigit.core.catering.model.TargetDetectResult;
import com.wmdigit.core.hnsw.HnswRepository; import com.wmdigit.core.hnsw.HnswRepository;
import com.wmdigit.core.opencv.OpencvRepository; import com.wmdigit.core.opencv.OpencvRepository;
import com.wmdigit.core.videopipe.VideoPipeManager; import com.wmdigit.core.videopipe.VideoPipeRepository;
import com.wmdigit.data.database.entity.ProductsPO; import com.wmdigit.data.database.entity.ProductsPO;
import com.wmdigit.data.database.mapper.ProductsMapper; import com.wmdigit.data.database.mapper.ProductsMapper;
import com.wmdigit.data.database.repository.FeaturesRepository; import com.wmdigit.data.database.repository.FeaturesRepository;
import com.wmdigit.data.database.repository.ProductsRepository; import com.wmdigit.data.database.repository.ProductsRepository;
import com.wmdigit.data.mmkv.repository.CropLocalRepository; import com.wmdigit.data.mmkv.repository.CropLocalRepository;
import com.wmdigit.setting.R;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -78,7 +77,7 @@ public class DataLearningViewModel extends BaseViewModel { ...@@ -78,7 +77,7 @@ public class DataLearningViewModel extends BaseViewModel {
/** /**
* 视频流回调 * 视频流回调
*/ */
private final VideoPipeManager.OnVideoPipeEventListener onVideoPipeEventListener = new VideoPipeManager.OnVideoPipeEventListener() { private final VideoPipeRepository.OnVideoPipeEventListener onVideoPipeEventListener = new VideoPipeRepository.OnVideoPipeEventListener() {
@Override @Override
public void onObjectIn() { public void onObjectIn() {
onObjectInEvent(); onObjectInEvent();
...@@ -103,7 +102,7 @@ public class DataLearningViewModel extends BaseViewModel { ...@@ -103,7 +102,7 @@ public class DataLearningViewModel extends BaseViewModel {
keywords.postValue(""); keywords.postValue("");
cameraOpened.postValue(false); cameraOpened.postValue(false);
// 设置视频流回调 // 设置视频流回调
VideoPipeManager.getInstance().setListener(onVideoPipeEventListener); VideoPipeRepository.getInstance().setListener(onVideoPipeEventListener);
} }
/** /**
...@@ -127,11 +126,11 @@ public class DataLearningViewModel extends BaseViewModel { ...@@ -127,11 +126,11 @@ public class DataLearningViewModel extends BaseViewModel {
// 处理Bitmap // 处理Bitmap
if (frameCount > 15){ if (frameCount > 15){
// 喂给视频流 // 喂给视频流
VideoPipeManager.getInstance().processImage(bitmapCopy, true); VideoPipeRepository.getInstance().processImage(bitmapCopy, true);
} }
else if (frameCount == 15){ else if (frameCount == 15){
// 视频流设置空盘背景 // 视频流设置空盘背景
VideoPipeManager.getInstance().setEmptyImage(bitmapCopy, true); VideoPipeRepository.getInstance().setEmptyImage(bitmapCopy, true);
frameCount++; frameCount++;
} }
else{ else{
...@@ -427,7 +426,7 @@ public class DataLearningViewModel extends BaseViewModel { ...@@ -427,7 +426,7 @@ public class DataLearningViewModel extends BaseViewModel {
protected void onCleared() { protected void onCleared() {
super.onCleared(); super.onCleared();
// 注销视频流回调 // 注销视频流回调
VideoPipeManager.getInstance().setListener(null); VideoPipeRepository.getInstance().setListener(null);
// 释放计划任务 // 释放计划任务
if (scheduler != null && !scheduler.isShutdown()){ if (scheduler != null && !scheduler.isShutdown()){
scheduler.shutdown(); scheduler.shutdown();
......
/build
\ No newline at end of file
plugins {
id 'com.android.library'
}
android {
namespace 'com.wmdigit.service'
compileSdk 33
defaultConfig {
minSdk 24
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildFeatures{
aidl true
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
}
task makeJar(type: Jar) {
//指定生成的jar名
// baseName ('WmCateringService_v1.0.2_sdk')
archiveBaseName="WmCateringService_v1.0.2_sdk"
//从哪里打包class文件,根据你的AS版本会所有不同
//但是一定要能在此路径下可以找得到自己写的类
//如果你封装的jar包用起来有问题,很可能是此处出错
from('build/intermediates/javac/debug/classes/')
//去掉不需要打包的目录和文件
exclude('test/','BuildConfig.class','R.class')
//去掉R$开头的文件
exclude{ it.name.startsWith('R$') }
}
makeJar.dependsOn(build)
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
package com.wmdigit.service;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.wmdigit.service.test", appContext.getPackageName());
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
\ No newline at end of file
package com.wmdigit.common.model;
parcelable ProductsDTO;
\ No newline at end of file
package com.wmdigit.service;
import com.wmdigit.service.IOnInitListener;
import com.wmdigit.service.IOnDetectionListener;
import com.wmdigit.service.aidl.model.DetectResult;
import com.wmdigit.common.model.ProductsDTO;
interface ICateringInterface {
void init(IOnInitListener listener);
void importProducts(in List<ProductsDTO> products);
void registerDetectionListener(boolean generateBitmap, IOnDetectionListener listener);
void unregisterDetectionListener();
DetectResult autoDetect(boolean generateBitmap);
boolean checkActivation();
boolean checkCameraCrop();
boolean checkLearningDataInited();
void openSettingPage();
void resetCameraBackground();
}
\ No newline at end of file
package com.wmdigit.service;
import com.wmdigit.service.aidl.model.DetectResult;
interface IOnDetectionListener {
void onDetected(in DetectResult result);
}
\ No newline at end of file
package com.wmdigit.service;
interface IOnInitListener {
void onComplete();
}
\ No newline at end of file
package com.wmdigit.service.aidl.model;
parcelable DetectResult;
package com.wmdigit.common.model;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
/**
* 商品DTO
* @author dizi
*/
public class ProductsDTO implements Parcelable {
/**
* 品名
*/
private String productName;
/**
* 商品代码
*/
private String productCode;
/**
* 商品助记码
*/
private String productMnemonicCode;
/**
* 商品单价(元/份)
*/
private String unitPrice;
/**
* 在售状态,1:在售 2:下架
*/
private Integer onSale;
public ProductsDTO() {
}
public ProductsDTO(String productName, String productCode, String productMnemonicCode, String unitPrice, Integer onSale) {
this.productName = productName;
this.productCode = productCode;
this.productMnemonicCode = productMnemonicCode;
this.unitPrice = unitPrice;
this.onSale = onSale;
}
protected ProductsDTO(Parcel in) {
readFromParcel(in);
}
public static final Creator<ProductsDTO> CREATOR = new Creator<ProductsDTO>() {
@Override
public ProductsDTO createFromParcel(Parcel in) {
return new ProductsDTO(in);
}
@Override
public ProductsDTO[] newArray(int size) {
return new ProductsDTO[size];
}
};
@NonNull
@Override
public String toString() {
return super.toString();
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getProductCode() {
return productCode;
}
public void setProductCode(String productCode) {
this.productCode = productCode;
}
public String getProductMnemonicCode() {
return productMnemonicCode;
}
public void setProductMnemonicCode(String productMnemonicCode) {
this.productMnemonicCode = productMnemonicCode;
}
public String getUnitPrice() {
return unitPrice;
}
public Integer getOnSale() {
return onSale;
}
public void setOnSale(Integer onSale) {
this.onSale = onSale;
}
public void setUnitPrice(String unitPrice) {
this.unitPrice = unitPrice;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(productName);
dest.writeString(productCode);
dest.writeString(productMnemonicCode);
dest.writeString(unitPrice);
if (onSale == null) {
dest.writeByte((byte) 0);
} else {
dest.writeByte((byte) 1);
dest.writeInt(onSale);
}
}
protected void readFromParcel(Parcel in){
productName = in.readString();
productCode = in.readString();
productMnemonicCode = in.readString();
unitPrice = in.readString();
if (in.readByte() == 0) {
onSale = null;
} else {
onSale = in.readInt();
}
}
}
package com.wmdigit.service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.os.RemoteException;
import com.wmdigit.common.model.ProductsDTO;
import com.wmdigit.service.aidl.model.DetectResult;
import com.wmdigit.service.listener.OnServiceConnectListener;
import java.util.List;
/**
* WmSdk类,实现了WmSdkInterface接口,提供了与服务端进行通信的功能
* @author dizi
*/
public class WmSdk implements WmSdkInterface{
/**
* 服务包名,用于绑定服务
*/
private static final String SERVICE_PACKAGE = "com.wmdigit.cateringdetect";
/**
* 服务动作,用于绑定服务
*/
private static final String SERVICE_ACTION = "com.wmdigit.service";
/**
* WmSdk的单例实例
*/
private static WmSdk instance;
/**
* 获取WmSdk的单例实例
* @return WmSdk的实例
*/
public static WmSdk getInstance(){
if (instance == null){
synchronized (WmSdk.class){
if (instance == null){
instance = new WmSdk();
}
}
}
return instance;
}
/**
* 应用程序上下文
*/
private Context context;
/**
* AIDL接口对象
*/
private ICateringInterface aidlInterface;
/**
* 服务连接监听器
*/
private OnServiceConnectListener onServiceConnectListener;
private boolean isServiceConnected = false;
/**
* DeathRecipient用于处理aidl接口对象的死亡消息
*/
private final IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (aidlInterface != null) {
aidlInterface.asBinder().unlinkToDeath(this, 0);
}
}
};
/**
* ServiceConnection对象,用于处理与服务的连接和断开连接
*/
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isServiceConnected = true;
aidlInterface = ICateringInterface.Stub.asInterface(service);
// 注册DeathRecipient
try {
aidlInterface.asBinder().linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
if (onServiceConnectListener != null){
onServiceConnectListener.onConnected(name, service);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
isServiceConnected = false;
if (onServiceConnectListener != null){
onServiceConnectListener.onDisconnected(name);
}
}
};
/**
* 绑定服务
* @param context 应用程序上下文
* @param listener 服务连接监听器,用于处理服务连接状态的变化
*/
@Override
public void bindService(Context context, OnServiceConnectListener listener) {
this.context = context;
this.onServiceConnectListener = listener;
Intent intent = new Intent();
intent.setPackage(SERVICE_PACKAGE);
intent.setAction(SERVICE_ACTION);
context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
/**
* 解绑服务
*/
@Override
public void unbindService() {
if (serviceConnection != null){
context.unbindService(serviceConnection);
}
}
/**
* 初始化SDK
* @param listener 初始化监听器,用于处理初始化过程中的回调
*/
@Override
public void init(IOnInitListener listener) {
try {
aidlInterface.init(listener);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 导入产品数据
* @param products 产品数据列表
*/
@Override
public void importProducts(List<ProductsDTO> products) {
try {
aidlInterface.importProducts(products);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 注册检测监听器
* @param generateBitmap 是否生成位图
* @param listener 检测监听器,用于处理检测过程中的回调
*/
@Override
public void registerDetectionListener(boolean generateBitmap, IOnDetectionListener listener) {
try {
aidlInterface.registerDetectionListener(generateBitmap, listener);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 取消注册检测监听器
*/
@Override
public void unregisterDetectionListener() {
try {
aidlInterface.unregisterDetectionListener();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 自动检测
* @param generateBitmap 是否生成位图
* @return 检测结果
*/
@Override
public DetectResult autoDetect(boolean generateBitmap) {
DetectResult detectResult = null;
try {
detectResult = aidlInterface.autoDetect(generateBitmap);
} catch (Exception e){
e.printStackTrace();
}
return detectResult;
}
/**
* 检查服务应用是否已安装
*
* @param context Android上下文,用于访问包管理器以查询应用安装状态
* @return 如果服务应用已安装,则返回true;否则返回false
*/
@Override
public boolean checkServiceAppInstalled(Context context) {
try{
// 尝试获取服务应用的包信息
context.getPackageManager().getPackageInfo(SERVICE_PACKAGE, 0);
}
catch (PackageManager.NameNotFoundException e){
// 如果未找到包,则服务应用未安装
return false;
}
// 如果成功获取到包信息,则服务应用已安装
return true;
}
/**
* 检查与服务的连接状态
*
* @return 如果服务连接已建立,则返回true;否则返回false
*/
@Override
public boolean checkServiceConnected() {
// 返回当前的服务连接状态
return isServiceConnected;
}
/**
* 检查激活状态
* @return 激活状态,true表示已激活,false表示未激活
*/
@Override
public boolean checkActivation() {
boolean result = false;
try {
result = aidlInterface.checkActivation();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 检查相机裁剪功能
* @return 相机裁剪功能状态,true表示支持,false表示不支持
*/
@Override
public boolean checkCameraCrop() {
boolean result = false;
try{
result = aidlInterface.checkCameraCrop();
} catch (Exception e){
e.printStackTrace();
}
return result;
}
/**
* 检查学习数据是否已初始化
* @return 学习数据初始化状态,true表示已初始化,false表示未初始化
*/
@Override
public boolean checkLearningDataInitCompleted() {
boolean result = false;
try{
result = aidlInterface.checkLearningDataInited();
} catch (Exception e){
e.printStackTrace();
}
return result;
}
/**
* 打开设置页面
*/
@Override
public void openSettingPage() {
try {
aidlInterface.openSettingPage();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 重置相机背景
*/
@Override
public void resetCameraBackground() {
try {
aidlInterface.resetCameraBackground();
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.wmdigit.service;
import android.content.Context;
import com.wmdigit.common.model.ProductsDTO;
import com.wmdigit.service.aidl.model.DetectResult;
import com.wmdigit.service.listener.OnServiceConnectListener;
import java.util.List;
/**
* WmSdk界面接口,定义了一系列与服务交互的方法
*/
public interface WmSdkInterface {
/**
* 绑定服务
*
* @param context 上下文,用于绑定服务
* @param listener 服务连接监听器,处理服务连接状态
*/
void bindService(Context context, OnServiceConnectListener listener);
/**
* 解绑服务
*
* 调用此方法前应确保服务已绑定
*/
void unbindService();
/**
* 初始化SDK
*
* @param listener 初始化监听器,处理初始化结果
*/
void init(IOnInitListener listener);
/**
* 导入产品信息
*
* @param products 产品数据传输对象列表,包含需要导入的产品信息
*/
void importProducts(List<ProductsDTO> products);
/**
* 注册检测监听器
*
* @param generateBitmap 是否生成位图
* @param listener 检测事件监听器,处理检测结果
*/
void registerDetectionListener(boolean generateBitmap, IOnDetectionListener listener);
/**
* 取消注册检测监听器
*
* 调用此方法前应确保检测监听器已注册
*/
void unregisterDetectionListener();
/**
* 自动检测
*
* @param generateBitmap 是否生成位图
* @return 检测结果
*/
DetectResult autoDetect(boolean generateBitmap);
/**
* 检查服务应用程序是否已安装
*
* @param context Android应用程序的上下文,用于访问包管理器等系统服务
* @return 如果服务应用程序已安装返回true,否则返回false
*/
boolean checkServiceAppInstalled(Context context);
/**
* 检查服务是否已连接
*
* @return 如果服务已连接返回true,否则返回false
*/
boolean checkServiceConnected();
/**
* 获取激活状态
*
* @return 激活状态,true表示已激活,false表示未激活
*/
boolean checkActivation();
/**
* 获取摄像头裁剪状态
*
* @return 裁剪状态,true表示开启裁剪,false表示关闭裁剪
*/
boolean checkCameraCrop();
/**
* 获取学习数据初始化状态
*
* @return 初始化状态,true表示已初始化,false表示未初始化
*/
boolean checkLearningDataInitCompleted();
/**
* 打开设置页面
*
* 此方法用于引导用户至应用的设置页面进行相关配置
*/
void openSettingPage();
/**
* 重置摄像头背景
*
* 调用此方法可以重置摄像头的背景至默认状态
*/
void resetCameraBackground();
}
package com.wmdigit.service.aidl.model;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
/**
* DetectResult类,表示检测的结果,实现了Parcelable接口,用于组件间数据传递
* @author dizi
*/
public class DetectResult implements Parcelable {
/**
* 识别结果的状态码,默认为0
*/
private int code = 0;
/**
* 图像位图
*/
private Bitmap bitmap;
/**
* 识别到的产品编码列表
*/
private List<String> productCodes;
public DetectResult() {
this.bitmap = null;
this.productCodes = new ArrayList<>();
}
/**
* 构造函数,初始化检测结果对象
*
* @param code 识别结果的状态码
* @param bitmap 识别的图像位图
* @param productCodes 识别到的产品编码列表
*/
public DetectResult(int code, Bitmap bitmap, List<String> productCodes) {
this.code = code;
this.bitmap = bitmap;
this.productCodes = productCodes != null ? new ArrayList<>(productCodes) : new ArrayList<>();
}
/**
* 构造函数,用于从Parcel中读取数据并重建对象
*
* @param in 用于读取数据的Parcel对象
*/
protected DetectResult(Parcel in) {
readFromParcel(in);
}
/**
* 创建DetectResult对象的Creator实例
*
* @return DetectResult对象的Creator实例
*/
public static final Creator<DetectResult> CREATOR = new Creator<DetectResult>() {
@Override
public DetectResult createFromParcel(Parcel in) {
// 从指定的Parcel中读取数据并创建DetectResult对象
return new DetectResult(in);
}
@Override
public DetectResult[] newArray(int size) {
// 创建并返回DetectResult对象的数组
return new DetectResult[size];
}
};
/**
* 描述内容的类型,返回0表示没有需要特殊处理的内容
*
* @return int类型,表示内容的类型
*/
@Override
public int describeContents() {
return 0;
}
/**
* 将当前对象的数据写入到Parcel中
* 此方法用于对象的序列化,将对象的状态转换为可以在进程间传输的形式
*
* @param dest 用于写入数据的Parcel对象
* @param flags 写入操作的标志,目前未使用但保留以兼容性
*/
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(code);
// 先写入productCodes列表的大小
dest.writeInt(productCodes.size());
// 使用StringBuilder将所有产品编码拼接成一个字符串
StringBuilder sb = new StringBuilder();
for (String productCode : productCodes) {
sb.append(productCode).append(",");
}
dest.writeString(sb.toString());
// 检查bitmap是否为空,并根据情况写入标志和bitmap数据
if (bitmap == null) {
dest.writeInt(0);
} else {
dest.writeInt(1);
dest.writeParcelable(bitmap, flags);
}
}
/**
* 从Parcel中读取数据,恢复当前对象
* 此方法用于对象的反序列化,从Parcel数据中恢复对象的状态
*
* @param in 包含数据的Parcel对象
*/
public void readFromParcel(Parcel in) {
code = in.readInt();
// 先读取productCodes列表的大小
int size = in.readInt();
// 根据读取的大小初始化productCodes列表
String productCodesStr = in.readString();
productCodes = new ArrayList<>();
if (productCodesStr != null && !productCodesStr.isEmpty()) {
String[] codes = productCodesStr.split(",");
for (String code : codes) {
if (!code.isEmpty()) {
productCodes.add(code);
}
}
}
// 读取bitmap是否存在标志
int hasBitmap = in.readInt();
// 根据标志恢复bitmap,如果不存在则置为null
if (hasBitmap == 0) {
bitmap = null;
} else {
// 如果存在,则读取并恢复bitmap对象
try {
bitmap = in.readParcelable(Bitmap.class.getClassLoader());
} catch (Exception e) {
// 异常处理,确保程序不会崩溃
bitmap = null;
e.printStackTrace();
}
}
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
public List<String> getProductCodes() {
return productCodes;
}
public void setProductCodes(List<String> productCodes) {
this.productCodes = productCodes != null ? new ArrayList<>(productCodes) : new ArrayList<>();
}
}
package com.wmdigit.service.listener;
import android.content.ComponentName;
import android.os.IBinder;
/**
* 服务连接监听器接口,用于监听服务的连接和断开事件
*/
public interface OnServiceConnectListener {
/**
* 当服务连接成功时调用
*
* @param name 服务的组件名称
* @param service 服务的Binder对象,用于与服务通信
*/
void onConnected(ComponentName name, IBinder service);
/**
* 当服务断开连接时调用,例如服务进程结束或主动断开连接
*
* @param name 服务的组件名称
*/
void onDisconnected(ComponentName name);
}
package com.wmdigit.service;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
\ No newline at end of file
...@@ -11,6 +11,12 @@ android { ...@@ -11,6 +11,12 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro" consumerProguardFiles "consumer-rules.pro"
javaCompileOptions {
annotationProcessorOptions {
arguments = [ AROUTER_MODULE_NAME : project.getName() ]
}
}
} }
buildTypes { buildTypes {
...@@ -37,4 +43,9 @@ dependencies { ...@@ -37,4 +43,9 @@ dependencies {
implementation project(path: ':common') implementation project(path: ':common')
implementation project(path: ':core') implementation project(path: ':core')
implementation project(path: ':data-local') implementation project(path: ':data-local')
implementation project(path: ':camera')
implementation project(path: ':module-setting')
implementation 'com.alibaba:arouter-api:1.5.2'
annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'
} }
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.CateringDetect">
<service android:name=".aidl.CateringService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="camera|location">
<intent-filter android:priority="1000">
<action android:name="com.wmdigit.service" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>
</manifest> </manifest>
\ No newline at end of file
package com.wmdigit.common.model;
parcelable ProductsDTO;
\ No newline at end of file
...@@ -3,17 +3,20 @@ package com.wmdigit.service; ...@@ -3,17 +3,20 @@ package com.wmdigit.service;
import com.wmdigit.service.IOnInitListener; import com.wmdigit.service.IOnInitListener;
import com.wmdigit.service.IOnDetectionListener; import com.wmdigit.service.IOnDetectionListener;
import com.wmdigit.service.aidl.model.DetectResult; import com.wmdigit.service.aidl.model.DetectResult;
import com.wmdigit.common.model.ProductsDTO;
interface ICateringInterface { interface ICateringInterface {
void init(IOnInitListener listener); void init(IOnInitListener listener);
void registerDetectionListener(IOnDetectionListener listener); void importProducts(in List<ProductsDTO> products);
void registerDetectionListener(boolean generateBitmap, IOnDetectionListener listener);
void unregisterDetectionListener(); void unregisterDetectionListener();
DetectResult autoDetect(); DetectResult autoDetect(boolean generateBitmap);
boolean checkActivation(); boolean checkActivation();
......
...@@ -3,5 +3,5 @@ package com.wmdigit.service; ...@@ -3,5 +3,5 @@ package com.wmdigit.service;
import com.wmdigit.service.aidl.model.DetectResult; import com.wmdigit.service.aidl.model.DetectResult;
interface IOnDetectionListener { interface IOnDetectionListener {
void onDetected(out DetectResult result); void onDetected(in DetectResult result);
} }
\ No newline at end of file
package com.wmdigit.service;
import android.content.Context;
/**
* ServiceModule类作为应用程序上下文的存储和提供者,主要用于在应用的各个部分之间共享上下文信息
*/
public class ServiceModule {
/**
* 保存应用程序的全局上下文,全局上下文在整个应用中是唯一的
*/
private static Context appContext;
/**
* 初始化ServiceModule,设置应用程序上下文
*
* @param context 应用程序上下文,用于初始化appContext
*/
public static void init(Context context){
if (appContext == null){
appContext = context;
}
}
/**
* 获取应用程序的全局上下文
*
* @return 返回保存的应用程序全局上下文,如果未初始化则返回null
*/
public static Context getAppContext() {
return appContext;
}
}
package com.wmdigit.service.aidl; package com.wmdigit.service.aidl;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ServiceInfo;
import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
...@@ -10,6 +18,7 @@ import androidx.lifecycle.LifecycleService; ...@@ -10,6 +18,7 @@ import androidx.lifecycle.LifecycleService;
import com.wmdigit.service.ICateringInterface; import com.wmdigit.service.ICateringInterface;
import com.wmdigit.service.IOnInitListener; import com.wmdigit.service.IOnInitListener;
import com.wmdigit.service.R;
/** /**
* 餐饮AIDL服务 * 餐饮AIDL服务
...@@ -20,7 +29,7 @@ public class CateringService extends LifecycleService { ...@@ -20,7 +29,7 @@ public class CateringService extends LifecycleService {
/** /**
* AIDL接口实现 * AIDL接口实现
*/ */
private final ICateringInterface.Stub stub = new CateringInterfaceImpl(); private final ICateringInterface.Stub stub = new CateringInterfaceImpl(this);
@Nullable @Nullable
@Override @Override
...@@ -28,4 +37,61 @@ public class CateringService extends LifecycleService { ...@@ -28,4 +37,61 @@ public class CateringService extends LifecycleService {
super.onBind(intent); super.onBind(intent);
return stub; return stub;
} }
@Override
public void onCreate() {
super.onCreate();
createNotificationChannel();
}
@Override
public void onDestroy() {
super.onDestroy();
stopForeground(true);
android.os.Process.killProcess(android.os.Process.myPid());
}
private static final String CHANNEL_ID = "my_channel_02";
private static final String CHANNEL_NAME = "服务通知";
private static final String CHANNEL_DESCRIPTION = "识别服务运行中";
private static final int IMPORTANCE = NotificationManager.IMPORTANCE_HIGH;
private static final int NOTIFICATION_ID = 1;
private static final int FOREGROUND_SERVICE_TYPES = FOREGROUND_SERVICE_TYPE_LOCATION | ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
Notification notification = new Notification.Builder(this)
.setContentTitle("通知")
.setContentText("识别服务运行中")
// .setSmallIcon(R.drawable.ic_launcher_foreground) // 启用小图标设置
.build();
startForeground(NOTIFICATION_ID, notification);
return;
}
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (mNotificationManager == null) {
throw new IllegalStateException("Failed to get NotificationManager");
}
NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, IMPORTANCE);
mChannel.setDescription(CHANNEL_DESCRIPTION);
mChannel.enableVibration(false);
// mChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400}); // 如果需要振动则启用此行
mNotificationManager.createNotificationChannel(mChannel);
Notification notification = new Notification.Builder(this)
.setContentTitle("通知")
.setContentText("识别服务正在运行")
// .setSmallIcon(R.drawable.ic_launcher_foreground) // 启用小图标设置
.setChannelId(CHANNEL_ID)
.build();
if (Build.VERSION.SDK_INT >= 30) {
startForeground(NOTIFICATION_ID, notification, FOREGROUND_SERVICE_TYPES);
} else {
startForeground(NOTIFICATION_ID, notification);
}
}
} }
...@@ -9,13 +9,16 @@ import androidx.annotation.NonNull; ...@@ -9,13 +9,16 @@ import androidx.annotation.NonNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* DetectResult类,表示检测的结果,实现了Parcelable接口,用于组件间数据传递 * DetectResult类,表示检测的结果,实现了Parcelable接口,用于组件间数据传递
* @author dizi * @author dizi
*/ */
public class DetectResult implements Parcelable { public class DetectResult implements Parcelable {
/**
* 识别结果的状态码,默认为0
*/
private int code = 0;
/** /**
* 图像位图 * 图像位图
*/ */
...@@ -33,12 +36,14 @@ public class DetectResult implements Parcelable { ...@@ -33,12 +36,14 @@ public class DetectResult implements Parcelable {
/** /**
* 构造函数,初始化检测结果对象 * 构造函数,初始化检测结果对象
* *
* @param bitmap 识别的图像位图 * @param code 识别结果的状态码
* @param bitmap 识别的图像位图
* @param productCodes 识别到的产品编码列表 * @param productCodes 识别到的产品编码列表
*/ */
public DetectResult(Bitmap bitmap, List<String> productCodes) { public DetectResult(int code, Bitmap bitmap, List<String> productCodes) {
this.code = code;
this.bitmap = bitmap; this.bitmap = bitmap;
this.productCodes = productCodes; this.productCodes = productCodes != null ? new ArrayList<>(productCodes) : new ArrayList<>();
} }
/** /**
...@@ -88,17 +93,19 @@ public class DetectResult implements Parcelable { ...@@ -88,17 +93,19 @@ public class DetectResult implements Parcelable {
*/ */
@Override @Override
public void writeToParcel(@NonNull Parcel dest, int flags) { public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(code);
// 先写入productCodes列表的大小 // 先写入productCodes列表的大小
dest.writeInt(productCodes.size()); dest.writeInt(productCodes.size());
// 遍历productCodes列表,逐个写入每个元素 // 使用StringBuilder将所有产品编码拼接成一个字符串
StringBuilder sb = new StringBuilder();
for (String productCode : productCodes) { for (String productCode : productCodes) {
dest.writeString(productCode); sb.append(productCode).append(",");
} }
dest.writeString(sb.toString());
// 检查bitmap是否为空,并根据情况写入标志和bitmap数据 // 检查bitmap是否为空,并根据情况写入标志和bitmap数据
if (bitmap == null){ if (bitmap == null) {
dest.writeInt(0); dest.writeInt(0);
} } else {
else{
dest.writeInt(1); dest.writeInt(1);
dest.writeParcelable(bitmap, flags); dest.writeParcelable(bitmap, flags);
} }
...@@ -110,25 +117,59 @@ public class DetectResult implements Parcelable { ...@@ -110,25 +117,59 @@ public class DetectResult implements Parcelable {
* *
* @param in 包含数据的Parcel对象 * @param in 包含数据的Parcel对象
*/ */
public void readFromParcel(Parcel in){ public void readFromParcel(Parcel in) {
code = in.readInt();
// 先读取productCodes列表的大小 // 先读取productCodes列表的大小
int size = in.readInt(); int size = in.readInt();
// 根据读取的大小初始化productCodes列表 // 根据读取的大小初始化productCodes列表
productCodes = new ArrayList<>(size); String productCodesStr = in.readString();
// 遍历读取的大小,逐个读取并添加到productCodes列表 productCodes = new ArrayList<>();
for (int i = 0; i < size; i++){ if (productCodesStr != null && !productCodesStr.isEmpty()) {
productCodes.add(in.readString()); String[] codes = productCodesStr.split(",");
for (String code : codes) {
if (!code.isEmpty()) {
productCodes.add(code);
}
}
} }
// 读取bitmap是否存在标志 // 读取bitmap是否存在标志
int hasBitmap = in.readInt(); int hasBitmap = in.readInt();
// 根据标志恢复bitmap,如果不存在则置为null // 根据标志恢复bitmap,如果不存在则置为null
if (hasBitmap == 0){ if (hasBitmap == 0) {
bitmap = null; bitmap = null;
} } else {
else{
// 如果存在,则读取并恢复bitmap对象 // 如果存在,则读取并恢复bitmap对象
bitmap = in.readParcelable(Bitmap.class.getClassLoader()); try {
bitmap = in.readParcelable(Bitmap.class.getClassLoader());
} catch (Exception e) {
// 异常处理,确保程序不会崩溃
bitmap = null;
e.printStackTrace();
}
} }
} }
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
public List<String> getProductCodes() {
return productCodes;
}
public void setProductCodes(List<String> productCodes) {
this.productCodes = productCodes != null ? new ArrayList<>(productCodes) : new ArrayList<>();
}
} }
...@@ -34,3 +34,4 @@ include ':module-setting' ...@@ -34,3 +34,4 @@ include ':module-setting'
include ':data-remote' include ':data-remote'
include ':service' include ':service'
include ':opencv' include ':opencv'
include ':service-sdk'
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment