From 0a7e1c2db46258036223a033b8bbd08be1e74666 Mon Sep 17 00:00:00 2001 From: Wong Joon Hui Date: Thu, 12 May 2022 11:06:41 +0800 Subject: [PATCH] Bug fix --- .idea/gradle.xml | 1 - .idea/misc.xml | 1 + app/build.gradle | 10 +- app/src/main/assets/CHANGELOG.md | 13 + .../main/assets/CST_1640011627_KIOSK002.env | 1 - .../CST_1640011627_KIOSK002_hwb_build.env | 18 -- .../CST_1640011627_KIOSK002_hwb_build.json | 19 ++ app/src/main/assets/SampleConfig.json | 19 ++ .../java/com/cst/im30/MainApplication.java | 15 +- .../com/cst/im30/activity/MainActivity.java | 72 ++++- .../cst/im30/activity/SettingActivity.java | 7 +- .../cst/im30/api/RetrofitAPICollection.java | 11 + .../java/com/cst/im30/common/AppConfig.java | 3 +- .../com/cst/im30/utility/DebugPassword.java | 85 +++++ .../com/cst/im30/utility/DeviceUtils.java | 18 ++ .../java/com/cst/im30/utility/JsonUtils.java | 13 + .../java/com/cst/im30/utility/Logger.java | 3 +- .../java/com/cst/im30/utility/SetupUtils.java | 298 +++++++++--------- app/src/main/res/layout/activity_main.xml | 2 + app/src/main/res/values/strings.xml | 2 +- gradle.properties | 2 + 21 files changed, 419 insertions(+), 194 deletions(-) create mode 100644 app/src/main/assets/CHANGELOG.md delete mode 100644 app/src/main/assets/CST_1640011627_KIOSK002.env delete mode 100644 app/src/main/assets/CST_1640011627_KIOSK002_hwb_build.env create mode 100644 app/src/main/assets/CST_1640011627_KIOSK002_hwb_build.json create mode 100644 app/src/main/assets/SampleConfig.json create mode 100644 app/src/main/java/com/cst/im30/utility/DebugPassword.java create mode 100644 app/src/main/java/com/cst/im30/utility/DeviceUtils.java diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 526b4c2..a2d7c21 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -13,7 +13,6 @@ - diff --git a/.idea/misc.xml b/.idea/misc.xml index c7de089..9925b15 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -33,6 +33,7 @@ + diff --git a/app/build.gradle b/app/build.gradle index 8a30848..786bb42 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,14 +7,18 @@ android { defaultConfig { applicationId "com.cst.im30" - minSdkVersion 22 + minSdkVersion 25 //noinspection ExpiredTargetSdkVersion targetSdkVersion 28 - versionCode 2 - versionName "1.0.1" + versionCode 4 + versionName "1.0.3" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + // Terminal Config + buildConfigField("String", "CONFIG_URL", CONFIG_URL) + buildConfigField("String", "AUTH_KEY", AUTH_KEY) + // Bugfender buildConfigField("String", "BUGFENDER_TOKEN", BUGFENDER_TOKEN) } diff --git a/app/src/main/assets/CHANGELOG.md b/app/src/main/assets/CHANGELOG.md new file mode 100644 index 0000000..6bd4aba --- /dev/null +++ b/app/src/main/assets/CHANGELOG.md @@ -0,0 +1,13 @@ +V4 +- Change from file to fetch config via web +- Add fixed url/token to gradle.properties +- Added DebugCode to access config + +V3 +- Bug fix for Echo Socket + +V2 +- Extract environment from gradle.properties to external file at sdcard/CST/.env + +V1 +- Initial \ No newline at end of file diff --git a/app/src/main/assets/CST_1640011627_KIOSK002.env b/app/src/main/assets/CST_1640011627_KIOSK002.env deleted file mode 100644 index d815106..0000000 --- a/app/src/main/assets/CST_1640011627_KIOSK002.env +++ /dev/null @@ -1 +0,0 @@ -IyBFY2hvIFNvY2tldApTRVJWRVJfVVJMPSJodHRwOi8vaGVsbG8td29ybGQtcmV3YXJkcy1ib29raW5nLWFwaS1idWlsZC50ZXN0cGlnZW9uLm5ldCIKQ0xJRU5UX0lEPSIzIgpDTElFTlRfU0VDUkVUPSJ3NVJKN2JNRjZOSDM5YURWeUpKVjBNMWdPZzBuWlFjTzJhdFZjWHlGIgpFQ0hPX1NFUlZFUl9VUkw9Imh0dHA6Ly9oZWxsby13b3JsZC1yZXdhcmRzLWJvb2tpbmctYXBpLWJ1aWxkLnRlc3RwaWdlb24ubmV0IgpFQ0hPX1NFUlZFUl9QT1JUPSI2MDAxIgpLSU9TS19DT0RFPSJLSU9TSzAwMiIKU09DS0VUX1BSRUZJWD0iSGVsbG9Cb29raW5nQXBpXyIKUEFZTUVOVF9DSEFOTkVMX0lEPSJwYXltZW50IyIKUEFZTUVOVF9FVkVOVF9UWVBFPSIubWFrZXBheW1lbnQiCklDX0NIQU5ORUxfSUQ9InZlcmlmaWNhdGlvbiMiCklDX0VWRU5UX1RZUEU9Ii5ndWVzdHZlcmlmaWNhdGlvbiIKCiMgQVdTCkFXU19BQ0NFU1NfS0VZPSJBS0lBWEhXRVBQUFNQT01TTEw1UiIKQVdTX1NFQ1JFVF9LRVk9Ik5oTzdJR2h3Y1RkV080MWdTMEtURUtnaVo3bWlrbVo0SGRNRzFXMlIiCgpMT0dfR1JPVVBfTkFNRT0iaG90ZWwta2lvc2si \ No newline at end of file diff --git a/app/src/main/assets/CST_1640011627_KIOSK002_hwb_build.env b/app/src/main/assets/CST_1640011627_KIOSK002_hwb_build.env deleted file mode 100644 index 7800b70..0000000 --- a/app/src/main/assets/CST_1640011627_KIOSK002_hwb_build.env +++ /dev/null @@ -1,18 +0,0 @@ -# Echo Socket -SERVER_URL="http://hello-world-rewards-booking-api-build.testpigeon.net" -CLIENT_ID="3" -CLIENT_SECRET="w5RJ7bMF6NH39aDVyJJV0M1gOg0nZQcO2atVcXyF" -ECHO_SERVER_URL="http://hello-world-rewards-booking-api-build.testpigeon.net" -ECHO_SERVER_PORT="6001" -KIOSK_CODE="KIOSK002" -SOCKET_PREFIX="HelloBookingApi_" -PAYMENT_CHANNEL_ID="payment#" -PAYMENT_EVENT_TYPE=".makepayment" -IC_CHANNEL_ID="verification#" -IC_EVENT_TYPE=".guestverification" - -# AWS -AWS_ACCESS_KEY="AKIAXHWEPPPSPOMSLL5R" -AWS_SECRET_KEY="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" - -LOG_GROUP_NAME="hotel-kiosk" \ No newline at end of file diff --git a/app/src/main/assets/CST_1640011627_KIOSK002_hwb_build.json b/app/src/main/assets/CST_1640011627_KIOSK002_hwb_build.json new file mode 100644 index 0000000..c5cafe1 --- /dev/null +++ b/app/src/main/assets/CST_1640011627_KIOSK002_hwb_build.json @@ -0,0 +1,19 @@ +{ + "data" : { + "server_url" : "http://hello-world-rewards-booking-api-build.testpigeon.net", + "client_id" : "3", + "client_secret" : "w5RJ7bMF6NH39aDVyJJV0M1gOg0nZQcO2atVcXyF", + "echo_server_url" : "http://hello-world-rewards-booking-api-build.testpigeon.net", + "echo_server_port" : "6001", + "kiosk_code" : "KIOSK002", + "socket_prefix" : "HelloBookingApi_", + "payment_channel_id" : "payment#", + "payment_event_type" : ".makepayment", + "ic_channel_id" : "verification#", + "ic_event_type" : ".guestverification", + "aws_access_key" : "AKIAXHWEPPPSPOMSLL5R", + "aws_secret_key" : "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", + "aws_log_group" : "hotel-kiosk", + "debug_code" : "1234" + } +} \ No newline at end of file diff --git a/app/src/main/assets/SampleConfig.json b/app/src/main/assets/SampleConfig.json new file mode 100644 index 0000000..c5cafe1 --- /dev/null +++ b/app/src/main/assets/SampleConfig.json @@ -0,0 +1,19 @@ +{ + "data" : { + "server_url" : "http://hello-world-rewards-booking-api-build.testpigeon.net", + "client_id" : "3", + "client_secret" : "w5RJ7bMF6NH39aDVyJJV0M1gOg0nZQcO2atVcXyF", + "echo_server_url" : "http://hello-world-rewards-booking-api-build.testpigeon.net", + "echo_server_port" : "6001", + "kiosk_code" : "KIOSK002", + "socket_prefix" : "HelloBookingApi_", + "payment_channel_id" : "payment#", + "payment_event_type" : ".makepayment", + "ic_channel_id" : "verification#", + "ic_event_type" : ".guestverification", + "aws_access_key" : "AKIAXHWEPPPSPOMSLL5R", + "aws_secret_key" : "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", + "aws_log_group" : "hotel-kiosk", + "debug_code" : "1234" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cst/im30/MainApplication.java b/app/src/main/java/com/cst/im30/MainApplication.java index 8c8a727..388d527 100644 --- a/app/src/main/java/com/cst/im30/MainApplication.java +++ b/app/src/main/java/com/cst/im30/MainApplication.java @@ -34,6 +34,7 @@ import com.cst.im30.model.SettlementResponse; import com.cst.im30.model.VoidRequest; import com.cst.im30.model.VoidResponse; import com.cst.im30.utility.CloudWatchLogger; +import com.cst.im30.utility.DeviceUtils; import com.cst.im30.utility.Logger; import com.cst.im30.utility.SetupUtils; @@ -110,7 +111,7 @@ public class MainApplication extends Application { super.onCreate(); instance = this; - if (new SetupUtils().readEnvFile()) { + if (new SetupUtils().setupConfig(getApplicationContext())) { setupReady = true; } @@ -126,18 +127,22 @@ public class MainApplication extends Application { } public void log(String logStreamName, String message) { + if (config == null) return; this.cloudWatchLogger.log(config.getLogGroupName(), logStreamName, message); } public void logICScan(String message) { + if (config == null) return; this.cloudWatchLogger.log(config.getLogGroupName(), "IcScanLog", message); } public void logPayment(String message) { + if (config == null) return; this.cloudWatchLogger.log(config.getLogGroupName(), "Payment", message); } public void logError(String message) { + if (config == null) return; this.cloudWatchLogger.log(config.getLogGroupName(), "Error", message); } @@ -145,12 +150,8 @@ public class MainApplication extends Application { public void initializeBugfender() { Bugfender.init(this, BuildConfig.BUGFENDER_TOKEN, BuildConfig.DEBUG); - String serialNumber; - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - serialNumber = android.os.Build.getSerial(); - } else { - serialNumber = android.os.Build.SERIAL; - } + String serialNumber = DeviceUtils.getSerialNumber(); + Bugfender.setDeviceString("Stage", BuildConfig.FLAVOR); Bugfender.setDeviceString("S/N", serialNumber); Bugfender.setDeviceString("Kiosk Code", config.getKioskCode()); diff --git a/app/src/main/java/com/cst/im30/activity/MainActivity.java b/app/src/main/java/com/cst/im30/activity/MainActivity.java index acb1bf3..384e52f 100644 --- a/app/src/main/java/com/cst/im30/activity/MainActivity.java +++ b/app/src/main/java/com/cst/im30/activity/MainActivity.java @@ -13,11 +13,12 @@ import android.content.pm.PackageManager; import android.graphics.Color; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.ColorDrawable; -import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -26,11 +27,9 @@ import android.widget.Toast; import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; -import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; -import com.cst.im30.BuildConfig; import com.cst.im30.EchoClient; import com.cst.im30.MainApplication; import com.cst.im30.PaymentHandler; @@ -43,8 +42,11 @@ import com.cst.im30.service.PaymentService; import com.cst.im30.service.UploadTransactionPreAuthPaymentService; import com.cst.im30.service.UploadTransactionSaleCompletionPaymentService; import com.cst.im30.service.UploadTransactionSalePaymentService; +import com.cst.im30.utility.DebugPassword; +import com.cst.im30.utility.DeviceUtils; import com.cst.im30.utility.Logger; import com.cst.im30.utility.PaymentUtils; +import com.cst.im30.utility.SetupUtils; import com.daimajia.slider.library.SliderLayout; import com.daimajia.slider.library.SliderTypes.BaseSliderView; import com.daimajia.slider.library.SliderTypes.TextSliderView; @@ -65,13 +67,14 @@ public class MainActivity extends AppCompatActivity implements CallableInterface private AnimationDrawable animationDrawable; // Echo Server Connection private LinearLayout echoStatusLayout; + private FrameLayout paymentEchoStatusLayout, icEchoStatusLayout; private ImageView paymentEchoStatusImageView, icEchoStatusImageView; private TextView paymentEchoStatusTextView, icEchoStatusTextView; private TextView testTitle; //todo until new screen private SliderLayout sliderLayout; private PaymentHandler paymentHandler; + private DebugPassword debugPassword; - @RequiresApi(api = Build.VERSION_CODES.M) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -79,11 +82,13 @@ public class MainActivity extends AppCompatActivity implements CallableInterface checkPermissions(); - - sliderLayout = findViewById(R.id.slider_layout_ma); sliderLayout.setVisibility(View.INVISIBLE); //todo until get good image + paymentEchoStatusLayout = findViewById(R.id.paymentEchoStatusLayout); + icEchoStatusLayout = findViewById(R.id.icEchoStatusLayout); + icEchoStatusLayout = findViewById(R.id.icEchoStatusLayout); + echoStatusLayout = findViewById(R.id.echoStatusLayout); paymentEchoStatusImageView = findViewById(R.id.paymentEchoStatusImageView); icEchoStatusImageView = findViewById(R.id.icEchoStatusImageView); @@ -96,6 +101,18 @@ public class MainActivity extends AppCompatActivity implements CallableInterface //setupTextSlider(); paymentHandler = new PaymentHandler(this); + + if (MainApplication.config != null && MainApplication.config.getDebugCode() != null && !MainApplication.config.getDebugCode().isEmpty()) { + try { + debugPassword = new DebugPassword(MainApplication.config.getDebugCode()); + + paymentEchoStatusLayout.setOnClickListener(v -> debugPress(DebugPassword.INPUT_0)); + icEchoStatusLayout.setOnClickListener(v -> debugPress(DebugPassword.INPUT_1)); + testTitle.setOnClickListener(v -> debugPress(DebugPassword.INPUT_RESET)); + } catch (Exception e) { + Logger.logD("Debug Code: " + MainApplication.config.getDebugCode() + " is invalid! Debug Disabled."); + } + } } @Override @@ -247,7 +264,10 @@ public class MainActivity extends AppCompatActivity implements CallableInterface private void setupNotReady() { //echoStatusLayout.setVisibility(View.GONE); + String text = "S/N: " + DeviceUtils.getSerialNumber() + " has no configuration yet."; + TextView setupText = findViewById(R.id.setupText); + setupText.setText(text); setupText.setVisibility(View.VISIBLE); } @@ -617,7 +637,11 @@ public class MainActivity extends AppCompatActivity implements CallableInterface private void onConnectSuccessPayment(Object[] args) { try { - String channelID = MainApplication.config.getSocketPrefix() + MainApplication.config.getPaymentChannelId() + MainApplication.config.getKioskCode(); + String channelId = MainApplication.config.getPaymentChannelId(); + if (!channelId.endsWith("#")) { + channelId += "#"; + } + String channelID = MainApplication.config.getSocketPrefix() + channelId + MainApplication.config.getKioskCode(); String eventType = MainApplication.config.getPaymentEventType(); EchoCallback echoCallback = this::receiveMessagePayment; MainApplication.paymentClient.channel(channelID).listen(eventType, echoCallback); @@ -628,7 +652,7 @@ public class MainActivity extends AppCompatActivity implements CallableInterface updatePaymentOnline(); } catch (Exception ex) { - Logger.logE("Error on contact: " + ex.getMessage()); + Logger.logE("Error on contact: " + Log.getStackTraceString(ex)); } } @@ -650,7 +674,11 @@ public class MainActivity extends AppCompatActivity implements CallableInterface private void onConnectSuccessIC(Object[] args) { try { - String channelID = MainApplication.config.getSocketPrefix() + MainApplication.config.getIcChannelId() + MainApplication.config.getKioskCode(); + String channelId = MainApplication.config.getIcChannelId(); + if (!channelId.endsWith("#")) { + channelId += "#"; + } + String channelID = MainApplication.config.getSocketPrefix() + channelId + MainApplication.config.getKioskCode(); String eventType = MainApplication.config.getIcEventType(); EchoCallback echoCallback = this::receiveMessageIC; MainApplication.icClient.channel(channelID).listen(eventType, echoCallback); @@ -661,7 +689,7 @@ public class MainActivity extends AppCompatActivity implements CallableInterface updateICOnline(); } catch (Exception ex) { - Logger.logE("Error on contact: " + ex.getMessage()); + Logger.logE("Error on contact: " + Log.getStackTraceString(ex)); } } @@ -699,7 +727,7 @@ public class MainActivity extends AppCompatActivity implements CallableInterface runOnUiThread(() -> { paymentEchoStatusImageView.setImageResource(R.drawable.echo_payment_online); paymentEchoStatusTextView.setText(R.string.online_caps); - paymentEchoStatusTextView.setTextColor(getResources().getColor(R.color.online)); + paymentEchoStatusTextView.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.online)); }); } @@ -707,7 +735,7 @@ public class MainActivity extends AppCompatActivity implements CallableInterface runOnUiThread(() -> { paymentEchoStatusImageView.setImageResource(R.drawable.echo_payment_offline); paymentEchoStatusTextView.setText(R.string.offline_caps); - paymentEchoStatusTextView.setTextColor(getResources().getColor(R.color.offline)); + paymentEchoStatusTextView.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.offline)); }); } @@ -715,7 +743,7 @@ public class MainActivity extends AppCompatActivity implements CallableInterface runOnUiThread(() -> { icEchoStatusImageView.setImageResource(R.drawable.echo_ic_online); icEchoStatusTextView.setText(R.string.online_caps); - icEchoStatusTextView.setTextColor(getResources().getColor(R.color.online)); + icEchoStatusTextView.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.online)); }); } @@ -723,7 +751,7 @@ public class MainActivity extends AppCompatActivity implements CallableInterface runOnUiThread(() -> { icEchoStatusImageView.setImageResource(R.drawable.echo_ic_offline); icEchoStatusTextView.setText(R.string.offline_caps); - icEchoStatusTextView.setTextColor(getResources().getColor(R.color.offline)); + icEchoStatusTextView.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.offline)); }); } @@ -770,7 +798,6 @@ public class MainActivity extends AppCompatActivity implements CallableInterface Logger.logI("Back Button Disabled!"); } - @RequiresApi(api = Build.VERSION_CODES.M) private void checkPermissions() { List permsArray = new ArrayList<>(); @@ -829,4 +856,19 @@ public class MainActivity extends AppCompatActivity implements CallableInterface mHandler.postDelayed(mRunnable, 3000); } + private void debugPress(String input) { + debugPassword.input(input); + if (debugPassword.isUnlock()) { + runDebug(); + } + } + + private void runDebug() { + Logger.logD("Debug Code Entered!"); + + SetupUtils.showConfigDialog(MainActivity.this); + + debugPassword.reset(); + } + } \ No newline at end of file diff --git a/app/src/main/java/com/cst/im30/activity/SettingActivity.java b/app/src/main/java/com/cst/im30/activity/SettingActivity.java index bf160ec..927743a 100644 --- a/app/src/main/java/com/cst/im30/activity/SettingActivity.java +++ b/app/src/main/java/com/cst/im30/activity/SettingActivity.java @@ -168,7 +168,12 @@ public class SettingActivity extends AppCompatActivity { runOnUiThread(() -> Toast.makeText(SettingActivity.this, "Connected!", Toast.LENGTH_SHORT).show()); - echoClient.channel(MainApplication.config.getPaymentChannelId() + MainApplication.config.getKioskCode()) + String channelId = MainApplication.config.getPaymentChannelId(); + if (!channelId.endsWith("#")) { + channelId += "#"; + } + + echoClient.channel(channelId + MainApplication.config.getKioskCode()) .listen(MainApplication.config.getPaymentEventType(), this::receiveMessage); } diff --git a/app/src/main/java/com/cst/im30/api/RetrofitAPICollection.java b/app/src/main/java/com/cst/im30/api/RetrofitAPICollection.java index 02cd1b3..18ebc46 100644 --- a/app/src/main/java/com/cst/im30/api/RetrofitAPICollection.java +++ b/app/src/main/java/com/cst/im30/api/RetrofitAPICollection.java @@ -46,4 +46,15 @@ public interface RetrofitAPICollection { @Body String body ); + @Headers({ + "Accept: application/json", + "Content-Type: application/json" + }) + @GET("api/v1/terminal-im30/{serialNumber}") + Call getConfig( + @Header("x-api-id") String authId, + @Header("x-api-key") String authKey, + @Path("serialNumber") String serialNumber + ); + } diff --git a/app/src/main/java/com/cst/im30/common/AppConfig.java b/app/src/main/java/com/cst/im30/common/AppConfig.java index f600857..f312e86 100644 --- a/app/src/main/java/com/cst/im30/common/AppConfig.java +++ b/app/src/main/java/com/cst/im30/common/AppConfig.java @@ -23,6 +23,7 @@ public class AppConfig implements Serializable { icEventType, awsAccessKey, awsSecretKey, - logGroupName; + logGroupName, + debugCode; } diff --git a/app/src/main/java/com/cst/im30/utility/DebugPassword.java b/app/src/main/java/com/cst/im30/utility/DebugPassword.java new file mode 100644 index 0000000..4e68739 --- /dev/null +++ b/app/src/main/java/com/cst/im30/utility/DebugPassword.java @@ -0,0 +1,85 @@ +package com.cst.im30.utility; + +import java.security.InvalidParameterException; +import java.util.Objects; + +public class DebugPassword { + + private final String password; + private final String convertedPassword; + private String entered; + private boolean unlock = false; + + public DebugPassword(String password) { + if (password == null || password.isEmpty()) { + throw new InvalidParameterException("Password is blank or null. Debug Menu disabled!"); + } + if (!validatePassword(password)) { + throw new InvalidParameterException("Invalid Password: " + password + "! Only digits 1-9 accepted, e.g. \"1234\", \"123\""); + } + this.password = password; + this.convertedPassword = convertPassword(password); + this.entered = ""; + + Logger.logD("Debug Password Enabled."); + } + + // Password must be a string with digits only from 1-9 (e.g. "1234", "123"). Any length accepted. + private boolean validatePassword(String password) { + if (password == null || password.isEmpty()) { + return false; + } + String regex = "[1-9]+"; + return password.matches(regex); + } + + private String convertPassword(String password) { + if (password == null || password.isEmpty()) { return null; } + + StringBuilder tmp = new StringBuilder(); + + boolean left = true; + for (char ch : password.toCharArray()) { + int v = Integer.parseInt(Character.toString(ch)); + for (int i = 0; i < v; i++) { + tmp.append((left ? INPUT_0 : INPUT_1)); + } + left = !left; + } + return tmp.toString(); + } + + public static final String INPUT_0 = "0"; + public static final String INPUT_1 = "1"; + public static final String INPUT_RESET = "-1"; + + public void input(String input) { + if (Objects.equals(input, INPUT_0)) { + entered += INPUT_0; + } else if (Objects.equals(input, INPUT_1)) { + entered += INPUT_1; + } else { + entered = ""; + } + + // Compare + String filteredPassword = convertedPassword.substring(0, entered.length()); + if (!entered.equalsIgnoreCase(filteredPassword)) { + entered = ""; + } + + if (entered.equalsIgnoreCase(convertedPassword)) { + entered = ""; + unlock = true; + } + } + + public boolean isUnlock() { + return unlock; + } + + public void reset() { + entered = ""; + unlock = false; + } +} diff --git a/app/src/main/java/com/cst/im30/utility/DeviceUtils.java b/app/src/main/java/com/cst/im30/utility/DeviceUtils.java new file mode 100644 index 0000000..3d42ee3 --- /dev/null +++ b/app/src/main/java/com/cst/im30/utility/DeviceUtils.java @@ -0,0 +1,18 @@ +package com.cst.im30.utility; + +import android.annotation.SuppressLint; +import android.os.Build; + +public class DeviceUtils { + + @SuppressLint("MissingPermission") + public static String getSerialNumber() { + String serialNumber; + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + serialNumber = android.os.Build.getSerial(); + } else { + serialNumber = android.os.Build.SERIAL; + } + return serialNumber; + } +} diff --git a/app/src/main/java/com/cst/im30/utility/JsonUtils.java b/app/src/main/java/com/cst/im30/utility/JsonUtils.java index d6115c3..f74b60b 100644 --- a/app/src/main/java/com/cst/im30/utility/JsonUtils.java +++ b/app/src/main/java/com/cst/im30/utility/JsonUtils.java @@ -2,10 +2,23 @@ package com.cst.im30.utility; import android.text.TextUtils; +import org.json.JSONException; import org.json.JSONObject; public class JsonUtils { + public static JSONObject convertJSONStringToJSONObject(String string) { + try { + if (string == null || string.isEmpty()) { + return null; + } + return new JSONObject(string); + } catch (JSONException e) { + e.printStackTrace(); + return null; + } + } + public static String extractJsonString(JSONObject responseJSON, String name) { try { if (responseJSON.has(name)) { diff --git a/app/src/main/java/com/cst/im30/utility/Logger.java b/app/src/main/java/com/cst/im30/utility/Logger.java index ca8fbe3..5bb7cbb 100644 --- a/app/src/main/java/com/cst/im30/utility/Logger.java +++ b/app/src/main/java/com/cst/im30/utility/Logger.java @@ -53,7 +53,6 @@ public class Logger { Log.e(TAG, title + " - " + message); Bugfender.e("ERROR", title + " - " + message); MainApplication.getInstance().logError(title + " - " + message); - Bugfender.sendIssue(title, message); } public static void logW(String message) { @@ -114,7 +113,7 @@ public class Logger { MainApplication.getInstance().logICScan(data + " - " + extra); } catch (IOException e) { - Log.e("Exception", "File write failed: " + e.toString()); + Log.e("Exception", "File write failed: " + e); } } diff --git a/app/src/main/java/com/cst/im30/utility/SetupUtils.java b/app/src/main/java/com/cst/im30/utility/SetupUtils.java index a338985..813b502 100644 --- a/app/src/main/java/com/cst/im30/utility/SetupUtils.java +++ b/app/src/main/java/com/cst/im30/utility/SetupUtils.java @@ -1,198 +1,208 @@ package com.cst.im30.utility; +import android.app.AlertDialog; import android.content.Context; -import android.os.Environment; -import android.util.Base64; +import android.util.Log; +import androidx.annotation.NonNull; + +import com.cst.im30.BuildConfig; import com.cst.im30.MainApplication; +import com.cst.im30.R; +import com.cst.im30.activity.MainActivity; +import com.cst.im30.api.RetrofitAPICollection; +import com.cst.im30.api.RetrofitClient; import com.cst.im30.common.AppConfig; +import org.json.JSONException; +import org.json.JSONObject; + import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; -import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import lombok.SneakyThrows; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; public class SetupUtils { - public boolean readEnvFile() { - if (isExternalStorageReadable()) { - File configFile = getFile(); - if (configFile == null) { return false; } + private final boolean LOCAL_CONFIG = false; - long numberOfLines = countLines(configFile); + private String envJsonString = null; - if (numberOfLines == 1) { - decodeAndParseFile(configFile); - } else { - parseFile(configFile); - } + public static String getSampleEnv(Context context) { + try { + InputStream inputStream = context.getAssets().open("SampleConfig.json"); + return new BufferedReader( + new InputStreamReader(inputStream, StandardCharsets.UTF_8)) + .lines() + .collect(Collectors.joining("\n")); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } - return true; + @SneakyThrows + public boolean setupConfig(Context context) { + if (LOCAL_CONFIG) { + envJsonString = SetupUtils.getSampleEnv(context); } else { - Logger.logD("External Storage Not Readable"); + Thread t = new Thread(this::getConfig); + t.start(); + t.join(); + } + + if (envJsonString == null || envJsonString.isEmpty()) { + Logger.logE("Unsuccessful in obtaining Configuration"); + return false; + } + JSONObject json = JsonUtils.convertJSONStringToJSONObject(envJsonString); + if (json == null) { + Logger.logD("JSON is null"); return false; } - } - private File getFile() { - File appDirectory = new File(Environment.getExternalStorageDirectory() + "/CST"); - File[] files = appDirectory.listFiles(); + if (json.has("data")) { + try { + json = json.getJSONObject("data"); + } catch (JSONException e) { + e.printStackTrace(); + return false; + } + } - if (files == null || files.length == 0) { - Logger.logD("ENV File Not Found! (Folder Empty)"); - return null; + if (json.has("current")) { + try { + json = json.getJSONObject("current"); + } catch (JSONException e) { + e.printStackTrace(); + return false; + } } - for (File file : files) { - String fileName = file.getName().toLowerCase(); - if (fileName.endsWith(".env")) { - Logger.logD("Found: " + file.getAbsolutePath()); - return file; + LinkedHashMap infoMap = new LinkedHashMap<>(); + Iterator keys = json.keys(); + while (keys.hasNext()) { + String key = keys.next(); + if (json.has(key)) { + String value = JsonUtils.extractJsonString(json, key); + String k = key.trim().replaceAll("^\"|\"$", ""); + String v = value.trim().replaceAll("^\"|\"$", ""); + infoMap.put(k, v); } } - Logger.logD("ENV File Not Found! (Got Files but no ENV)"); - return null; + if (infoMap.size() > 0) { + readConfiguration(infoMap); + } else { + return false; + } + return true; } - private boolean decodeAndParseFile(File file) { - try { - FileInputStream is = new FileInputStream(file); - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - - String config; + public void getConfig() { + Logger.logD("START"); + String serialNumber = DeviceUtils.getSerialNumber(); + //String serialNumber = "1640011627"; + RetrofitAPICollection service = RetrofitClient.getRetrofitClient(BuildConfig.CONFIG_URL).create(RetrofitAPICollection.class); + Call call = service.getConfig(serialNumber, BuildConfig.AUTH_KEY, serialNumber); - String base64Line = reader.readLine(); - byte[] decodedString = Base64.decode(base64Line.getBytes(StandardCharsets.UTF_8), Base64.DEFAULT); - config = new String(decodedString); - Map map = parseDecodedFile(config); - readConfiguration(map); + Logger.logAPI(serialNumber); - return true; - } catch (IOException e) { - e.printStackTrace(); - } - return false; - } + /*call.enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull Response response) { + onResponseGetConfig(response); + } - private long countLines(File file) { - long lines = 0; - try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) { - String line; - while ((line = reader.readLine()) != null) { - if (line.trim().isEmpty()) { - continue; - } - lines++; + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + assert true; + //onFailureGetConfig(t); } + });*/ + + try { + Response response = call.execute(); + onResponseGetConfig(response); } catch (IOException e) { e.printStackTrace(); } - return lines; - } - - private Map parseDecodedFile(String config) { - String[] lines = config.split(Pattern.quote("\n")); - - LinkedHashMap infoMap = new LinkedHashMap<>(); - for (String line : lines) { - if (line.length() > 0 && !line.startsWith("#")) { - String[] value = line.split("="); - String k, v; - k = value[0]; - if (value.length == 1) { - v = ""; - } else { - // Filter In-Line # Comments - String vStr = value[1]; - int commentIndex = vStr.indexOf("#"); - if (commentIndex != -1) { - vStr = vStr.substring(0, commentIndex); - } - v = vStr; - } - String key = k.trim().replaceAll("^\"|\"$", ""); - String valueMap = v.trim().replaceAll("^\"|\"$", ""); - infoMap.put(key, valueMap); - } - } - return infoMap; } - private boolean parseFile(File file) { + private void onResponseGetConfig(Response response) { try { - FileInputStream is = new FileInputStream(file); - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - - LinkedHashMap infoMap = new LinkedHashMap<>(); - String line, k, v; - String[] value; - while ((line = reader.readLine()) != null) { - if (line.length() > 0 && !line.startsWith("#")) { - value = line.split("="); - k = value[0]; - if (value.length == 1) { - v = ""; - } else { - // Allows # Comments in the line - String vStr = value[1]; - int commentIndex = vStr.indexOf("#"); - if (commentIndex != -1) { - vStr = vStr.substring(0, commentIndex); - } - v = vStr; - } - String key = k.trim().replaceAll("^\"|\"$", ""); - String valueMap = v.trim().replaceAll("^\"|\"$", ""); - infoMap.put(key, valueMap); + if (!response.isSuccessful()) { + JSONObject responseJSON; + if (response.errorBody() != null) { + responseJSON = new JSONObject(response.errorBody().string()); + Logger.logAPI(responseJSON.toString()); } - } - - if (infoMap.isEmpty()) { return false; } - - readConfiguration(infoMap); + } else { + JSONObject responseJSON = new JSONObject(response.body()); + Logger.logAPI(responseJSON.toString()); - } catch (IOException e) { - e.printStackTrace(); + envJsonString = responseJSON.toString(); + } + } catch (Exception e) { + Logger.logE(Log.getStackTraceString(e)); } - return true; } private void readConfiguration(Map map) { MainApplication.config = AppConfig.builder() - .serverURL(map.get("SERVER_URL")) - .clientId(map.get("CLIENT_ID")) - .clientSecret(map.get("CLIENT_SECRET")) - .echoServerUrl(map.get("ECHO_SERVER_URL")) - .echoServerPort(map.get("ECHO_SERVER_PORT")) - .kioskCode(map.get("KIOSK_CODE")) - .socketPrefix(map.get("SOCKET_PREFIX")) - .paymentChannelId(map.get("PAYMENT_CHANNEL_ID")) - .paymentEventType(map.get("PAYMENT_EVENT_TYPE")) - .icChannelId(map.get("IC_CHANNEL_ID")) - .icEventType(map.get("IC_EVENT_TYPE")) - .awsAccessKey(map.get("AWS_ACCESS_KEY")) - .awsSecretKey(map.get("AWS_SECRET_KEY")) - .logGroupName(map.get("LOG_GROUP_NAME")) + .serverURL(map.get("server_url")) + .clientId(map.get("client_id")) + .clientSecret(map.get("client_secret")) + .echoServerUrl(map.get("echo_server_url")) + .echoServerPort(map.get("echo_server_port")) + .kioskCode(map.get("kiosk_code")) + .socketPrefix(map.get("socket_prefix")) + .paymentChannelId(map.get("payment_channel_id")) + .paymentEventType(map.get("payment_event_type")) + .icChannelId(map.get("ic_channel_id")) + .icEventType(map.get("ic_event_type")) + .awsAccessKey(map.get("aws_access_key")) + .awsSecretKey(map.get("aws_secret_key")) + .logGroupName(map.get("aws_log_group")) + .debugCode(map.get("debug_code")) .build(); } - /* Checks if external storage is available to at least read */ - public boolean isExternalStorageReadable() { - String state = Environment.getExternalStorageState(); - return Environment.MEDIA_MOUNTED.equals(state) || - Environment.MEDIA_MOUNTED_READ_ONLY.equals(state); + public static void showConfigDialog(final Context context) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + + String message = "Kiosk Code:" + MainApplication.config.getKioskCode() + "\n\n" + + "Host URL:" + MainApplication.config.getServerURL() + "\n\n" + + "Client ID:" + MainApplication.config.getClientId() + "\n" + + "Client Secret:" + MainApplication.config.getClientSecret() + "\n\n" + + + "Echo URL:" + MainApplication.config.getEchoServerUrl() + "\n" + + "Echo Port:" + MainApplication.config.getEchoServerPort() + "\n\n" + + "Socket Prefix:" + MainApplication.config.getSocketPrefix() + "\n" + + "Payment Channel ID:" + MainApplication.config.getPaymentChannelId() + "\n" + + "Payment Event Type:" + MainApplication.config.getPaymentEventType() + "\n" + + "IC Channel ID:" + MainApplication.config.getIcChannelId() + "\n" + + "IC Event Type:" + MainApplication.config.getIcEventType() + "\n\n" + + "AWS Access Key:" + MainApplication.config.getAwsAccessKey() + "\n\n" + + "AWS Secret Key:" + MainApplication.config.getClientSecret() + "\n\n" + + "AWS Log Group Name:" + MainApplication.config.getLogGroupName(); + + builder.setMessage(message) + .setCancelable(true).setPositiveButton("Dismiss", (dialog, which) -> dialog.dismiss()); + + AlertDialog alert = builder.create(); + alert.setTitle(context.getString(R.string.app_name) + " - " + BuildConfig.VERSION_NAME); + alert.show(); } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 2842197..f4ad84b 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -15,6 +15,7 @@ android:baselineAligned="false"> - CST - Hello Kiosk + CST - IM30 Terminal Title diff --git a/gradle.properties b/gradle.properties index 5ae1830..e33a0ae 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,6 +20,8 @@ android.enableJetifier=true # Terminal Specific Config +CONFIG_URL="https://dlrkv6rvha.execute-api.ap-southeast-1.amazonaws.com/production" +AUTH_KEY="G7AdPthrwWp2EoxT5gn6KE6WVgsfjRdtHBhTzSRNKIKmb4jsRMkcS6O5xSdW" # Bugfender BUGFENDER_TOKEN="rocgWcZ0aLZH7s1XqK6PpV3WeyBVb0w0" \ No newline at end of file