Commit f7effb20 by pye52

Merge branch 'new_master' into new_for_phone

parents 60e01ee8 0b3cabc6
......@@ -7,8 +7,8 @@ android {
applicationId "com.bgycc.smartcanteen"
minSdkVersion 22
targetSdkVersion 22
versionCode 12
versionName "1.3.1"
versionCode 13
versionName "1.3.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
abiFilters "armeabi", "armeabi-v7a", "x86", "mips"
......
......@@ -33,11 +33,9 @@ import com.bgycc.smartcanteen.state.ConnectState;
import com.bgycc.smartcanteen.state.PayOfflineState;
import com.bgycc.smartcanteen.state.PayOnlineState;
import com.bgycc.smartcanteen.state.QRCodeState;
import com.bgycc.smartcanteen.utils.EthernetHelper;
import com.bgycc.smartcanteen.utils.NetworkHelper;
import com.bgycc.smartcanteen.utils.NetworkUtils;
import com.bgycc.smartcanteen.utils.SmartCanteenUtils;
import com.bgycc.smartcanteen.utils.TTSHelper;
import com.bgycc.smartcanteen.utils.WifiHelper;
import com.bgycc.smartcanteen.viewModel.CommandViewModel;
import com.bgycc.smartcanteen.viewModel.PayOfflineViewModel;
import com.bgycc.smartcanteen.viewModel.PayOnlineViewModel;
......@@ -96,8 +94,8 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
super.attachBaseContext(newBase);
deviceSN = SmartCanteenUtils.getDeviceSN(newBase.getApplicationContext());
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
NetworkHelper.initialize(newBase.getApplicationContext());
TTSHelper.initialize(newBase.getApplicationContext());
NetworkUtils.initialize(newBase.getApplicationContext());
if (deviceSN.isEmpty()) {
LogUtils.file(TAG, "设备SN号为空");
......@@ -132,7 +130,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
SCWebSocketClient.getInstance().getConnectStateEvent().observe(this, event -> {
if (debugVs != null) return;
String networkType = NetworkHelper.getNetworkType(this);
String networkType = NetworkUtils.getNetworkType(this);
String msg;
switch (event.getState()) {
case ConnectState.OFFLINE:
......@@ -246,12 +244,16 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
break;
case CommandState.WAIT:
settingLayout.animate().setDuration(300).alpha(1f);
settingText.setText(event.getMessage());
break;
case CommandState.SUCCESS:
case CommandState.FAILED:
settingText.setText(event.getMessage());
settingLayout.animate().setDuration(300).alpha(1f);
break;
case CommandState.TOGGLE_DEBUG:
toggleDebugLayout();
break;
}
});
......@@ -265,6 +267,20 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
SCWebSocketClient.getInstance().tryConnect();
}
private void toggleDebugLayout() {
if (debugVs != null) {
inflateDebugLayout();
} else {
if (debugLayout.getVisibility() == View.GONE) {
debugLayout.setVisibility(View.VISIBLE);
logoLayout.setVisibility(View.GONE);
} else {
debugLayout.setVisibility(View.GONE);
logoLayout.setVisibility(View.VISIBLE);
}
}
}
private void paidSuccess(String successMessage) {
message.setTextColor(getResources().getColor(R.color.pay_success));
if (TextUtils.isEmpty(successMessage)) {
......@@ -318,7 +334,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
super.onDestroy();
captureHelper.onDestroy();
TTSHelper.release();
SCWebSocketClient.getInstance().close();
SCWebSocketClient.getInstance().realClose();
SCTaskExecutor.getInstance().quit();
}
......@@ -418,15 +434,15 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
}
private void refreshEthInfo() {
String ethMac = EthernetHelper.getMacAddress();
String ethIP = EthernetHelper.getIpString();
String ethMac = NetworkUtils.getEthernetMacAddress();
String ethIP = NetworkUtils.getEthernetIpString();
ethText.setText(String.format(Locale.getDefault(), getString(R.string.eth_text), ethMac, ethIP));
}
private void refreshWifiInfo() {
String wifiMac = WifiHelper.getMacAddress();
String wifiSSID = WifiHelper.getSSID();
String wifiIP = WifiHelper.getIpString();
String wifiMac = NetworkUtils.getWifiMacAddress();
String wifiSSID = NetworkUtils.getSSID();
String wifiIP = NetworkUtils.getWifiIpString();
wifiText.setText(String.format(Locale.getDefault(), getString(R.string.wifi_text), wifiMac, wifiSSID, wifiIP));
}
......
package com.bgycc.smartcanteen.command;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import com.bgycc.smartcanteen.entity.Command;
import com.bgycc.smartcanteen.entity.CommandResponse;
import com.bgycc.smartcanteen.entity.CommandWifiConfig;
import com.bgycc.smartcanteen.utils.WifiHelper;
import com.blankj.utilcode.util.LogUtils;
import com.bgycc.smartcanteen.utils.NetworkUtils;
import com.google.gson.Gson;
import static com.bgycc.smartcanteen.utils.SmartCanteenUtils.TAG;
public class WifiConfigCommandHandler extends CommandHandler {
private static final long DEFAULT_DELAY = 3000;
private static final long CHECK_DELAY = 8000;
private static final long POLLING_DELAY = 100;
private CommandWifiConfig wifiConfig;
public WifiConfigCommandHandler(Command command, Gson gson, CommandProgressCallback callback) {
......@@ -27,15 +23,9 @@ public class WifiConfigCommandHandler extends CommandHandler {
CommandWifiConfig.CommandWifiConfigData data = wifiConfig.getData();
if (data == null) return null;
String ssid = data.getSsid();
String identity = data.getIdentity();
String pwd = data.getPwd();
String type = data.getType();
WifiHelper.connect(ssid, identity, pwd, type);
if (!WifiHelper.isWifiEnabled()) {
if (!NetworkUtils.isWifiEnabled()) {
progress("正在启动Wifi", 5);
if (!WifiHelper.setEnable(true)) {
if (!NetworkUtils.setEnable(true)) {
String failedMessage = "无法启动Wifi";
progress(failedMessage, 5);
Thread.sleep(DEFAULT_DELAY);
......@@ -43,41 +33,30 @@ public class WifiConfigCommandHandler extends CommandHandler {
}
}
progress("正在配置Wifi", 10);
WifiConfiguration config = WifiHelper.createWifiConfiguration(ssid, identity, pwd, type);
if (config == null) {
String failedMessage = "Wifi参数有误";
progress(failedMessage, 10);
Thread.sleep(DEFAULT_DELAY);
return failed(failedMessage);
}
String ssid = data.getSsid();
String identity = data.getIdentity();
String pwd = data.getPwd();
String type = data.getType();
progress("正在配置Wifi", 10);
try {
if (!WifiHelper.removeWifiConfiguration(ssid)) {
return failed("无法修改Wifi配置");
}
int netId = WifiHelper.addNetwork(config);
if (!WifiHelper.enableNetwork(netId)) {
return failed("无法应用Wifi配置");
NetworkUtils.connect(ssid, identity, pwd, type);
// 轮训检查wifi是否链接成功
for (int i = 0; i < 30; i++) {
Thread.sleep(POLLING_DELAY);
WifiInfo info = NetworkUtils.getWifiInfo();
if (info == null || info.getIpAddress() == 0) {
continue;
}
String message = "Wifi配置成功";
progress(message, 50);
Thread.sleep(DEFAULT_DELAY);
return success(message);
}
} catch (Exception e) {
Thread.sleep(DEFAULT_DELAY);
return failed(e.getMessage());
}
progress("正在连接Wifi", 20);
// 等待一段时间后检查是否连接成功
Thread.sleep(CHECK_DELAY);
WifiInfo info = WifiHelper.getWifiInfo();
if (info != null) {
LogUtils.d(TAG, "当前网络: " + info.getSSID() + " 目标网络: " + ssid);
if (info.getIpAddress() != 0) {
String successMessage = "Wifi已成功切换";
progress(successMessage, 50);
Thread.sleep(DEFAULT_DELAY);
return success(successMessage);
}
}
return failed("无法连接Wifi");
}
}
package com.bgycc.smartcanteen.entity;
import androidx.annotation.StringDef;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
......@@ -11,6 +12,11 @@ public class Command {
public static final String LOG_UPLOAD = "LOG_PULL";
public static final String APP_UPDATE = "CONFIG_UPDATE";
public static final String CONFIG_WIFI = "CONFIG_WIFI";
public static final String CONFIG_LOG = "CONFIG_LOG";
@StringDef(value = {LOG_UPLOAD, APP_UPDATE, CONFIG_WIFI, CONFIG_LOG})
public @interface COMMAND_ACTION {
}
@PrimaryKey(autoGenerate = true)
private long id;
......@@ -22,7 +28,7 @@ public class Command {
}
public Command(String command, String action) {
public Command(String command, @COMMAND_ACTION String action) {
this.data = command;
this.action = action;
this.finish = false;
......
......@@ -4,15 +4,15 @@ import android.os.Build;
public class QRCodeScanFactory {
private static final String TPS = "TPS";
private static final String T3 = "56iqDS";
private static final String QUAD = "QUAD";
private static final String TPS_PORT = "/dev/ttyS0";
private static final String T3_PORT = "/dev/ttyS6";
private static final String QUAD_PORT = "/dev/ttyS6";
public static IQRCodeScan create() throws Exception {
if (Build.DEVICE.contains(TPS)) {
if (Build.MODEL.contains(TPS)) {
return new TPS(TPS_PORT);
} else if (Build.DEVICE.contains(T3)) {
return new T3(T3_PORT);
} else if (Build.DEVICE.contains(QUAD)) {
return new QUAD(QUAD_PORT);
} else {
throw new RuntimeException("不明设备型号: " + Build.DEVICE);
}
......
......@@ -2,10 +2,10 @@ package com.bgycc.smartcanteen.qrcode;
import android_serialport_api.SerialPort;
public class T3 extends BaseQRCodeScan {
public class QUAD extends BaseQRCodeScan {
private SerialPort serial;
public T3(String filePath) throws Exception {
public QUAD(String filePath) throws Exception {
serial = new SerialPort(filePath, 115200, 0);
setup(serial.getInputStream());
}
......
package com.bgycc.smartcanteen.socket;
import android.annotation.SuppressLint;
import android.net.ConnectivityManager;
import android.net.Network;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
......@@ -9,8 +13,7 @@ import com.bgycc.smartcanteen.entity.CheckDevice;
import com.bgycc.smartcanteen.entity.Heartbeat;
import com.bgycc.smartcanteen.executor.SCTaskExecutor;
import com.bgycc.smartcanteen.state.ConnectState;
import com.bgycc.smartcanteen.utils.NetworkHelper;
import com.bgycc.smartcanteen.utils.WifiHelper;
import com.bgycc.smartcanteen.utils.NetworkUtils;
import com.blankj.utilcode.util.LogUtils;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
......@@ -36,7 +39,9 @@ import static com.bgycc.smartcanteen.utils.SmartCanteenUtils.TAG;
*/
public class SCWebSocketClient extends WebSocketClient {
private static final long HEARTBEAT_INTERVAL = 3 * 60 * 1000;
private static final long RECONNECT_DELAY = 5000;
private static final long RECONNECT_DELAY = 5 * 1000;
private static final long SWITCH_INTERVAL = 15 * 1000;
private static final String RESPONSE_ACTION = "action";
private static final String RESPONSE_DATA = "data";
private static final String CHECK_DEVICE = "CHECK_DEVICE";
......@@ -47,6 +52,8 @@ public class SCWebSocketClient extends WebSocketClient {
private ScheduledFuture<?> heartbeatFuture;
private ScheduledFuture<?> reconnectFuture;
private boolean forceStop = false;
// 记录上一次网络切换的时间,避免网络切换过于频繁
private long lastSwitchTime = -1;
private List<SCWebSocketListener> listener = new ArrayList<>();
private MutableLiveData<ConnectState> connectState = new MutableLiveData<>();
......@@ -64,6 +71,8 @@ public class SCWebSocketClient extends WebSocketClient {
}
public void tryConnect() {
LogUtils.d(TAG, "尝试通过WebSocket链接服务器");
forceStop = false;
SCTaskExecutor.getInstance().executeOnDiskIO(() -> {
try {
boolean connectResult = connectBlocking(10, TimeUnit.SECONDS);
......@@ -113,17 +122,16 @@ public class SCWebSocketClient extends WebSocketClient {
LogUtils.d(TAG, "发送数据包: " + Arrays.toString(data));
}
@Override
public void close() {
stopHeartbeat();
public void realClose() {
listener.clear();
if (reconnectFuture != null) {
reconnectFuture.cancel(true);
reconnectFuture = null;
}
listener.clear();
forceStop = true;
LogUtils.d(TAG, "SCWebSocket close");
super.close();
stopHeartbeat();
}
@Override
......@@ -185,30 +193,34 @@ public class SCWebSocketClient extends WebSocketClient {
if (forceStop) return;
switchNetwork();
// socket链接断开后就无需继续定时发送心跳了
stopHeartbeat();
tryReconnect();
}
@Override
public void onError(Exception e) {
LogUtils.i(TAG, "链接异常: " + e.getMessage(), e);
LogUtils.e(TAG, "链接异常: " + e.getMessage(), e);
connectState.postValue(new ConnectState(ConnectState.OFFLINE));
switchNetwork();
for (SCWebSocketListener l : listener) {
l.onError(e);
}
if (forceStop) return;
switchNetwork();
stopHeartbeat();
tryReconnect();
}
private void switchNetwork() {
// 保证Wifi状态启动
if (!WifiHelper.isWifiEnabled()) {
WifiHelper.setEnable(true);
@SuppressLint("ObsoleteSdkInt")
private synchronized void switchNetwork() {
long currentTime = System.currentTimeMillis();
if ((currentTime - lastSwitchTime) < SWITCH_INTERVAL) {
return;
}
NetworkHelper.switchNetwork();
lastSwitchTime = currentTime;
connectState.postValue(new ConnectState(ConnectState.CHANGE_NETWORK));
NetworkUtils.switchNetwork(callback);
}
private void tryReconnect() {
......@@ -228,26 +240,20 @@ public class SCWebSocketClient extends WebSocketClient {
}
};
private Runnable reconnectRunnable = new Runnable() {
private Runnable reconnectRunnable = () -> {
LogUtils.d(TAG, "开始尝试重连");
connectState.postValue(new ConnectState(ConnectState.RECONNECTING));
for (SCWebSocketListener l : listener) {
l.onReconnect();
}
reconnect();
};
private ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback() {
@Override
public void run() {
LogUtils.d(TAG, "开始尝试重连");
connectState.postValue(new ConnectState(ConnectState.RECONNECTING));
for (SCWebSocketListener l : listener) {
l.onReconnect();
}
try {
boolean result = reconnectBlocking();
if (result) {
LogUtils.d(TAG, "重连成功");
} else {
reconnectFuture = SCTaskExecutor.getInstance().schedule(reconnectRunnable, RECONNECT_DELAY, TimeUnit.MILLISECONDS);
LogUtils.d(TAG, "重连失败,一段时间后再次尝试");
}
} catch (InterruptedException e) {
reconnectFuture = SCTaskExecutor.getInstance().schedule(reconnectRunnable, RECONNECT_DELAY, TimeUnit.MILLISECONDS);
LogUtils.w(TAG, "重连过程异常,一段时间后再次尝试: " + e.getMessage(), e);
}
public void onAvailable(@NonNull Network network) {
NetworkUtils.unregisterNetworkCallback(this);
NetworkUtils.bindProcessToNetwork(network);
}
};
}
......@@ -9,8 +9,9 @@ public class CommandState {
public static final int WAIT = 1;
public static final int SUCCESS = 2;
public static final int FAILED = 3;
public static final int TOGGLE_DEBUG = 4;
@IntDef(value = {IDLE, WAIT, SUCCESS, FAILED})
@IntDef(value = {IDLE, WAIT, SUCCESS, FAILED, TOGGLE_DEBUG})
public @interface COMMAND_STATE {
}
......
package com.bgycc.smartcanteen.utils;
import com.blankj.utilcode.util.LogUtils;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
import static com.bgycc.smartcanteen.utils.SmartCanteenUtils.TAG;
public class EthernetHelper {
public static String getMacAddress() {
try {
NetworkInterface networkInterface = NetworkInterface.getByName("eth0");
if (networkInterface == null) {
return "";
}
byte[] data = networkInterface.getHardwareAddress();
return String.format("%02x:%02x:%02x:%02x:%02x:%02x", data[0], data[1], data[2], data[3], data[4], data[5]);
} catch (Exception e) {
LogUtils.e(TAG, "获取设备MAC地址失败: " + e.getMessage(), e);
}
return "02:00:00:00:00:00";
}
public static String getIpString() {
try {
NetworkInterface networkInterface = NetworkInterface.getByName("eth0");
if (networkInterface == null) {
return "";
}
for (Enumeration<InetAddress> enumIpAddr = networkInterface.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress()) {
String address = inetAddress.getHostAddress();
if(!address.contains("::")) return address;
}
}
} catch (Exception e) {
LogUtils.e(TAG, "获取以太网ip地址失败: " + e.getMessage(), e);
}
return "0.0.0.0";
}
}
package com.bgycc.smartcanteen.utils;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Build;
import androidx.annotation.NonNull;
import com.bgycc.smartcanteen.R;
import com.blankj.utilcode.util.LogUtils;
import static com.bgycc.smartcanteen.utils.SmartCanteenUtils.TAG;
public class NetworkHelper {
private static ConnectivityManager connectivityManager;
private static int transportType = NetworkCapabilities.TRANSPORT_WIFI;
public static void initialize(Context context) {
connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
public static String getNetworkType(Context context) {
switch (transportType) {
case NetworkCapabilities.TRANSPORT_ETHERNET:
return context.getString(R.string.network_type_ethernet);
case NetworkCapabilities.TRANSPORT_WIFI:
return context.getString(R.string.network_type_wifi);
default:
return context.getString(R.string.network_type_unknown);
}
}
public static synchronized void switchNetwork() {
if (transportType == NetworkCapabilities.TRANSPORT_ETHERNET) {
requestWifi();
} else {
requestEthernet();
}
}
private static void requestEthernet() {
if (connectivityManager == null) {
LogUtils.w(TAG, "ConnectivityManager should be initialize before use");
return;
}
int targetTransportType = NetworkCapabilities.TRANSPORT_ETHERNET;
if (!hasTransport(targetTransportType)) {
LogUtils.d(TAG, "当前设备不支持通过以太网链接网络");
return;
}
transportType = targetTransportType;
LogUtils.d(TAG, "网络切换至以太网");
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(transportType)
.build();
connectivityManager.requestNetwork(request, createCallback());
}
private static void requestWifi() {
if (connectivityManager == null) {
LogUtils.w(TAG, "ConnectivityManager should be initialize before use");
return;
}
int targetTransportType = NetworkCapabilities.TRANSPORT_WIFI;
if (!hasTransport(targetTransportType)) {
LogUtils.d(TAG, "当前设备不支持通过Wifi链接网络");
return;
}
transportType = targetTransportType;
LogUtils.d(TAG, "网络切换至Wifi");
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(transportType)
.build();
connectivityManager.requestNetwork(request, createCallback());
}
private static boolean hasTransport(int transportType) {
Network[] networks = connectivityManager.getAllNetworks();
NetworkCapabilities capabilities;
for (Network n : networks) {
capabilities = connectivityManager.getNetworkCapabilities(n);
if (capabilities == null) continue;
if (capabilities.hasTransport(transportType)) {
return true;
}
}
return false;
}
private static ConnectivityManager.NetworkCallback createCallback() {
return new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(@NonNull Network network) {
super.onAvailable(network);
LogUtils.i(TAG, "网络切换成功: " + network);
if (Build.VERSION.SDK_INT >= 23) {
connectivityManager.bindProcessToNetwork(network);
} else {
ConnectivityManager.setProcessDefaultNetwork(network);
}
connectivityManager.unregisterNetworkCallback(this);
}
@Override
public void onUnavailable() {
super.onUnavailable();
LogUtils.i(TAG, "网络切换失败: " + transportType);
}
};
}
}
......@@ -121,6 +121,11 @@ public class CommandViewModel extends ViewModel implements CommandProgressCallba
case Command.CONFIG_WIFI:
handler = new WifiConfigCommandHandler(command, gson, CommandViewModel.this);
break;
case Command.CONFIG_LOG:
// CONFIG_LOG后直接return
commandState.postValue(new CommandState(CommandState.TOGGLE_DEBUG));
commandFinishAndUpdateDB();
return;
}
} catch (Exception e) {
commandState.postValue(new CommandState(CommandState.FAILED, e.getMessage()));
......
......@@ -103,6 +103,7 @@ public class PayOfflineViewModel extends ViewModel {
// 1、没有action
// 2、message为"操作完成"
LogUtils.d(TAG, "离线支付结果响应: " + original);
cancelTimeout();
ResponseRunnable runnable = new ResponseRunnable(original);
SCTaskExecutor.getInstance().executeOnDiskIO(runnable);
}
......
......@@ -108,6 +108,8 @@ public class QRCodeViewModel extends ViewModel {
String action = jsonObject.get(RESPONSE_ACTION).getAsString();
Command command = new Command(scanData, action);
commandRepository.insertCommand(command);
// 任务写入数据库成功后,主动发送一次IDLE,以重置界面提示语
qrCodeState.postValue(new QRCodeState(QRCodeState.IDLE));
againDelay();
} else {
qrCodeState.postValue(new QRCodeState(QRCodeState.FAILED));
......
......@@ -58,7 +58,6 @@
android:layout_height="50dp" />
<TextView
android:id="@+id/_setting_msg"
android:text="@string/setting_wifi"
android:textColor="#666"
android:textSize="22sp"
android:layout_width="wrap_content"
......
......@@ -12,7 +12,6 @@
<string name="network_type_ethernet">以太网</string>
<string name="network_type_wifi">Wifi</string>
<string name="network_type_unknown">未知</string>
<string name="setting_wifi">正在设置wifi</string>
<string name="default_message">请出示付款码</string>
<string name="qrcode_failed">请出示付款码</string>
......
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 sign in to comment