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