Wong Joon Hui
3 years ago
21 changed files with 419 additions and 194 deletions
@ -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/<filename>.env |
||||
|
||||
V1 |
||||
- Initial |
@ -1 +0,0 @@
|
||||
IyBFY2hvIFNvY2tldApTRVJWRVJfVVJMPSJodHRwOi8vaGVsbG8td29ybGQtcmV3YXJkcy1ib29raW5nLWFwaS1idWlsZC50ZXN0cGlnZW9uLm5ldCIKQ0xJRU5UX0lEPSIzIgpDTElFTlRfU0VDUkVUPSJ3NVJKN2JNRjZOSDM5YURWeUpKVjBNMWdPZzBuWlFjTzJhdFZjWHlGIgpFQ0hPX1NFUlZFUl9VUkw9Imh0dHA6Ly9oZWxsby13b3JsZC1yZXdhcmRzLWJvb2tpbmctYXBpLWJ1aWxkLnRlc3RwaWdlb24ubmV0IgpFQ0hPX1NFUlZFUl9QT1JUPSI2MDAxIgpLSU9TS19DT0RFPSJLSU9TSzAwMiIKU09DS0VUX1BSRUZJWD0iSGVsbG9Cb29raW5nQXBpXyIKUEFZTUVOVF9DSEFOTkVMX0lEPSJwYXltZW50IyIKUEFZTUVOVF9FVkVOVF9UWVBFPSIubWFrZXBheW1lbnQiCklDX0NIQU5ORUxfSUQ9InZlcmlmaWNhdGlvbiMiCklDX0VWRU5UX1RZUEU9Ii5ndWVzdHZlcmlmaWNhdGlvbiIKCiMgQVdTCkFXU19BQ0NFU1NfS0VZPSJBS0lBWEhXRVBQUFNQT01TTEw1UiIKQVdTX1NFQ1JFVF9LRVk9Ik5oTzdJR2h3Y1RkV080MWdTMEtURUtnaVo3bWlrbVo0SGRNRzFXMlIiCgpMT0dfR1JPVVBfTkFNRT0iaG90ZWwta2lvc2si |
@ -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" |
@ -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" |
||||
} |
||||
} |
@ -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" |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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<String, String> infoMap = new LinkedHashMap<>(); |
||||
Iterator<String> 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<String> 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<String, String> map = parseDecodedFile(config); |
||||
readConfiguration(map); |
||||
Logger.logAPI(serialNumber); |
||||
|
||||
return true; |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
return false; |
||||
} |
||||
/*call.enqueue(new Callback<String>() { |
||||
@Override |
||||
public void onResponse(@NonNull Call<String> call, @NonNull Response<String> 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<String> call, @NonNull Throwable t) { |
||||
assert true; |
||||
//onFailureGetConfig(t);
|
||||
} |
||||
});*/ |
||||
|
||||
try { |
||||
Response<String> response = call.execute(); |
||||
onResponseGetConfig(response); |
||||
} catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
return lines; |
||||
} |
||||
|
||||
private Map<String, String> parseDecodedFile(String config) { |
||||
String[] lines = config.split(Pattern.quote("\n")); |
||||
|
||||
LinkedHashMap<String, String> 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<String> response) { |
||||
try { |
||||
FileInputStream is = new FileInputStream(file); |
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is)); |
||||
|
||||
LinkedHashMap<String, String> 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<String, String> 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(); |
||||
} |
||||
|
||||
} |
||||
|
Loading…
Reference in new issue