package com.bgycc.smartcanteen; import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.ViewModelProvider; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.media.AudioManager; import android.os.Bundle; import android.os.Handler; import android.text.TextUtils; import android.view.KeyEvent; import android.view.View; import android.view.ViewStub; import android.view.Window; import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.RotateAnimation; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.bgycc.smartcanteen.entity.PayData; import com.bgycc.smartcanteen.executor.SCTaskExecutor; import com.bgycc.smartcanteen.socket.SCWebSocketClient; import com.bgycc.smartcanteen.state.CommandState; 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.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; import com.bgycc.smartcanteen.viewModel.QRCodeViewModel; import com.bgycc.smartcanteen.viewModel.ViewModelFactory; import com.blankj.utilcode.util.LogUtils; import java.net.URI; import java.net.URISyntaxException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import static com.bgycc.smartcanteen.utils.SmartCanteenUtils.TAG; @SuppressWarnings("all") public class MainActivity extends AppCompatActivity implements View.OnClickListener { private PayOnlineViewModel payOnlineViewModel; private PayOfflineViewModel payOfflineViewModel; private QRCodeViewModel qrCodeViewModel; private CommandViewModel commandViewModel; private String deviceSN; private ViewStub debugVs; private LinearLayout debugLayout; private LinearLayout settingLayout; private ImageView logoLayout; private ImageView settingImg; private TextView versionText; private TextView snText; private TextView serverText; private TextView qrCodeText; private TextView sendText; private TextView recText; private TextView ethText; private TextView wifiText; private TextView settingText; private TextView socketConnectedTime; private TextView message; private AudioManager audioManager; private Handler handler = new Handler(); private SimpleDateFormat payDateFormat = new SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault()); private SimpleDateFormat socketConnectedTimeDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); @Override protected void attachBaseContext(Context newBase) { super.attachBaseContext(newBase); deviceSN = SmartCanteenUtils.getDeviceSN(newBase.getApplicationContext()); audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); NetworkHelper.initialize(newBase.getApplicationContext()); TTSHelper.initialize(newBase.getApplicationContext()); if (deviceSN.isEmpty()) { LogUtils.file(TAG, "设备SN号为空"); } String host = SmartCanteenUtils.getMainWebSocketServerUrl(deviceSN, BuildConfig.VERSION_NAME); LogUtils.d(TAG, "服务器地址: " + host); try { URI uri = new URI(host); SCWebSocketClient.init(uri, deviceSN, Injection.provideGson()); } catch (URISyntaxException e) { LogUtils.e(TAG, "服务器地址异常: " + e.getMessage(), e); } } @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); ViewModelFactory factory = Injection.injectFactory(deviceSN); ViewModelProvider provider = new ViewModelProvider(this, factory); payOnlineViewModel = provider.get(PayOnlineViewModel.class); payOfflineViewModel = provider.get(PayOfflineViewModel.class); qrCodeViewModel = provider.get(QRCodeViewModel.class); commandViewModel = provider.get(CommandViewModel.class); initViews(); SCWebSocketClient.getInstance().getConnectStateEvent().observe(this, event -> { if (debugVs != null) return; String networkType = NetworkHelper.getNetworkType(this); String msg; switch (event.getState()) { case ConnectState.OFFLINE: msg = String.format(getString(R.string.connect_offline), networkType); break; case ConnectState.CONNECTING: msg = String.format(getString(R.string.connect_connecting), networkType); break; case ConnectState.CONNECTED: msg = String.format(getString(R.string.connect_connected), networkType); // socket链接成功后刷新wifi及eth的ip地址信息 refreshEthInfo(); refreshWifiInfo(); break; case ConnectState.RECONNECTING: msg = String.format(getString(R.string.connect_reconnecting), networkType); break; case ConnectState.CHANGE_NETWORK: msg = String.format(getString(R.string.connect_change_network), networkType); break; default: msg = ""; break; } serverText.setText(msg); }); payOnlineViewModel.getPayOnlineStateEvent().observe(this, event -> { switch (event.getState()) { case PayOnlineState.SEND: message.setTextColor(getResources().getColor(R.color.pay_idle)); message.setText(R.string.pay_wait); String time = payDateFormat.format(new Date()); String log = "支付进度: " + time + " - " + event.getMessage(); LogUtils.file(TAG, log); if (debugVs != null) return; sendText.setText(log); break; case PayOnlineState.SUCCESS: message.setTextColor(getResources().getColor(R.color.pay_success)); String successText = event.getMessage(); if (TextUtils.isEmpty(successText)) { message.setText(R.string.pay_success); TTSHelper.speak(getString(R.string.pay_success)); } else { message.setText(successText); TTSHelper.speak(successText); } qrCodeViewModel.scan(); String successTime = payDateFormat.format(new Date()); String successLog = "支付成功: " + successTime + " - " + event.getMessage(); LogUtils.file(TAG, successLog); if (debugVs != null) return; recText.setText(successLog); break; case PayOnlineState.FAILED: message.setTextColor(getResources().getColor(R.color.pay_failed)); String failedText = event.getMessage(); if (TextUtils.isEmpty(failedText)) { message.setText(R.string.pay_failed); TTSHelper.speak(getString(R.string.pay_failed)); } else { message.setText(failedText); TTSHelper.speak(failedText); } qrCodeViewModel.scan(); String failedTime = payDateFormat.format(new Date()); String failedLog = "支付失败: " + failedTime + " - " + event.getMessage(); LogUtils.file(TAG, failedLog); if (debugVs != null) return; recText.setText(failedLog); break; } }); payOfflineViewModel.getPayOfflineStateEvent().observe(this, event -> { switch (event.getState()) { case PayOfflineState.MARK: message.setTextColor(getResources().getColor(R.color.pay_success)); String successText = event.getMessage(); if (TextUtils.isEmpty(successText)) { message.setText(R.string.pay_success); TTSHelper.speak(getString(R.string.pay_success)); } else { message.setText(successText); TTSHelper.speak(successText); } qrCodeViewModel.scan(); break; } }); qrCodeViewModel.getQRCodeStateEvent().observe(this, event -> { switch (event.getState()) { case QRCodeState.IDLE: message.setTextColor(getResources().getColor(R.color.pay_idle)); message.setText(R.string.pay_idle); break; case QRCodeState.SCANNING: message.setTextColor(getResources().getColor(R.color.pay_idle)); message.setText(R.string.qrcode_scanning); break; case QRCodeState.SUCCESS: TTSHelper.speak("beep"); PayData data = event.getData(); if (data == null) { LogUtils.w(TAG, "扫码成功,但任务创建失败"); return; } if (SCWebSocketClient.getInstance().isOpen()) { payOnlineViewModel.exec(data); } else { payOfflineViewModel.exec(data); } if (debugVs != null) return; String msg = String.format(Locale.getDefault(), getString(R.string.qrcode_text), data.getPayCode()); if (data.getTerminalType() != null) { msg += String.format(Locale.getDefault(), getString(R.string.terminal_type_text), data.getTerminalType()); } qrCodeText.setText(msg); break; case QRCodeState.FAILED: TTSHelper.speak("无效二维码"); qrCodeViewModel.scan(); break; case QRCodeState.ILLEGAL: message.setVisibility(View.GONE); sendText.setTextColor(getResources().getColor(R.color.serial_port_error)); sendText.setText(R.string.serial_port_init_failed); String logMessage = String.format(Locale.getDefault(), getString(R.string.log_dir), LogUtils.getConfig().getDir()); recText.setTextColor(getResources().getColor(R.color.serial_port_error)); recText.setText(logMessage); break; } }); commandViewModel.getCommandStateEvent().observe(this, event -> { switch (event.getState()) { case CommandState.IDLE: settingLayout.animate().setDuration(300).alpha(0f); break; case CommandState.WAIT: settingLayout.animate().setDuration(300).alpha(1f); break; case CommandState.SUCCESS: case CommandState.FAILED: settingText.setText(event.getMessage()); settingLayout.animate().setDuration(300).alpha(1f); break; } }); // 注意webSocketViewModel的初始化必须在SCWebSocketClient之后 payOnlineViewModel.initialize(); payOfflineViewModel.initialize(); qrCodeViewModel.initialize(); commandViewModel.initialize(); handler.post(updateTimeRunnable); SCWebSocketClient.getInstance().tryConnect(); } @Override protected void onDestroy() { super.onDestroy(); TTSHelper.release(); SCWebSocketClient.getInstance().close(); SCTaskExecutor.getInstance().quit(); } private void initViews() { debugVs = findViewById(R.id._debug_vs); settingLayout = findViewById(R.id._setting); logoLayout = findViewById(R.id._logo); settingImg = findViewById(R.id._setting_img); socketConnectedTime = findViewById(R.id._time); settingText = findViewById(R.id._setting_msg); message = findViewById(R.id._message); settingLayout.setAlpha(0); settingImg.post(() -> { Animation anim = new RotateAnimation(0f, 360f, 0.5f * settingImg.getWidth(), 0.5f * settingImg.getHeight()); anim.setDuration(2000); anim.setRepeatMode(Animation.RESTART); anim.setRepeatCount(-1); settingImg.startAnimation(anim); }); if (!BuildConfig.FLAVOR.equals("pro")) { inflateDebugLayout(); } } private void inflateDebugLayout() { if (debugVs == null) return; View layout = debugVs.inflate(); debugLayout = layout.findViewById(R.id._debug); snText = layout.findViewById(R.id._sn); versionText = layout.findViewById(R.id._version); sendText = layout.findViewById(R.id._send_msg); recText = layout.findViewById(R.id._recv_msg); qrCodeText = layout.findViewById(R.id._qrcode); serverText = layout.findViewById(R.id._server); ethText = layout.findViewById(R.id._eth); wifiText = layout.findViewById(R.id._wifi); debugVs = null; debugLayout.setVisibility(View.VISIBLE); logoLayout.setVisibility(View.GONE); debugLayout.setOnClickListener(this); logoLayout.setOnClickListener(this); refreshPackageInfo(); } private void refreshEthInfo() { String ethMac = EthernetHelper.getMacAddress(); String ethIP = EthernetHelper.getIpString(); 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(); wifiText.setText(String.format(Locale.getDefault(), getString(R.string.wifi_text), wifiMac, wifiSSID, wifiIP)); } private void refreshPackageInfo() { String versionName = ""; String versionCode = ""; PackageInfo pi; try { pi = getPackageManager().getPackageInfo(getPackageName(), 0); versionName = pi.versionName.toUpperCase(); versionCode = String.valueOf(pi.versionCode); } catch (PackageManager.NameNotFoundException e) { LogUtils.w(TAG, "无法获取app信息"); } versionText.setText(String.format(Locale.getDefault(), getString(R.string.version_text), versionName, versionCode)); snText.setText(String.format(Locale.getDefault(), getString(R.string.sn_text), deviceSN)); } private Runnable updateTimeRunnable = new Runnable() { @Override public void run() { socketConnectedTime.setText(socketConnectedTimeDateFormat.format(new Date())); handler.postDelayed(this, 1000); } }; @Override public boolean onKeyDown(int keyCode, KeyEvent event) { LogUtils.i(TAG, "按键KeyCode: " + keyCode); switch (keyCode) { case KeyEvent.KEYCODE_PAGE_UP: audioManager.adjustStreamVolume( AudioManager.STREAM_SYSTEM, AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI); break; case KeyEvent.KEYCODE_PAGE_DOWN: audioManager.adjustStreamVolume( AudioManager.STREAM_SYSTEM, AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI); break; } return super.onKeyDown(keyCode, event); } @Override public void onClick(View v) { switch (v.getId()) { case R.id._debug: debugLayout.setVisibility(View.GONE); logoLayout.setVisibility(View.VISIBLE); break; case R.id._logo: debugLayout.setVisibility(View.VISIBLE); logoLayout.setVisibility(View.GONE); break; } } }