From b5ad951171d43e60470c98932ea68d9d387ceca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=B8=87=E9=87=8C?= <1726150332@qq.com> Date: Mon, 13 Apr 2026 11:19:16 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.gitignore | 3 + .idea/caches/build_file_checksums.ser | Bin 541 -> 541 bytes app/src/main/AndroidManifest.xml | 86 ++- .../java/com/example/tyre/CheckActivity.java | 176 ++++-- .../java/com/example/tyre/DownActivity.java | 8 +- .../com/example/tyre/HomePageActivity.java | 78 ++- .../example/tyre/InStoreHouseActivity.java | 58 +- .../java/com/example/tyre/MainActivity.java | 378 +++++++++++-- .../java/com/example/tyre/MainActivity2.java | 189 +++++++ .../java/com/example/tyre/TestActivity.java | 2 +- .../com/example/tyre/TyreLayoutActivity.java | 507 ++++++++++++++++++ .../java/com/example/tyre/UpActivity.java | 94 +++- .../com/example/tyre/entity/BaseTyre.java | 11 + .../tyre/entity/CheckInfoSpinnerVo.java | 24 + .../example/tyre/entity/InStoreSpinnerVo.java | 9 + .../java/com/example/tyre/util/MyUrl.java | 6 +- app/src/main/res/drawable/bg_tire_circl.xml | 12 + app/src/main/res/drawable/tire_background.xml | 68 +++ app/src/main/res/drawable/tire_image.png | Bin 0 -> 28483 bytes app/src/main/res/drawable/tire_side_view.png | Bin 0 -> 15491 bytes app/src/main/res/layout/activity_check.xml | 496 +++++++++-------- app/src/main/res/layout/activity_down.xml | 42 ++ app/src/main/res/layout/activity_homepage.xml | 27 +- .../main/res/layout/activity_instorehouse.xml | 36 +- app/src/main/res/layout/activity_main.xml | 15 +- app/src/main/res/layout/activity_main2.xml | 49 ++ .../main/res/layout/activity_tyre_layout.xml | 214 ++++++++ app/src/main/res/layout/item_tire.xml | 71 +++ app/src/main/res/layout/item_tire_circle.xml | 34 ++ app/src/main/res/layout/item_tire_right.xml | 39 ++ app/src/main/res/mipmap-xxhdpi/test2.png | Bin 0 -> 8563 bytes app/src/main/res/values/styles.xml | 2 +- app/src/main/res/xml/file_paths.xml | 6 + 33 files changed, 2327 insertions(+), 413 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 app/src/main/java/com/example/tyre/MainActivity2.java create mode 100644 app/src/main/java/com/example/tyre/TyreLayoutActivity.java create mode 100644 app/src/main/java/com/example/tyre/entity/CheckInfoSpinnerVo.java create mode 100644 app/src/main/res/drawable/bg_tire_circl.xml create mode 100644 app/src/main/res/drawable/tire_background.xml create mode 100644 app/src/main/res/drawable/tire_image.png create mode 100644 app/src/main/res/drawable/tire_side_view.png create mode 100644 app/src/main/res/layout/activity_main2.xml create mode 100644 app/src/main/res/layout/activity_tyre_layout.xml create mode 100644 app/src/main/res/layout/item_tire.xml create mode 100644 app/src/main/res/layout/item_tire_circle.xml create mode 100644 app/src/main/res/layout/item_tire_right.xml create mode 100644 app/src/main/res/mipmap-xxhdpi/test2.png create mode 100644 app/src/main/res/xml/file_paths.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 46b8cd5ec7ee0402cf74a17bb33333ead4f01eaa..ddb1594aa3e1dac700176f75e17206e464ad3e93 100644 GIT binary patch delta 37 vcmV+=0NVeZ1f2wsmj#_vC;HT}oI(K*fQ8-M5t|m&txzhMp!Sk7lkNd8I^hu$ delta 37 vcmV+=0NVeZ1f2wsmj#@*xu-g@oI(K*83{_=d=>7n)x%nP8(nZLlkNd8DIX7{ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 70bcdef..215c958 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,9 @@ + + + + + + + + + + + - - - - + + + + + + - - - + - - + + + + - + + + - - - - + + - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/tyre/CheckActivity.java b/app/src/main/java/com/example/tyre/CheckActivity.java index b5a4d8e..355b76e 100644 --- a/app/src/main/java/com/example/tyre/CheckActivity.java +++ b/app/src/main/java/com/example/tyre/CheckActivity.java @@ -25,13 +25,17 @@ import com.android.hdhe.uhf.readerInterface.TagModel; import com.example.tyre.entity.AjaxResult; import com.example.tyre.entity.BaseTyre; import com.example.tyre.entity.Check; +import com.example.tyre.entity.CheckInfoSpinnerVo; import com.example.tyre.entity.EPC; +import com.example.tyre.entity.InStoreSpinnerVo; import com.example.tyre.entity.Tyre; import com.example.tyre.util.CommonDialog; import com.example.tyre.util.MyUrl; import com.example.tyre.util.PlayMusic; import com.example.tyre.util.SharedPreferencesUtils; import com.example.tyre.util.Util; +import com.google.android.material.textfield.TextInputEditText; +import com.google.android.material.textfield.TextInputLayout; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; @@ -51,22 +55,23 @@ import butterknife.OnClick; import cn.pda.serialport.Tools; public class CheckActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener { - @BindView(R.id.epc) - TextView Epc; - @BindView(R.id.checkresult) - TextView checkresult; - @BindView(R.id.tyrecon) - TextView tyrecon; - @BindView(R.id.th) - TextView th; - @BindView(R.id.zbh) - TextView zbh; - @BindView(R.id.result) - Spinner result; - @BindView(R.id.wc) - Button wc; - @BindView(R.id.fh) - Button fh; + @BindView(R.id.epc_display) TextInputEditText epcInput; + @BindView(R.id.th) TextInputEditText tyreNoInput; + @BindView(R.id.zbh) TextInputEditText selfNoInput; + @BindView(R.id.tyrecon) TextInputEditText remarkInput; + @BindView(R.id.patternDepth) TextInputEditText patternDepthInput; + @BindView(R.id.mileage) TextInputEditText mileageInput; + + @BindView(R.id.epc_layout) TextInputLayout epcLayout; + @BindView(R.id.th_layout) TextInputLayout tyreNoLayout; + @BindView(R.id.zbh_layout) TextInputLayout selfNoLayout; + @BindView(R.id.mileage_layout) TextInputLayout mileageNoLayout; + @BindView(R.id.patternDepth_layout) TextInputLayout patternDepthNoLayout; + + @BindView(R.id.result) Spinner resultSpinner; + @BindView(R.id.CheckTypeSpinner) Spinner checkTypeSpinner; + + private boolean isStart = true; private ProgressDialog progressDialog; private boolean runFlag = true; @@ -90,7 +95,8 @@ public class CheckActivity extends AppCompatActivity implements AdapterView.OnIt Thread thread = new InventoryThread(); thread.start(); Util.initSoundPool(this); - result.setOnItemSelectedListener(this); + resultSpinner.setOnItemSelectedListener(this); + checkTypeSpinner.setOnItemSelectedListener(this); check_spinner(); showLoadingDialog(); } @@ -198,16 +204,21 @@ public class CheckActivity extends AppCompatActivity implements AdapterView.OnIt nextTime = lastTime; Log.e("TAG", "run: " + time); } + if (listepc != null && !listepc.isEmpty()){ + startFlag = false; + String currentEpc = listepc.get(0); + epcInput.setText(currentEpc); + //请求后台 + debounceHandler.removeMessages(1); + lastScannedEpc = currentEpc; + debounceHandler.sendEmptyMessageDelayed(1, DEBOUNCE_DELAY); +// showLoadingDialog(); +// find(listepc.get(0).toString()); + } + clearData(); } }); Log.e("EPC", "listepc:+ " + listepc); - if (listepc != null && !listepc.isEmpty()){ - startFlag = false; - Epc.setText(listepc.get(0).toString()); - //请求后台 - find(listepc.get(0).toString()); - } - clearData(); } private void clearData() { listEPC.removeAll(listEPC); @@ -267,7 +278,10 @@ public class CheckActivity extends AppCompatActivity implements AdapterView.OnIt public void onItemSelected(AdapterView parent, View view, int position, long id) { switch (parent.getId()) { case R.id.result: - checkresult.setText(parent.getItemAtPosition(position).toString()); + // checkresult.setText(parent.getItemAtPosition(position).toString()); + break; + case R.id.CheckTypeSpinner: + // checktype.setText(parent.getItemAtPosition(position).toString()); break; } @@ -282,16 +296,31 @@ public class CheckActivity extends AppCompatActivity implements AdapterView.OnIt public void onViewClicked(View view) { switch (view.getId()) { case R.id.wc: - String rfid=Epc.getText().toString(); - String result = checkresult.getText().toString(); - String remark = tyrecon.getText().toString(); - if (rfid == null || rfid.isEmpty()) + String rfid=epcInput.getText().toString(); + String selectedResult = resultSpinner.getSelectedItem().toString(); + String checkType = checkTypeSpinner.getSelectedItem().toString(); + String remark = remarkInput.getText().toString(); + String mileage = mileageInput.getText().toString().trim(); + String patternDepth = patternDepthInput.getText().toString().trim(); + String tyreNo = tyreNoInput.getText().toString().trim(); + String selfNo = selfNoInput.getText().toString().trim(); + if (rfid == null || rfid.isEmpty() || rfid.equals("等待扫描...")) { - new CommonDialog(CheckActivity.this).setMessage("请扫描轮胎!").show(); + epcLayout.setError("请扫描芯片"); return; } - showLoadingDialog(); - insert_check_record(rfid,result,remark); + if (tyreNo == null || tyreNo.isEmpty()) + { + tyreNoLayout.setError("请检查胎号数据"); + return; + } + if (selfNo == null || selfNo.isEmpty()) + { + selfNoLayout.setError("请检查自编号数据"); + return; + } + showLoadingDialog(); + insert_check_record(rfid,selectedResult,remark,checkType,mileage,patternDepth); break; case R.id.fh: Intent intent=new Intent(this,HomePageActivity.class); @@ -300,16 +329,32 @@ public class CheckActivity extends AppCompatActivity implements AdapterView.OnIt } } + // 防抖延迟时间 (毫秒) + private static final long DEBOUNCE_DELAY = 500; + // 用于存储最后一次扫描到的 EPC + private String lastScannedEpc; - - + // 用于处理防抖逻辑的 Handler + private Handler debounceHandler = new Handler(new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + if (msg.what == 1 && lastScannedEpc != null) { + // 延迟时间到,执行查询 + showLoadingDialog(); + performFind(lastScannedEpc); + lastScannedEpc = null; // 清空,等待下一次扫描 + } + return true; + } + }); // 基本信息查询 - private void find(String epc) { + private void performFind(String epc) { OkGo.post(MyUrl.url + "/tyre/tyre/pdaQueryTyreInfo").tag(this).params("tyreEpc", epc) .execute(new StringCallback() { @Override public void onSuccess(Response response) { String body = response.body(); + hideLoadingDialog(); try { Gson gson = new Gson(); BaseTyre baseTyre = gson.fromJson(body, BaseTyre.class); @@ -317,12 +362,12 @@ public class CheckActivity extends AppCompatActivity implements AdapterView.OnIt // 空值处理:若字段为 null 则显示空字符串 String TyreNo = safeGetString(baseTyre.getTyreNo()); String zb = safeGetString(baseTyre.getSelfNo()); - th.setText(TyreNo); - zbh.setText(zb); + tyreNoInput.setText(TyreNo); + selfNoInput.setText(zb); }else { new CommonDialog(CheckActivity.this).setMessage("系统无此轮胎!").show(); - th.setText(""); - zbh.setText(""); + tyreNoInput.setText(""); + selfNoInput.setText(""); } } catch (JsonSyntaxException e) { return; @@ -335,37 +380,56 @@ public class CheckActivity extends AppCompatActivity implements AdapterView.OnIt return value == null || "null".equals(value) ? "" : value; } private void check_spinner() { - OkGo.post(MyUrl.url + "/system/dict/data//checkResultList").execute(new StringCallback() { + OkGo.post(MyUrl.url + "/system/dict/data/getDictDataList").execute(new StringCallback() { @Override public void onSuccess(Response response) { String body = response.body(); Gson gson = new Gson(); hideLoadingDialog(); - List stringList =new ArrayList<>(); - stringList = gson.fromJson(body, new TypeToken>() { - }.getType()); + CheckInfoSpinnerVo checkInfoSpinnerVo =new CheckInfoSpinnerVo(); + checkInfoSpinnerVo = gson.fromJson(body,CheckInfoSpinnerVo.class); + List stringList=checkInfoSpinnerVo.getCheckResultList(); + List checkTypeList=checkInfoSpinnerVo.getCheckTypeList(); +// List stringList =new ArrayList<>(); +// stringList = gson.fromJson(body, new TypeToken>() { +// }.getType()); ArrayAdapter arrayAdapter = new ArrayAdapter<>(CheckActivity.this, android.R.layout.simple_list_item_1, stringList); - result.setAdapter(arrayAdapter); - } - }); - result.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - checkresult.setText(parent.getItemAtPosition(position).toString()); - } - - @Override - public void onNothingSelected(AdapterView parent) { + resultSpinner.setAdapter(arrayAdapter); + ArrayAdapter checkTypeAdapter = new ArrayAdapter<>(CheckActivity.this, android.R.layout.simple_list_item_1, checkTypeList); + checkTypeSpinner.setAdapter(checkTypeAdapter); } }); +// result.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { +// @Override +// public void onItemSelected(AdapterView parent, View view, int position, long id) { +// //checkresult.setText(parent.getItemAtPosition(position).toString()); +// } +// +// @Override +// public void onNothingSelected(AdapterView parent) { +// } +// }); +// result.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { +// @Override +// public void onItemSelected(AdapterView parent, View view, int position, long id) { +// // checktype.setText(parent.getItemAtPosition(position).toString()); +// } +// +// @Override +// public void onNothingSelected(AdapterView parent) { +// } +// }); } //插质检记录表 - private void insert_check_record(String rfid,String result,String remark){ + private void insert_check_record(String rfid,String selectedResult,String remark,String checkType,String mileage,String patternDepth){ OkGo.post(MyUrl.url + "/tyre/check/PdaAddCheck") .params("tyreRfid",rfid) - .params("result",result) + .params("result",selectedResult) .params("remark",remark) + .params("maintenanceType",checkType) + .params("mileage",mileage) + .params("patternDepth",patternDepth) .params("CreateBy", SharedPreferencesUtils.getstring("user","admin")) .execute(new StringCallback() { @Override diff --git a/app/src/main/java/com/example/tyre/DownActivity.java b/app/src/main/java/com/example/tyre/DownActivity.java index 125d4ad..821f1ca 100644 --- a/app/src/main/java/com/example/tyre/DownActivity.java +++ b/app/src/main/java/com/example/tyre/DownActivity.java @@ -69,6 +69,8 @@ public class DownActivity extends AppCompatActivity implements AdapterView.OnIte TextView th; @BindView(R.id.zbh) TextView zbh; + @BindView(R.id.patternDepth) + EditText patternDepth; private boolean isStart = true; private ProgressDialog progressDialog; private boolean runFlag = true; @@ -270,6 +272,7 @@ public class DownActivity extends AppCompatActivity implements AdapterView.OnIte String carNo = car.getText().toString(); String millage = start.getText().toString(); String wheel = azlw.getText().toString(); + String depth = patternDepth.getText().toString(); if (rfid == null || rfid.isEmpty()) { new CommonDialog(DownActivity.this).setMessage("请扫描轮胎!").show(); @@ -291,7 +294,7 @@ public class DownActivity extends AppCompatActivity implements AdapterView.OnIte return; } showLoadingDialog(); - down_insert(rfid,carNo,millage,wheel); + down_insert(rfid,carNo,millage,wheel,depth); break; case R.id.back: Intent intent = new Intent(this, HomePageActivity.class); @@ -346,12 +349,13 @@ public class DownActivity extends AppCompatActivity implements AdapterView.OnIte return value == null || "null".equals(value) ? "" : value; } // 删除实时表、卸车记录表 - private void down_insert(String rfid,String carNo,String millage,String wheel) { + private void down_insert(String rfid,String carNo,String millage,String wheel,String patternDepth) { OkGo.post(MyUrl.url + "/tyre/install/PdaUnInstallTyre").tag(this) .params("tyreRfid", rfid) .params("mileage", millage) .params("carNo", carNo) .params("wheelPostion", wheel) + .params("patternDepth", patternDepth) .params("CreateBy", SharedPreferencesUtils.getstring("user","admin")) .execute(new StringCallback() { @Override diff --git a/app/src/main/java/com/example/tyre/HomePageActivity.java b/app/src/main/java/com/example/tyre/HomePageActivity.java index 22a88c1..32132b1 100644 --- a/app/src/main/java/com/example/tyre/HomePageActivity.java +++ b/app/src/main/java/com/example/tyre/HomePageActivity.java @@ -33,66 +33,94 @@ public class HomePageActivity extends AppCompatActivity { RadioButton zhijian; @BindView(R.id.logout) Button logout; - private long exitTime=0; - @Override + private long exitTime = 0; + + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_homepage); ButterKnife.bind(this); } - @OnClick({R.id.chaxun, R.id.ruku, R.id.chuku, R.id.zhuangxie, R.id.huanwei,R.id.zhijian,R.id.xxbd}) + /** + * 修改点 1:在注解列表中加入 R.id.logout + */ + @OnClick({R.id.chaxun, R.id.ruku, R.id.chuku, R.id.zhuangxie, R.id.huanwei, R.id.zhijian, R.id.xxbd,R.id.test1, R.id.logout}) public void onViewClicked(View view) { - Intent intent = new Intent(this,MainActivity.class); + Intent intent; // 建议在这里声明,而不是方法最开始 + switch (view.getId()) { case R.id.chaxun: - intent=new Intent(this,TyreSearchActivity.class); + intent = new Intent(this, TyreSearchActivity.class); break; case R.id.ruku: - intent=new Intent(this,InStoreHouseActivity.class); + intent = new Intent(this, InStoreHouseActivity.class); break; case R.id.chuku: - intent=new Intent(this,OutStoreHouseActivity.class); + intent = new Intent(this, OutStoreHouseActivity.class); break; case R.id.zhuangxie: - intent=new Intent(this,UpActivity.class); + intent = new Intent(this, UpActivity.class); break; case R.id.huanwei: - intent=new Intent(this,DownActivity.class); + intent = new Intent(this, DownActivity.class); break; case R.id.zhijian: - intent=new Intent(this,CheckActivity.class); - break; - case R.id.logout: - finish(); + intent = new Intent(this, CheckActivity.class); break; case R.id.xxbd: - intent=new Intent(this,TestActivity.class); + intent = new Intent(this, TestActivity.class); break; - + case R.id.test1: + intent = new Intent(this, TyreLayoutActivity.class); + break; + case R.id.logout: + // 修改点 2:处理退出逻辑 + handleLogout(); + return; // 退出后直接返回,不需要执行下面的 startActivity + default: + return; } + + // 只有非退出按钮才执行跳转 startActivity(intent); } + /** + * 新增:处理退出登录的逻辑 + */ + private void handleLogout() { + // 1. 清除登录状态(可选:清除 SharedPreferences 中的用户信息) + // 例如:SharedPreferencesUtils.clear(); + + // 2. 提示用户 + Toast.makeText(this, "已退出登录", Toast.LENGTH_SHORT).show(); + + // 3. 跳转回登录页 (MainActivity) + Intent intent = new Intent(this, MainActivity.class); + startActivity(intent); + + // 4. 关闭当前主页,防止按返回键回到主页 + finish(); + } + private void exit() { if ((System.currentTimeMillis() - exitTime) > 2000) { Toast.makeText(getApplicationContext(), "再按一次退出程序", Toast.LENGTH_SHORT).show(); exitTime = System.currentTimeMillis(); + } else { + finish(); + // System.exit(0); // 通常不需要手动调用 System.exit,finish() 即可 } - else{ - finish(); - System.exit(0); - } - - } + } @Override - public boolean onKeyDown(int keyCode,KeyEvent event){ - if(keyCode== KeyEvent.KEYCODE_BACK){ + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { exit(); return false; } - return super.onKeyDown(keyCode,event); + return super.onKeyDown(keyCode, event); } - } \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/java/com/example/tyre/InStoreHouseActivity.java b/app/src/main/java/com/example/tyre/InStoreHouseActivity.java index d680a98..9627903 100644 --- a/app/src/main/java/com/example/tyre/InStoreHouseActivity.java +++ b/app/src/main/java/com/example/tyre/InStoreHouseActivity.java @@ -69,6 +69,8 @@ public class InStoreHouseActivity extends AppCompatActivity implements AdapterVi TextView cengji; @BindView(R.id.huawen) TextView huawen; + @BindView(R.id.gcts) + TextView gcts; @BindView(R.id.back) Button back; @BindView(R.id.wtbm) @@ -97,6 +99,9 @@ public class InStoreHouseActivity extends AppCompatActivity implements AdapterVi @BindView(R.id.spinnerBrand) Spinner spinnerBrand; + + @BindView(R.id.spinnergcts) + Spinner spinnergcts; // private UHFService mDevice; // private MyHandler handler; private boolean isStart = true; @@ -119,6 +124,7 @@ public class InStoreHouseActivity extends AppCompatActivity implements AdapterVi spinnerlevel.setOnItemSelectedListener(this); spinnerSize.setOnItemSelectedListener(this); spinnerBrand.setOnItemSelectedListener(this); + spinnergcts.setOnItemSelectedListener(this); initView(); Thread thread = new InventoryThread(); thread.start(); @@ -249,17 +255,24 @@ public class InStoreHouseActivity extends AppCompatActivity implements AdapterVi nextTime = lastTime; Log.e("TAG", "run: " + time); } + if (listepc != null && !listepc.isEmpty()){ + startFlag = false; + String currentEpc = listepc.get(0); + scan.setText(currentEpc); + + } + clearData(); } }); Log.e("EPC", "listepc:+ " + listepc); - if (listepc != null && !listepc.isEmpty()){ - startFlag = false; -// scan.setText("测试"); - scan.setText(listepc.get(0).toString()); - //请求后台 - //find(listepc.get(0).toString()); - } - clearData(); +// if (listepc != null && !listepc.isEmpty()){ +// startFlag = false; +//// scan.setText("测试"); +// scan.setText(listepc.get(0).toString()); +// //请求后台 +// find(listepc.get(0).toString()); +// } +// clearData(); } private void clearData() { listEPC.removeAll(listEPC); @@ -279,6 +292,8 @@ public class InStoreHouseActivity extends AppCompatActivity implements AdapterVi BaseTyre baseTyre = gson.fromJson(body, BaseTyre.class); if (baseTyre != null) { // 空值处理:若字段为 null 则显示空字符串 + String tyreNo = safeGetString(baseTyre.getTyreNo()); + wtbm.setText(tyreNo); String brand = safeGetString(baseTyre.getTyreBrand()); String model = safeGetString(baseTyre.getTyreModel()); String level = safeGetString(baseTyre.getTyreLevel()); @@ -300,7 +315,7 @@ public class InStoreHouseActivity extends AppCompatActivity implements AdapterVi return value == null || "null".equals(value) ? "" : value; } //添加到库存表 - private void insert_inventory(String epc,String wtbm,String pinpai,String xinghao,String cengji,String huawen,String tyrekind) { + private void insert_inventory(String epc,String wtbm,String pinpai,String xinghao,String cengji,String huawen,String grooves,String tyrekind) { OkGo.post(MyUrl.url + "/tyre/inventory/pdaAddInventory").tag(this) .params("tyreRfid", epc) .params("tyreEpc",epc) @@ -309,6 +324,7 @@ public class InStoreHouseActivity extends AppCompatActivity implements AdapterVi .params("tyreModel",xinghao) .params("tyreLevel",cengji) .params("tyrePattern",huawen) + .params("grooves",grooves) .params("tyreType",tyrekind) .params("CreateBy",SharedPreferencesUtils.getstring("user","admin")).execute(new StringCallback() { @Override @@ -319,6 +335,11 @@ public class InStoreHouseActivity extends AppCompatActivity implements AdapterVi AjaxResult ajaxResult = gson.fromJson(body, AjaxResult.class); handleResponse(ajaxResult); } + @Override + public void onError(Response response) { + hideLoadingDialog(); + Toast.makeText(InStoreHouseActivity.this, "请求失败,请检查网络或重试", Toast.LENGTH_SHORT).show(); + } }); } private void handleResponse(AjaxResult result) { @@ -348,12 +369,13 @@ public class InStoreHouseActivity extends AppCompatActivity implements AdapterVi String c=cengji.getText().toString(); String h=huawen.getText().toString(); String t=tyrekind.getText().toString(); + String g=gcts.getText().toString(); if (scan.getText().toString()==""){ new CommonDialog(InStoreHouseActivity.this).setMessage("请扫描轮胎芯片!").show(); return; } showLoadingDialog(); - insert_inventory(epc,w,p,x,c,h,t); + insert_inventory(epc,w,p,x,c,h,g,t); // } break; case R.id.back: @@ -385,6 +407,9 @@ public class InStoreHouseActivity extends AppCompatActivity implements AdapterVi case R.id.spinnerBrand: pinpai.setText(parent.getItemAtPosition(position).toString()); break; + case R.id.spinnergcts: + gcts.setText(parent.getItemAtPosition(position).toString()); + break; } } private Toast mToast; @@ -438,17 +463,19 @@ public class InStoreHouseActivity extends AppCompatActivity implements AdapterVi List patternLevel=inStoreSpinnerVo.getLevelList(); List patternSize=inStoreSpinnerVo.getTyreSizeList(); List patternBrand=inStoreSpinnerVo.getTyreBrandList(); + List patternGcts=inStoreSpinnerVo.getGctsList(); ArrayAdapter arrayAdapter = new ArrayAdapter<>(InStoreHouseActivity.this, android.R.layout.simple_list_item_1, stringList); ArrayAdapter arrayAdapterPattern = new ArrayAdapter<>(InStoreHouseActivity.this, android.R.layout.simple_list_item_1, patternList); ArrayAdapter arrayAdapterLevel = new ArrayAdapter<>(InStoreHouseActivity.this, android.R.layout.simple_list_item_1, patternLevel); ArrayAdapter arrayAdapterSize = new ArrayAdapter<>(InStoreHouseActivity.this, android.R.layout.simple_list_item_1, patternSize); ArrayAdapter arrayAdapterBrand = new ArrayAdapter<>(InStoreHouseActivity.this, android.R.layout.simple_list_item_1, patternBrand); - + ArrayAdapter arrayAdapterGcts = new ArrayAdapter<>(InStoreHouseActivity.this, android.R.layout.simple_list_item_1, patternGcts); spinnerkind.setAdapter(arrayAdapter); spinnerpattern.setAdapter(arrayAdapterPattern); spinnerlevel.setAdapter(arrayAdapterLevel); spinnerSize.setAdapter(arrayAdapterSize); spinnerBrand.setAdapter(arrayAdapterBrand); + spinnergcts.setAdapter(arrayAdapterGcts); } }); spinnerkind.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @@ -501,7 +528,16 @@ public class InStoreHouseActivity extends AppCompatActivity implements AdapterVi public void onNothingSelected(AdapterView parent) { } }); + spinnergcts.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + gcts.setText(parent.getItemAtPosition(position).toString()); + } + @Override + public void onNothingSelected(AdapterView parent) { + } + }); } private KeyReceiver keyReceiver; private void registerReceiver() { diff --git a/app/src/main/java/com/example/tyre/MainActivity.java b/app/src/main/java/com/example/tyre/MainActivity.java index a843fb2..f059324 100644 --- a/app/src/main/java/com/example/tyre/MainActivity.java +++ b/app/src/main/java/com/example/tyre/MainActivity.java @@ -1,29 +1,55 @@ package com.example.tyre; +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.DownloadManager; import android.app.ProgressDialog; +import android.content.ActivityNotFoundException; +import android.content.BroadcastReceiver; +import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; import android.os.Bundle; +import android.os.Environment; +import android.os.Process; +import android.util.Log; import android.widget.Button; +import android.widget.CheckBox; import android.widget.EditText; -import android.widget.ListPopupWindow; +import android.widget.TextView; import android.widget.Toast; import com.example.tyre.entity.AjaxResult; -import com.example.tyre.entity.Tyre; import com.example.tyre.util.CommonDialog; import com.example.tyre.util.MyUrl; import com.example.tyre.util.SharedPreferencesUtils; import com.google.gson.Gson; import com.lzy.okgo.OkGo; +import com.lzy.okgo.callback.FileCallback; import com.lzy.okgo.callback.StringCallback; +import com.lzy.okgo.model.Progress; import com.lzy.okgo.model.Response; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.core.content.FileProvider; + +import org.json.JSONObject; + +import java.io.File; + import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; -//implements OnTouchListener,OnItemClickListener public class MainActivity extends AppCompatActivity { @BindView(R.id.login_name) EditText loginName; @@ -31,54 +57,190 @@ public class MainActivity extends AppCompatActivity { EditText loginPass; @BindView(R.id.login_log) Button loginLog; - private EditText etTest; - private ListPopupWindow lpw; - private String[] list; + @BindView(R.id.checkbox_remember) + CheckBox cbRemember; + @BindView(R.id.version) + TextView version; + // 声明进度条变量 private ProgressDialog progressDialog; + private static final int PERMISSION_REQUEST_CODE = 1001; + private BroadcastReceiver downloadReceiver; + + private static final int REQUEST_PERMISSIONS = 100; + private long downloadId; + private DownloadManager downloadManager; + private String newVersionName; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); - loginName.setText("admin"); - loginPass.setText("haiwei@123"); -// etTest = (EditText) findViewById(R.id.login_name); -// etTest.setOnTouchListener(this); -// list = new String[] { "item1", "item2", "item3", "item4" }; -// lpw = new ListPopupWindow(this); -// lpw.setAdapter(new ArrayAdapter(this,android.R.layout.simple_list_item_1, list)); -// lpw.setAnchorView(etTest); -// lpw.setModal(true); -// lpw.setOnItemClickListener(this); + // --- 新增:页面加载时读取保存的账号密码 --- + initRememberInfo(); + // 2. 检查权限并开始检查更新 + //showLoadingDialog(); + //checkPermissionsAndUpdate(); + // 注册下载完成广播接收器 + //registerReceiver(onDownloadComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); + } + /** + * 检查权限并开始版本更新检查 + */ + private void checkPermissionsAndUpdate() { + // Android 6.0+ 动态申请存储权限(用于下载APK) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, + new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + PERMISSION_REQUEST_CODE); + } else { + // 权限已获取,开始检查更新 + checkAppUpdate(); + } + } else { + // 版本低于 6.0,直接检查更新 + checkAppUpdate(); + } + } + /** + * 初始化“记住密码”的数据回显 + */ + private void initRememberInfo() { + // 假设你在 SharedPreferencesUtils 中保存的 key 是 "user" 和 "password" + String savedUser = SharedPreferencesUtils.getstring("user", ""); + String savedPass = SharedPreferencesUtils.getstring("password", ""); + + // 如果用户名不为空,说明之前勾选过,则填充数据并勾选CheckBox + if (savedUser != null && !savedUser.isEmpty()) { + loginName.setText(savedUser); + loginPass.setText(savedPass); + cbRemember.setChecked(true); + } else { + cbRemember.setChecked(false); + } } @OnClick(R.id.login_log) public void onViewClicked() { - showLoadingDialog(); - login(loginName.getText().toString(),loginPass.getText().toString()); - } -// @Override -// public void onItemClick(AdapterView parent, View view, int position,long id) {String item = list[position];etTest.setText(item);lpw.dismiss(); -// } -// @Override -// public boolean onTouch(View v, MotionEvent event) {final int DRAWABLE_RIGHT = 2; -// if (event.getAction() == MotionEvent.ACTION_UP) {if (event.getX() >= (v.getWidth() - ((EditText) v) .getCompoundDrawables()[DRAWABLE_RIGHT].getBounds().width())) {lpw.show();return true; -// } -// } -// return false; -// } - private void showLoadingDialog() { - progressDialog = new ProgressDialog(this); - progressDialog.setMessage("数据加载中..."); - progressDialog.setCancelable(false); - progressDialog.show(); - } - private void hideLoadingDialog() { - if (progressDialog != null && progressDialog.isShowing()) { - progressDialog.dismiss(); + // 获取输入内容 + String name = loginName.getText().toString().trim(); + String pass = loginPass.getText().toString().trim(); + + // --- 修改:在点击登录时立即获取CheckBox状态 --- + // 注意:这里直接用 cbRemember.isChecked(),不要等到网络请求回来再判断 + boolean isRemember = cbRemember.isChecked(); + + // 简单的非空校验 + if (name.isEmpty() || pass.isEmpty()) { + Toast.makeText(this, "用户名或密码不能为空", Toast.LENGTH_SHORT).show(); + return; } + + showLoadingDialog(); + login(name, pass, isRemember); // 将状态传递给 login 方法 } - private void login(String name, String pass) { + + /** + * 核心逻辑:检查更新 + */ + private void checkAppUpdate() { + // 1. 获取本地版本号 (从 AndroidManifest.xml 读取) + String localVersionName = getVersionName(); + Log.d("Version", "本地版本: " + localVersionName); + // 2. 请求服务器接口获取最新版本 + // 假设接口返回格式: { "code": "0", "data": { "versionCode": 2, "apkUrl": "..." } } + OkGo.post(MyUrl.url + "/system/package/checkUpdate") // 替换为你实际的检查更新接口地址 + .tag(this) + .execute(new StringCallback() { + @Override + public void onSuccess(Response response) { + hideLoadingDialog(); + try { + JSONObject jsonObject = new JSONObject(response.body()); + String code = jsonObject.getString("code"); + if ("0".equals(code)) { + JSONObject data = jsonObject.getJSONObject("data"); +// // 获取服务器版本号 (String) + String serverVersionName = data.getString("version"); + String apkUrl = data.getString("fileName"); + int compareResult = compareVersion(serverVersionName, localVersionName); + runOnUiThread(() -> { + version.setText("当前版本: " + localVersionName + " | 最新版本: " + serverVersionName); + }); +// // 3. 对比版本号 +// // 如果服务器版本 > 本地版本 + if (compareResult > 0) { + showUpdateDialog(apkUrl, serverVersionName); + } +// if (compareVersion(serverVersionName, localVersionName) > 0) { +// showUpdateDialog(apkUrl); +// } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + /** + * 显示更新对话框 + */ + private void showUpdateDialog(String downloadUrl, String serverVersion) { + this.newVersionName = serverVersion; + new AlertDialog.Builder(this) + .setTitle("发现新版本 " + serverVersion) + .setMessage("检测到最新版本,是否立即下载并安装?") + .setPositiveButton("立即下载", (dialog, which) -> startDownload(downloadUrl)) + .setNegativeButton("取消", null) + .show(); + } + + private String getVersionName() { + // 1. 先尝试从 SP 读取(这个值是上次下载更新时存进去的最新版本) + + // 3. 如果 SP 里没有(说明是第一次安装或没更新过),读取 build.gradle 里的 versionName + try { + PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0); + return packageInfo.versionName; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return "1.0"; + } + private int compareVersion(String v1, String v2) { + // 如果两个版本号完全相同,直接返回0 + if (v1.equals(v2)) { + return 0; + } + + // 将版本号按 "." 分割成数组 + String[] v1Array = v1.split("\\."); + String[] v2Array = v2.split("\\."); + + // 获取最大长度,确保能遍历完所有部分 + int maxLength = Math.max(v1Array.length, v2Array.length); + + for (int i = 0; i < maxLength; i++) { + // 获取当前位的数值,如果该位置不存在(比如 1.0 对比 1.0.1),则默认为 0 + int v1Part = i < v1Array.length ? Integer.parseInt(v1Array[i]) : 0; + int v2Part = i < v2Array.length ? Integer.parseInt(v2Array[i]) : 0; + + // 逐位比较 + if (v1Part > v2Part) { + return 1; // v1 大 + } else if (v1Part < v2Part) { + return -1; // v2 大 + } + } + return 0; // 相等 + } + /** + * 登录请求 + * @param isRemember 是否勾选了记住密码 + */ + private void login(String name, String pass, boolean isRemember) { OkGo.post(MyUrl.url + "/PdaLogin") .tag(this) .params("username", name) @@ -90,9 +252,23 @@ public class MainActivity extends AppCompatActivity { String body = response.body(); Gson gson = new Gson(); AjaxResult ajaxResult = gson.fromJson(body, AjaxResult.class); + + // 处理登录结果 handleLoginResponse(ajaxResult); - SharedPreferencesUtils.putstring("user",name); - SharedPreferencesUtils.putstring("password",pass); + + // --- 修改:在这里处理保存逻辑 --- + // 如果登录成功(假设 code 为 "0" 代表成功),且用户勾选了记住密码,则保存 + // 如果用户没勾选,则清空保存的数据 + if ("0".equals(ajaxResult.getCode())) { + if (isRemember) { + SharedPreferencesUtils.putstring("user", name); + SharedPreferencesUtils.putstring("password", pass); + } else { + // 如果没勾选,清除本地存储的密码(可选) + SharedPreferencesUtils.putstring("user", ""); + SharedPreferencesUtils.putstring("password", ""); + } + } } @Override @@ -103,11 +279,28 @@ public class MainActivity extends AppCompatActivity { }); } + private void showLoadingDialog() { + if (progressDialog == null) { + progressDialog = new ProgressDialog(this); + progressDialog.setMessage("数据加载中..."); + progressDialog.setCancelable(false); + } + if (!progressDialog.isShowing()) { + progressDialog.show(); + } + } + + private void hideLoadingDialog() { + if (progressDialog != null && progressDialog.isShowing()) { + progressDialog.dismiss(); + } + } + private void handleLoginResponse(AjaxResult result) { switch (result.getCode()) { case "0": + // 登录成功 Toast.makeText(this, result.getMsg(), Toast.LENGTH_SHORT).show(); - //SharedPreferencesUtils.putString("UserID", name); Intent intent = new Intent(this, HomePageActivity.class); startActivity(intent); finish(); @@ -120,4 +313,103 @@ public class MainActivity extends AppCompatActivity { break; } } -} + /** + * 开始下载 APK + * @param fileName 服务器上的文件名 (例如: "app-release.apk" 或若依数据库里的 "resourceName") + */ + private void startDownload(String fileName) { + // 1. 构建完整的下载 URL + String downloadUrl = MyUrl.url + "/common/downloadApk?fileName=tyre_update.apk"; + + downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); + DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadUrl)); + request.setTitle("APK下载"); + request.setDescription("正在下载更新..."); + request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); + request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + request.setRequiresDeviceIdle(false); + request.setAllowedOverMetered(true); + request.setAllowedOverRoaming(true); + } + + downloadId = downloadManager.enqueue(request); + Toast.makeText(this, "开始下载...", Toast.LENGTH_SHORT).show(); + } + + + + + private BroadcastReceiver onDownloadComplete = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); + + if (downloadId == id) { + SharedPreferencesUtils.putstring("local_app_version", newVersionName); + runOnUiThread(() -> { + // 这里更新界面上的 TextView + version.setText("当前版本: " + newVersionName); // ⭐ 修改:直接显示新版本 + }); + DownloadManager.Query query = new DownloadManager.Query(); + query.setFilterById(id); + + Cursor cursor = downloadManager.query(query); + if (cursor.moveToFirst()) { + int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)); + + if (status == DownloadManager.STATUS_SUCCESSFUL) { + String localUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)); + installApk(Uri.parse(localUri).getPath()); + } else { + int reason = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON)); + Toast.makeText(context, "下载失败,错误码: " + reason, Toast.LENGTH_SHORT).show(); + } + } + cursor.close(); + } + } + }; + private void installApk(String apkPath) { + File file = new File(apkPath); + if (!file.exists()) { + Toast.makeText(this, "APK文件不存在", Toast.LENGTH_SHORT).show(); + return; + } + + Intent intent = new Intent(Intent.ACTION_VIEW); + Uri uri; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + uri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", file); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + } else { + uri = Uri.fromFile(file); + } + + intent.setDataAndType(uri, "application/vnd.android.package-archive"); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + try { + startActivity(intent); + } catch (Exception e) { + Toast.makeText(this, "无法安装应用: " + e.getMessage(), Toast.LENGTH_LONG).show(); + } + } + @Override + protected void onDestroy() { + super.onDestroy(); + // unregisterReceiver(onDownloadComplete); + } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == 1001) { + if (resultCode == RESULT_OK) { + Toast.makeText(this, "已获得安装未知应用权限", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(this, "未获得安装未知应用权限,无法自动安装", Toast.LENGTH_LONG).show(); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/tyre/MainActivity2.java b/app/src/main/java/com/example/tyre/MainActivity2.java new file mode 100644 index 0000000..11b15bc --- /dev/null +++ b/app/src/main/java/com/example/tyre/MainActivity2.java @@ -0,0 +1,189 @@ +package com.example.tyre; + + +import android.Manifest; +import android.app.DownloadManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.provider.Settings; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.core.content.FileProvider; + +import com.example.tyre.util.MyUrl; + +import java.io.File; + +public class MainActivity2 extends AppCompatActivity { + + private static final int REQUEST_PERMISSIONS = 100; + private long downloadId; + private DownloadManager downloadManager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main2); + + Button downloadButton = findViewById(R.id.download_button); + + // 注册下载完成广播接收器 + registerReceiver(onDownloadComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); + + downloadButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (checkPermissions()) { + startDownload("app-release.apk"); + } else { + requestPermissions(); + } + } + }); + downloadButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (checkPermissions()) { + startDownload("app-release.apk"); + } else { + requestPermissions(); + } + } + }); + } + + private boolean checkPermissions() { + boolean storagePermissionGranted = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; + boolean installPermissionGranted = true; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + installPermissionGranted = getPackageManager().canRequestPackageInstalls(); + } + + return storagePermissionGranted && installPermissionGranted; + } + + private void requestPermissions() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + ActivityCompat.requestPermissions(this, + new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + REQUEST_PERMISSIONS); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (!getPackageManager().canRequestPackageInstalls()) { + startActivityForResult(new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES) + .setData(Uri.parse(String.format("package:%s", getPackageName()))), 1001); + } + } + } + + /** + * 开始下载 APK + * @param fileName 服务器上的文件名 (例如: "app-release.apk" 或若依数据库里的 "resourceName") + */ + private void startDownload(String fileName) { + // 1. 构建完整的下载 URL + String downloadUrl = MyUrl.url + "/common/downloadApk?fileName=tyre_update.apk"; + + downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); + DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadUrl)); + request.setTitle("APK下载"); + request.setDescription("正在下载更新..."); + request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); + request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + request.setRequiresDeviceIdle(false); + request.setAllowedOverMetered(true); + request.setAllowedOverRoaming(true); + } + + downloadId = downloadManager.enqueue(request); + Toast.makeText(this, "开始下载...", Toast.LENGTH_SHORT).show(); + } + + private BroadcastReceiver onDownloadComplete = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); + + if (downloadId == id) { + DownloadManager.Query query = new DownloadManager.Query(); + query.setFilterById(id); + + Cursor cursor = downloadManager.query(query); + if (cursor.moveToFirst()) { + int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)); + + if (status == DownloadManager.STATUS_SUCCESSFUL) { + String localUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)); + installApk(Uri.parse(localUri).getPath()); + } else { + int reason = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON)); + Toast.makeText(context, "下载失败,错误码: " + reason, Toast.LENGTH_SHORT).show(); + } + } + cursor.close(); + } + } + }; + + private void installApk(String apkPath) { + File file = new File(apkPath); + if (!file.exists()) { + Toast.makeText(this, "APK文件不存在", Toast.LENGTH_SHORT).show(); + return; + } + + Intent intent = new Intent(Intent.ACTION_VIEW); + Uri uri; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + uri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", file); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + } else { + uri = Uri.fromFile(file); + } + + intent.setDataAndType(uri, "application/vnd.android.package-archive"); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + try { + startActivity(intent); + } catch (Exception e) { + Toast.makeText(this, "无法安装应用: " + e.getMessage(), Toast.LENGTH_LONG).show(); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unregisterReceiver(onDownloadComplete); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == 1001) { + if (resultCode == RESULT_OK) { + Toast.makeText(this, "已获得安装未知应用权限", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(this, "未获得安装未知应用权限,无法自动安装", Toast.LENGTH_LONG).show(); + } + } + } +} diff --git a/app/src/main/java/com/example/tyre/TestActivity.java b/app/src/main/java/com/example/tyre/TestActivity.java index 5778ee5..8fd09a2 100644 --- a/app/src/main/java/com/example/tyre/TestActivity.java +++ b/app/src/main/java/com/example/tyre/TestActivity.java @@ -85,7 +85,7 @@ public class TestActivity extends AppCompatActivity { private KeyReceiver keyReceiver; private String deptId; String min = "EC0001012026010100000001"; // 左边界(包含) - String max = "EC00010120260101001000000"; // 右边界(包含) + String max = "EC0001012026010100100000"; // 右边界(包含) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/app/src/main/java/com/example/tyre/TyreLayoutActivity.java b/app/src/main/java/com/example/tyre/TyreLayoutActivity.java new file mode 100644 index 0000000..6c1877a --- /dev/null +++ b/app/src/main/java/com/example/tyre/TyreLayoutActivity.java @@ -0,0 +1,507 @@ +package com.example.tyre; + +import androidx.appcompat.app.AppCompatActivity; + +import android.app.ProgressDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import com.android.hdhe.uhf.reader.UhfReader; +import com.android.hdhe.uhf.readerInterface.TagModel; +import com.example.tyre.entity.AjaxResult; +import com.example.tyre.entity.BaseCar; +import com.example.tyre.entity.BaseTyre; +import com.example.tyre.entity.EPC; +import com.example.tyre.util.CarSelectionDialog; +import com.example.tyre.util.CommonDialog; +import com.example.tyre.util.MyUrl; +import com.example.tyre.util.SharedPreferencesUtils; +import com.example.tyre.util.Util; +import com.google.android.material.textfield.TextInputEditText; +import com.google.android.material.textfield.TextInputLayout; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; +import com.lzy.okgo.OkGo; +import com.lzy.okgo.callback.StringCallback; +import com.lzy.okgo.model.Response; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import cn.pda.serialport.Tools; + +public class TyreLayoutActivity extends AppCompatActivity { + + // 1. 使用 @BindView 绑定控件 + // 注意:变量名可以自定义,但 ID 必须对应 XML 中的 id + @BindView(R.id.et_plate_number) + TextInputEditText etPlateNumber; + + @BindView(R.id.btn_retrieve) + Button btnRetrieve; // 因为使用的是 MaterialButton,这里类型要对应 + + @BindView(R.id.til_plate_number) + TextInputLayout tilPlateNumber; + + private ProgressDialog progressDialog; + + private boolean isStart = true; + private boolean runFlag = true; + private boolean startFlag = false; + private UhfReader manager; // UHF manager,UHF Operating handle + private ArrayList listEPC; + private ArrayList listepc = new ArrayList(); + long lastTime; + long nextTime; + private Toast mToast; + private Toast toast; + private KeyReceiver keyReceiver; + String min = "EC0001012026010100000001"; // 左边界(包含) + String max = "EC0001012026010100100000"; // 右边界(包含) + // 1. 定义数据模型类 (对应后台返回的字段) + public static class TireData { + public String position; // 轮位名称 + public String brand; // 品牌 + public String spec; // 规格 + public String installDate; // 安装日期 + + public TireData(String position, String brand, String spec, String installDate) { + this.position = position; + this.brand = brand; + this.spec = spec; + this.installDate = installDate; + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_tyre_layout); + ButterKnife.bind(this); + manager = MyApplication.getManager(); + listEPC = new ArrayList(); + Thread thread = new InventoryThread(); + thread.start(); + Util.initSoundPool(this); + tilPlateNumber = findViewById(R.id.til_plate_number); + etPlateNumber.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + // 核心代码:只要用户输入了内容,就清除错误提示 + tilPlateNumber.setError(null); + + // 可选:如果你使用了 errorEnabled="true",可能还需要调用下面这行来彻底清除空间占用 + // tilPlateNumber.setErrorEnabled(false); + } + }); + tilPlateNumber.setEndIconOnClickListener(v -> { + // 在这里处理搜索逻辑 + String plateNumber = etPlateNumber.getText().toString().trim(); + if (!plateNumber.isEmpty()) { + // 执行搜索 + showLoadingDialog(); + performSearch(plateNumber); + } else { + tilPlateNumber.setError("车牌号不能为空"); + } + }); + } + @Override + public void onResume() { + super.onResume(); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + registerReceiver(); + } + + + @Override + public void onPause() { + startFlag = false; + super.onPause(); + unregisterReceiver(); + } + @Override + protected void onDestroy() { + startFlag = false; + runFlag = false; + super.onDestroy(); + } + private void performSearch(String carNo) { + OkGo.post(MyUrl.url + "/tyre/car/PdaQueryCarList") + .tag(this).params("carNo", carNo) + .execute(new StringCallback() { + @Override + public void onSuccess(Response response) { + hideLoadingDialog(); + String body = response.body(); + Gson gson=new Gson(); + List baseCarList = gson.fromJson(body, new TypeToken>(){}.getType()); + Log.e("EPC", "listepc:+ " + baseCarList.size()); + if (baseCarList!=null && baseCarList.size()>0){ + List carNoList = new ArrayList<>(); + for (BaseCar car : baseCarList) { + carNoList.add(car.getCarNo()); + } + // 显示自定义弹窗 + showCarSelectionDialog(carNoList); + } + } + }); + } + private void showCarSelectionDialog(List carNoList) { + CarSelectionDialog dialog = new CarSelectionDialog(this, carNoList); + dialog.setOnCarSelectedListener(new CarSelectionDialog.OnCarSelectedListener() { + @Override + public void onCarSelected(String carNo) { + // 处理选中的车辆 + etPlateNumber.setText(carNo); + } + }); + dialog.show(); + } + @OnClick({R.id.btn_retrieve}) + public void onViewClicked(View view) { + String PlateNumber = etPlateNumber.getText().toString(); + switch (view.getId()) { + case R.id.btn_retrieve: + if (PlateNumber == null || PlateNumber.isEmpty()) + { + new CommonDialog(TyreLayoutActivity.this).setMessage("请输入车牌号!").show(); + return; + } + carBangding(PlateNumber); + break; + } + } + //绑定车牌与RFID信息 + private void carBangding(String PlateNumber) { + OkGo.post(MyUrl.url + "/tyre/tyre/getCarBingTire") + .tag(this).params("carNo", PlateNumber) + .execute(new StringCallback() { + @Override + public void onSuccess(Response response) { + //hideLoadingDialog(); + String body = response.body(); + Gson gson=new Gson(); +// List baseTyreList = gson.fromJson(body, BaseTyre.class); + List baseTyreList = gson.fromJson(body, new com.google.gson.reflect.TypeToken>(){}.getType()); + bindDataToViews(baseTyreList); + } + }); + } + private void findCar(String carNumber) { + OkGo.post(MyUrl.url + "/tyre/car/queryCarByRfid").tag(this).params("rfid", carNumber).execute(new StringCallback() { + @Override + public void onSuccess(Response response) { + String body = response.body(); + hideLoadingDialog(); + try { + Gson gson = new Gson(); + BaseCar baseCar = gson.fromJson(body, BaseCar.class); + if (baseCar != null) { + // 空值处理:若字段为 null 则显示空字符串 + String carNo = safeGetString(baseCar.getCarNo()); + etPlateNumber.setText(carNo); + }else { + new CommonDialog(TyreLayoutActivity.this).setMessage("请检查车辆芯片绑定数据!").show(); + } + } catch (JsonSyntaxException e) { + return; + } + } + }); + } + private String safeGetString(String value) { + return value == null || "null".equals(value) ? "" : value; + } + /** + * 核心方法:将数据绑定到具体的 Include 视图上 + */ + private void bindDataToViews(List dataList) { + if (dataList == null) return; + + // 遍历列表,根据具体位置找到对应的 View + for (BaseTyre tyre : dataList) { + View targetView = null; + + // 根据后端返回的位置字段判断要更新哪个布局 + // 假设 tyre.getPosition() 返回 "LF" (左前), "RF" (右前) 等 + String pos = tyre.getWheelPostion(); + + if ("左前轮".equals(pos) || "1".equals(pos)) { + targetView = findViewById(R.id.tire_left_front); + } else if ("右前轮".equals(pos) || "2".equals(pos)) { + targetView = findViewById(R.id.tire_right_front); + } else if ("左内轮".equals(pos)) { + targetView = findViewById(R.id.tire_middle_left); + }else if ("右内轮".equals(pos)) { + targetView = findViewById(R.id.tire_middle_right); + }else if ("左外轮".equals(pos)) { + targetView = findViewById(R.id.tire_bottom_left); + }else if ("右外轮".equals(pos)) { + targetView = findViewById(R.id.tire_bottom_right); + } + // ... 其他位置 + + if (targetView != null) { + updateTireView(targetView, tyre); + } + } + } +// private void bindDataToViews(List dataList) { +// // 假设 dataList.get(0) 是左前轮,dataList.get(1) 是右前轮... +// // 请根据你实际的列表顺序调整索引 +// +// if (dataList.size() >= 2) { +// // 绑定左前轮 +// updateTireView(findViewById(R.id.tire_left_front), dataList.get(0)); +// +// // 绑定右前轮 +// updateTireView(findViewById(R.id.tire_right_front), dataList.get(1)); +// +// // 绑定其他轮胎... +// // updateTireView(findViewById(R.id.tire_left_mid), dataList.get(2)); +// } +// } + + /** + * 通用方法:更新单个轮胎卡片的 UI + * @param container include 标签对应的根布局 View + * @param data 该轮胎的数据对象 + */ + private void updateTireView(View container, BaseTyre data) { + if (container == null) return; + + // 【关键点】:必须在 container 内部查找控件,而不是在 Activity 中直接查找 + TextView tvBrand = container.findViewById(R.id.tv_brand); + TextView tvSpec = container.findViewById(R.id.tv_spec); + TextView tvSelfNo = container.findViewById(R.id.tv_self_no); // 或者是 tv_install_date + TextView tvDate = container.findViewById(R.id.tv_install_date); // 或者是 tv_install_date + // 设置数据 + if (tvBrand != null) tvBrand.setText("品牌: " + data.getTyreBrand()); + if (tvSpec != null) tvSpec.setText("规格: " + data.getTyreModel()); + if (tvSelfNo != null) tvSelfNo.setText("自编号: " + data.getSelfNo()); + if (tvDate != null) tvDate.setText("日期: " + data.getCreateTime()); + } + + private void addToList(final List list, final String epc, final byte rssi) { + runOnUiThread(new Runnable() { + @Override + public void run() { + // The epc for the first time + if (list.isEmpty()) { + EPC epcTag = new EPC(); + epcTag.setEpc(epc); + epcTag.setCount(1); + epcTag.setRssi(rssi); + list.add(epcTag); + listepc.add(epc); + + }else { + for (int i = 0; i < list.size(); i++) { + EPC mEPC = list.get(i); + // list contain this epc + if (epc.equals(mEPC.getEpc())) { + mEPC.setCount(mEPC.getCount() + 1); + mEPC.setRssi(rssi); + list.set(i, mEPC); + break; + } else if (i == (list.size() - 1)) { + // list doesn't contain this epc + EPC newEPC = new EPC(); + newEPC.setEpc(epc); + newEPC.setCount(1); + newEPC.setRssi(rssi); + list.add(newEPC); + listepc.add(epc); + } + } + } + + // play sound + lastTime = SystemClock.elapsedRealtime(); + long time = lastTime - nextTime; + if (time >= 60) { + Util.play(1, 0); + nextTime = lastTime; + Log.e("TAG", "run: " + time); + } + if (listepc != null && !listepc.isEmpty()){ + startFlag = false; + String currentEpc = listepc.get(0); + if (isInRange(currentEpc, min, max)) { + //请求后台 查询车辆信息 + // car.setText(currentEpc); + //请求后台 + debounceHandler.removeMessages(1); + lastScannedEpc = currentEpc; + debounceHandler.sendEmptyMessageDelayed(1, DEBOUNCE_DELAY); + } + + } + clearData(); + } + }); + Log.e("EPC", "listepc:+ " + listepc); + } + /** + * 判断字符串是否在指定的区间内(包含边界) + * @param target 要判断的目标字符串 + * @param min 区间左边界 + * @param max 区间右边界 + * @return true=在区间内,false=不在 + */ + public static boolean isInRange(String target, String min, String max) { + // 空值校验,避免空指针异常 + if (target == null || min == null || max == null) { + return false; + } + // compareTo规则: + // 1. 字符串相等返回0; + // 2. 目标字符串 > 对比字符串 返回正数; + // 3. 目标字符串 < 对比字符串 返回负数。 + // 因此:target >= min 等价于 target.compareTo(min) >= 0; + // target <= max 等价于 target.compareTo(max) <= 0。 + return target.compareTo(min) >= 0 && target.compareTo(max) <= 0; + } + // 防抖延迟时间 (毫秒) + private static final long DEBOUNCE_DELAY = 500; + // 用于存储最后一次扫描到的 EPC + private String lastScannedEpc; + // 用于处理防抖逻辑的 Handler + private Handler debounceHandler = new Handler(new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + if (msg.what == 1 && lastScannedEpc != null) { + // 延迟时间到,执行查询 + showLoadingDialog(); + + //查询车辆数据方法 + findCar(lastScannedEpc); + lastScannedEpc = null; // 清空,等待下一次扫描 + } + return true; + } + }); + private void clearData() { + listEPC.removeAll(listEPC); + listepc.removeAll(listepc); + } + + private class KeyReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + int keyCode = intent.getIntExtra("keyCode", 0); + if (keyCode == 0) { + keyCode = intent.getIntExtra("keycode", 0); + } + boolean keyDown = intent.getBooleanExtra("keydown", false); + if (keyDown) { + if (toast == null) { + // toast = Toast.makeText(OutStoreHouseActivity.this, "KeyReceiver:keyCode = down" + keyCode, Toast.LENGTH_SHORT); + } else { + // toast.setText("KeyReceiver:keyCode = down" + keyCode); + } + // toast.show(); + switch (keyCode) { + case KeyEvent.KEYCODE_F1: + case KeyEvent.KEYCODE_F2: + case KeyEvent.KEYCODE_F3: + case KeyEvent.KEYCODE_F4: + case KeyEvent.KEYCODE_F5: + //扫描 + startFlag = true; + break; + } + } + } + } + private void registerReceiver() { + keyReceiver = new KeyReceiver(); + IntentFilter filter = new IntentFilter(); + filter.addAction("android.rfid.FUN_KEY"); + filter.addAction("android.intent.action.FUN_KEY"); + registerReceiver(keyReceiver, filter); + } + private void unregisterReceiver() { + unregisterReceiver(keyReceiver); + } + + class InventoryThread extends Thread { + private List tagList; + byte[] accessPassword = Tools.HexString2Bytes("00000000"); + @Override + public void run() { + super.run(); + while (runFlag) { + if (startFlag) { + tagList = manager.inventoryRealTime(); //实时盘存 + if (tagList != null && !tagList.isEmpty()) { + //播放提示音 + Util.play(1, 0); + for (TagModel tag : tagList) { + if (tag == null) { + String epcStr = ""; +// String epcStr = new String(epc); + addToList(listEPC, epcStr, (byte) -1); + } else { + String epcStr = Tools.Bytes2HexString(tag.getmEpcBytes(), tag.getmEpcBytes().length); +// String epcStr = new String(epc); + byte rssi = tag.getmRssi(); + addToList(listEPC, epcStr, rssi); + } + + } + } + tagList = null; + try { + Thread.sleep(20); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + } + } + + private void showLoadingDialog() { + progressDialog = new ProgressDialog(this); + progressDialog.setMessage("数据加载中..."); + progressDialog.setCancelable(false); + progressDialog.show(); + } + private void hideLoadingDialog() { + if (progressDialog != null && progressDialog.isShowing()) { + progressDialog.dismiss(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/tyre/UpActivity.java b/app/src/main/java/com/example/tyre/UpActivity.java index 483e640..8007222 100644 --- a/app/src/main/java/com/example/tyre/UpActivity.java +++ b/app/src/main/java/com/example/tyre/UpActivity.java @@ -90,7 +90,8 @@ public class UpActivity extends AppCompatActivity implements AdapterView.OnItemS private Toast mToast; private Toast toast; private KeyReceiver keyReceiver; - + String min = "EC0001012026010100000001"; // 左边界(包含) + String max = "EC0001012026010100100000"; // 右边界(包含) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -217,17 +218,70 @@ public class UpActivity extends AppCompatActivity implements AdapterView.OnItemS nextTime = lastTime; Log.e("TAG", "run: " + time); } + if (listepc != null && !listepc.isEmpty()){ + startFlag = false; + String currentEpc = listepc.get(0); + if (isInRange(currentEpc, min, max)) { + //请求后台 查询车辆信息 + // car.setText(currentEpc); + //请求后台 + }else { + EPC.setText(currentEpc); + //请求后台 + debounceHandler.removeMessages(1); + } + debounceHandler.removeMessages(1); + lastScannedEpc = currentEpc; + debounceHandler.sendEmptyMessageDelayed(1, DEBOUNCE_DELAY); + } + + clearData(); } }); Log.e("EPC", "listepc:+ " + listepc); - if (listepc != null && !listepc.isEmpty()){ - startFlag = false; - EPC.setText(listepc.get(0).toString()); - //请求后台 - find(listepc.get(0).toString()); - } - clearData(); } + /** + * 判断字符串是否在指定的区间内(包含边界) + * @param target 要判断的目标字符串 + * @param min 区间左边界 + * @param max 区间右边界 + * @return true=在区间内,false=不在 + */ + public static boolean isInRange(String target, String min, String max) { + // 空值校验,避免空指针异常 + if (target == null || min == null || max == null) { + return false; + } + // compareTo规则: + // 1. 字符串相等返回0; + // 2. 目标字符串 > 对比字符串 返回正数; + // 3. 目标字符串 < 对比字符串 返回负数。 + // 因此:target >= min 等价于 target.compareTo(min) >= 0; + // target <= max 等价于 target.compareTo(max) <= 0。 + return target.compareTo(min) >= 0 && target.compareTo(max) <= 0; + } + // 防抖延迟时间 (毫秒) + private static final long DEBOUNCE_DELAY = 500; + // 用于存储最后一次扫描到的 EPC + private String lastScannedEpc; + // 用于处理防抖逻辑的 Handler + private Handler debounceHandler = new Handler(new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + if (msg.what == 1 && lastScannedEpc != null) { + // 延迟时间到,执行查询 + showLoadingDialog(); + if (isInRange(lastScannedEpc, min, max)){ + //查询车辆数据方法 + findCar(lastScannedEpc); + }else { + find(lastScannedEpc); + } + lastScannedEpc = null; // 清空,等待下一次扫描 + } + return true; + } + }); private void clearData() { listEPC.removeAll(listEPC); listepc.removeAll(listepc); @@ -356,11 +410,35 @@ public class UpActivity extends AppCompatActivity implements AdapterView.OnItemS } } // 基本信息查询 + private void findCar(String carNumber) { + OkGo.post(MyUrl.url + "/tyre/car/queryCarByRfid").tag(this).params("rfid", carNumber).execute(new StringCallback() { + @Override + public void onSuccess(Response response) { + String body = response.body(); + hideLoadingDialog(); + try { + Gson gson = new Gson(); + BaseCar baseCar = gson.fromJson(body, BaseCar.class); + if (baseCar != null) { + // 空值处理:若字段为 null 则显示空字符串 + String carNo = safeGetString(baseCar.getCarNo()); + car.setText(carNo); + }else { + new CommonDialog(UpActivity.this).setMessage("请检查车辆芯片绑定数据!").show(); + } + } catch (JsonSyntaxException e) { + return; + } + } + }); + } + // 基本信息查询 private void find(String epc) { OkGo.post(MyUrl.url + "/tyre/tyre/pdaQueryTyreInfo").tag(this).params("tyreEpc", epc).execute(new StringCallback() { @Override public void onSuccess(Response response) { String body = response.body(); + hideLoadingDialog(); try { Gson gson = new Gson(); BaseTyre baseTyre = gson.fromJson(body, BaseTyre.class); diff --git a/app/src/main/java/com/example/tyre/entity/BaseTyre.java b/app/src/main/java/com/example/tyre/entity/BaseTyre.java index 7bf2582..fa00b22 100644 --- a/app/src/main/java/com/example/tyre/entity/BaseTyre.java +++ b/app/src/main/java/com/example/tyre/entity/BaseTyre.java @@ -1,6 +1,7 @@ package com.example.tyre.entity; +import java.util.Date; /** * 轮胎基础信息对象 base_tyre @@ -47,6 +48,16 @@ public class BaseTyre private String wheelPostion; + private String createTime; + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + public String getSelfNo() { return selfNo; } diff --git a/app/src/main/java/com/example/tyre/entity/CheckInfoSpinnerVo.java b/app/src/main/java/com/example/tyre/entity/CheckInfoSpinnerVo.java new file mode 100644 index 0000000..e088b1a --- /dev/null +++ b/app/src/main/java/com/example/tyre/entity/CheckInfoSpinnerVo.java @@ -0,0 +1,24 @@ +package com.example.tyre.entity; + +import java.util.List; + +public class CheckInfoSpinnerVo { + private List checkResultList; + private List checkTypeList; + + public List getCheckResultList() { + return checkResultList; + } + + public void setCheckResultList(List checkResultList) { + this.checkResultList = checkResultList; + } + + public List getCheckTypeList() { + return checkTypeList; + } + + public void setCheckTypeList(List checkTypeList) { + this.checkTypeList = checkTypeList; + } +} diff --git a/app/src/main/java/com/example/tyre/entity/InStoreSpinnerVo.java b/app/src/main/java/com/example/tyre/entity/InStoreSpinnerVo.java index 50c9ec9..675c91c 100644 --- a/app/src/main/java/com/example/tyre/entity/InStoreSpinnerVo.java +++ b/app/src/main/java/com/example/tyre/entity/InStoreSpinnerVo.java @@ -8,6 +8,15 @@ public class InStoreSpinnerVo { private List levelList; private List patternList; private List kindList; + private List gctsList; + + public List getGctsList() { + return gctsList; + } + + public void setGctsList(List gctsList) { + this.gctsList = gctsList; + } public List getTyreBrandList() { return tyreBrandList; diff --git a/app/src/main/java/com/example/tyre/util/MyUrl.java b/app/src/main/java/com/example/tyre/util/MyUrl.java index b668167..23cea64 100644 --- a/app/src/main/java/com/example/tyre/util/MyUrl.java +++ b/app/src/main/java/com/example/tyre/util/MyUrl.java @@ -6,7 +6,7 @@ package com.example.tyre.util; * @date: 2019-10-24 13:59 */ public class MyUrl { - public static String url="http://10.11.187.77:8020"; -// public static String url="http://www.qdhys.xyz:8020"; -// public static String url="http://192.168.0.102:8020"; +// public static String url="http://192.168.31.26:8020"; + public static String url="http://www.qdhys.xyz:8020"; +// public static String url="http://10.11.187.77:8020"; } diff --git a/app/src/main/res/drawable/bg_tire_circl.xml b/app/src/main/res/drawable/bg_tire_circl.xml new file mode 100644 index 0000000..4390073 --- /dev/null +++ b/app/src/main/res/drawable/bg_tire_circl.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/tire_background.xml b/app/src/main/res/drawable/tire_background.xml new file mode 100644 index 0000000..a6b40fb --- /dev/null +++ b/app/src/main/res/drawable/tire_background.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/tire_image.png b/app/src/main/res/drawable/tire_image.png new file mode 100644 index 0000000000000000000000000000000000000000..31dcf1517faf8c147fe86b06c22f2394c6f9ace8 GIT binary patch literal 28483 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/tire_side_view.png b/app/src/main/res/drawable/tire_side_view.png new file mode 100644 index 0000000000000000000000000000000000000000..e0f23a4f34b0ac6824fe3f7a48cc83409fe228af GIT binary patch literal 15491 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 Oz$wdX$kobNg#JI3h|s - - + android:layout_height="match_parent" + android:background="#F5F5F5"> + - + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="16dp"> - + - - - - - - - - - - - - - - - - - - - - - - android:textColor="@color/black" - android:textSize="22sp" - android:inputType="textMultiLine" - android:minLines="3" - android:lines="5" - android:gravity="top|start" - android:padding="8dp" - android:scrollbars="vertical" - android:hint="请输入内容" - /> + + + + + + + + + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +