diff --git a/bVNC/build.gradle b/bVNC/build.gradle index 97532c448..dd0d1d946 100644 --- a/bVNC/build.gradle +++ b/bVNC/build.gradle @@ -44,6 +44,8 @@ dependencies { implementation group: 'androidx.legacy', name: 'legacy-support-v4', version: '1.0.0' implementation group: 'androidx.vectordrawable', name: 'vectordrawable', version: '1.1.0' implementation group: 'androidx.preference', name: 'preference-ktx', version: '1.2.0' + implementation group: 'androidx.preference', name: 'preference-ktx', version: '1.2.0' + implementation group: 'at.favre.lib', name: 'bcrypt', version: '0.10.2' implementation 'net.zetetic:android-database-sqlcipher:4.5.1@aar' implementation "androidx.sqlite:sqlite-ktx:2.2.0" implementation group: 'org.yaml', name: 'snakeyaml', version: '1.23' diff --git a/bVNC/src/main/java/com/iiordanov/bVNC/Constants.java b/bVNC/src/main/java/com/iiordanov/bVNC/Constants.java index 653f43cb6..1fd85e439 100644 --- a/bVNC/src/main/java/com/iiordanov/bVNC/Constants.java +++ b/bVNC/src/main/java/com/iiordanov/bVNC/Constants.java @@ -210,8 +210,10 @@ public class Constants { public static final String preferSendingUnicode = "preferSendingUnicode"; public static final String doNotShowDesktopThumbnails = "doNotShowDesktopThumbnails"; public static final String showOnlyConnectionNicknames = "showOnlyConnectionNicknames"; - public static final String softwareKeyboardType = "softwareKeyboardType"; + public static final String masterPassword = "masterPassword"; + public static final String onlyLockConnectionEditing = "onlyLockConnectionEditing"; + public static final int HASH_COST = 6; public static final String ACTION_USB_PERMISSION = "com.iiordanov.aSPICE.USB_PERMISSION"; public static final int usbDeviceTimeout = 5000; @@ -220,7 +222,6 @@ public class Constants { public static final int REMOTE_SOUND_DISABLED = 2; public static final int REMOTE_SOUND_ON_SERVER = 1; public static final int REMOTE_SOUND_ON_DEVICE = 0; - public static final int SHORT_VIBRATION = 1; diff --git a/bVNC/src/main/java/com/iiordanov/bVNC/RemoteCanvas.java b/bVNC/src/main/java/com/iiordanov/bVNC/RemoteCanvas.java index a7cdad2b3..4ee99529f 100644 --- a/bVNC/src/main/java/com/iiordanov/bVNC/RemoteCanvas.java +++ b/bVNC/src/main/java/com/iiordanov/bVNC/RemoteCanvas.java @@ -1105,6 +1105,15 @@ public void run() { }); } + public void showFatalMessageAndQuitTimer(final String error) { + closeConnection(); + handler.post(new Runnable() { + public void run() { + Utils.showFatalErrorMessageTimer(getContext(), error); + } + }); + } + /** * If necessary, initializes an SSH tunnel and returns local forwarded port, or @@ -1970,7 +1979,7 @@ public String retrievevvFileName() { } @Override - public void onTextObtained(String dialogId, String[] obtainedString, boolean dialogCancelled, boolean save) { + public void onTextObtained(String dialogId, String[] obtainedString, boolean dialogCancelled, boolean save, boolean obtainedBooleans[]) { if (dialogCancelled) { handler.sendEmptyMessage(RemoteClientLibConstants.DISCONNECT_NO_MESSAGE); return; diff --git a/bVNC/src/main/java/com/iiordanov/bVNC/SSHConnection.java b/bVNC/src/main/java/com/iiordanov/bVNC/SSHConnection.java index ea1ae5aaa..52fa27f95 100644 --- a/bVNC/src/main/java/com/iiordanov/bVNC/SSHConnection.java +++ b/bVNC/src/main/java/com/iiordanov/bVNC/SSHConnection.java @@ -676,7 +676,7 @@ public String[] replyToChallenge(String name, String instruction, } @Override - public void onTextObtained(String dialogId, String[] obtainedStrings, boolean dialogCancelled, boolean keep) { + public void onTextObtained(String dialogId, String[] obtainedStrings, boolean dialogCancelled, boolean keep, boolean[] obtainedBooleans) { if (dialogCancelled) { handler.sendEmptyMessage(RemoteClientLibConstants.DISCONNECT_NO_MESSAGE); return; diff --git a/bVNC/src/main/java/com/iiordanov/bVNC/Utils.java b/bVNC/src/main/java/com/iiordanov/bVNC/Utils.java index fae07f750..4a22c482d 100644 --- a/bVNC/src/main/java/com/iiordanov/bVNC/Utils.java +++ b/bVNC/src/main/java/com/iiordanov/bVNC/Utils.java @@ -31,6 +31,8 @@ import android.content.ContextWrapper; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; +import android.content.DialogInterface.OnShowListener; +import android.widget.Button; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; @@ -39,6 +41,7 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; +import android.os.CountDownTimer; import android.os.Message; import android.text.ClipboardManager; import android.text.Html; @@ -58,6 +61,7 @@ import com.google.android.play.core.review.ReviewManager; import com.google.android.play.core.review.ReviewManagerFactory; import com.undatech.opaque.ConnectionSetupActivity; +import com.undatech.opaque.RemoteClientLibConstants; import com.undatech.remoteClientUi.R; import net.sqlcipher.database.SQLiteDatabase; @@ -79,6 +83,8 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.Locale; public class Utils { private final static String TAG = "Utils"; @@ -137,7 +143,7 @@ public static void showErrorMessage(Context _context, String message) { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } - }); + }, null); } public static void showFatalErrorMessage(final Context _context, String message) { @@ -150,10 +156,50 @@ public void onClick(DialogInterface dialog, int which) { Utils.justFinish(activity); } } + }, null); + } + + public static void showFatalErrorMessageTimer(final Context _context, String message) { + showMessage(_context, _context.getString(R.string.error) + "!", message, android.R.drawable.ic_dialog_alert, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + Activity activity = Utils.getActivity(_context); + if (activity != null) { + Utils.justFinish(activity); + } + } + }, new DialogInterface.OnShowListener() { + private static final int AUTO_DISMISS_MILLIS = RemoteClientLibConstants.ON_SHOW_LISTENER_TIMER; + @Override + public void onShow(final DialogInterface dialog) { + final Button defaultButton = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE); + final CharSequence negativeButtonText = defaultButton.getText(); + new CountDownTimer(AUTO_DISMISS_MILLIS, RemoteClientLibConstants.ON_SHOW_LISTENER_CHECK_INTERVAL) { + @Override + public void onTick(long millisUntilFinished) { + defaultButton.setText(String.format( + Locale.getDefault(), "%s (%d)", + negativeButtonText, + TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) + 1 //add one so it never displays zero + )); + } + @Override + public void onFinish() { + if (((AlertDialog) dialog).isShowing()) { + dialog.dismiss(); + Activity activity = Utils.getActivity(_context); + if (activity != null) { + Utils.justFinish(activity); + } + } + } + }.start(); + } }); } - public static void showMessage(Context _context, String title, String message, int icon, DialogInterface.OnClickListener ackHandler) { + public static void showMessage(Context _context, String title, String message, int icon, DialogInterface.OnClickListener ackHandler, DialogInterface.OnShowListener showHandler) { try { if (alertDialog != null && alertDialog.isShowing() && !isContextActivityThatIsFinishing(_context)) { alertDialog.dismiss(); @@ -166,6 +212,9 @@ public static void showMessage(Context _context, String title, String message, i builder.setIcon(icon); if (!(alertDialog != null && alertDialog.isShowing()) && !isContextActivityThatIsFinishing(_context)) { alertDialog = builder.create(); + if ( showHandler != null ) { + alertDialog.setOnShowListener(showHandler); + } alertDialog.show(); } } catch (IllegalArgumentException e) { diff --git a/bVNC/src/main/java/com/iiordanov/bVNC/dialogs/GetTextFragment.java b/bVNC/src/main/java/com/iiordanov/bVNC/dialogs/GetTextFragment.java index acdaf2643..c2795f9c2 100644 --- a/bVNC/src/main/java/com/iiordanov/bVNC/dialogs/GetTextFragment.java +++ b/bVNC/src/main/java/com/iiordanov/bVNC/dialogs/GetTextFragment.java @@ -34,6 +34,7 @@ import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; +import android.widget.Switch; import android.widget.TextView; import androidx.fragment.app.DialogFragment; @@ -68,7 +69,7 @@ public class GetTextFragment extends DialogFragment { public static final String DIALOG_ID_GET_OPAQUE_OTP_CODE = "DIALOG_ID_GET_OPAQUE_OTP_CODE"; public interface OnFragmentDismissedListener { - void onTextObtained(String dialogId, String[] obtainedStrings, boolean dialogCancelled, boolean save); + void onTextObtained(String dialogId, String[] obtainedStrings, boolean dialogCancelled, boolean save, boolean[] obtainedBools); } private class TextMatcher implements TextWatcher { @@ -90,6 +91,7 @@ public void afterTextChanged(Editable arg0) {} private EditText textBox; private EditText textBox2; private EditText textBox3; + private Switch switch1; private Button buttonConfirm; private Button buttonCancel; private CheckBox checkboxKeepPassword; @@ -100,7 +102,7 @@ public void afterTextChanged(Editable arg0) {} private String t2; private String t3; private boolean keepPassword; - + private boolean sw1; private int dialogType = 0; private int messageNum = 0; private int errorNum = 0; @@ -111,7 +113,8 @@ public GetTextFragment () { public static GetTextFragment newInstance(String dialogId, String title, OnFragmentDismissedListener dismissalListener, int dialogType, int messageNum, int errorNum, - String t1, String t2, String t3, boolean keepPassword) { + String t1, String t2, String t3, boolean keepPassword, + boolean sw1) { android.util.Log.i(TAG, "newInstance called"); GetTextFragment f = new GetTextFragment(); f.setDismissalListener(dismissalListener); @@ -126,6 +129,7 @@ public static GetTextFragment newInstance(String dialogId, String title, args.putString("t2", t2); args.putString("t3", t3); args.putBoolean("keepPassword", keepPassword); + args.putBoolean("sw1", sw1); f.setArguments(args); f.setRetainInstance(false); @@ -145,6 +149,7 @@ public void onCreate(Bundle savedInstanceState) { t2 = getArguments().getString("t2"); t3 = getArguments().getString("t3"); keepPassword = getArguments().getBoolean("keepPassword"); + sw1 = getArguments().getBoolean("sw1"); } @Override @@ -192,12 +197,15 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa error = (TextView) v.findViewById(R.id.error); textBox = (EditText) v.findViewById(R.id.textBox); textBox2 = (EditText) v.findViewById(R.id.textBox2); + switch1 = (Switch) v.findViewById(R.id.switch1); + switch1.setVisibility(View.VISIBLE); + switch1.setText(R.string.master_password_only_lock_connection_editing); hideText(textBox); hideText(textBox2); buttonConfirm = (Button) v.findViewById(R.id.buttonConfirm); buttonCancel = (Button) v.findViewById(R.id.buttonCancel); dismissOnCancel(buttonCancel); - ensureMatchingDismissOnConfirm (buttonConfirm, textBox, textBox2, error); + ensureMatchingDismissOnConfirm (buttonConfirm, textBox, textBox2, switch1, error); break; case CredentialsWithDomain: v = inflater.inflate(R.layout.get_credentials_with_domain, container, false); @@ -252,10 +260,10 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa textBox2.setText(t2); if (textBox3 != null && t3 != null) textBox3.setText(t3); - - if (checkboxKeepPassword != null) { + if (checkboxKeepPassword != null) checkboxKeepPassword.setChecked(keepPassword); - } + if (switch1 != null) + switch1.setChecked(sw1); message = (TextView) v.findViewById(R.id.message); message.setText(messageNum); @@ -293,7 +301,7 @@ public void onClick(View v) { }); } - private void ensureMatchingDismissOnConfirm (Button buttonConfirm, final EditText textBox1, final EditText textBox2, final TextView error) { + private void ensureMatchingDismissOnConfirm (Button buttonConfirm, final EditText textBox1, final EditText textBox2, final Switch switch1, final TextView error) { buttonConfirm.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { @@ -316,6 +324,7 @@ public void onClick(View v) { public void onDismiss (DialogInterface dialog) { android.util.Log.i(TAG, "onDismiss called: Sending data back to Activity"); String[] results = new String[3]; + boolean[] b_results = new boolean[3]; if (textViewBox != null) { results[0] = textViewBox.getText().toString(); textViewBox.setText(""); @@ -335,11 +344,15 @@ public void onDismiss (DialogInterface dialog) { if (checkboxKeepPassword != null) { keepPassword = checkboxKeepPassword.isChecked(); } + if (switch1 != null) { + b_results[0] = switch1.isChecked(); + switch1.setChecked(false); + } if (dismissalListener != null && (wasConfirmed||wasCancelled)) { boolean cancelled = wasCancelled; wasCancelled = false; wasConfirmed = false; - dismissalListener.onTextObtained(dialogId, results, cancelled, keepPassword); + dismissalListener.onTextObtained(dialogId, results, cancelled, keepPassword, b_results); } super.onDismiss(dialog); } diff --git a/bVNC/src/main/java/com/iiordanov/bVNC/input/RemoteCanvasHandler.java b/bVNC/src/main/java/com/iiordanov/bVNC/input/RemoteCanvasHandler.java index 66f5c5438..865d2f009 100644 --- a/bVNC/src/main/java/com/iiordanov/bVNC/input/RemoteCanvasHandler.java +++ b/bVNC/src/main/java/com/iiordanov/bVNC/input/RemoteCanvasHandler.java @@ -111,7 +111,7 @@ private void showGetTextFragment(String tag, String dialogId, String title, c.pd.dismiss(); } GetTextFragment frag = GetTextFragment.newInstance(dialogId, title, dismissalListener, - dialogType, messageNum, errorNum, t1, t2, t3, keep); + dialogType, messageNum, errorNum, t1, t2, t3, keep, false); frag.setCancelable(false); frag.show(fm, tag); } @@ -124,7 +124,7 @@ private void showGetTextFragmentRemoteCanvas(String tag, String dialogId, String c.pd.dismiss(); } GetTextFragment frag = GetTextFragment.newInstance(dialogId, title, dismissalListener, - dialogType, messageNum, errorNum, t1, t2, t3, keep); + dialogType, messageNum, errorNum, t1, t2, t3, keep, false); frag.setCancelable(false); frag.show(fm, tag); } @@ -452,7 +452,7 @@ public void handleMessage(final Message msg) { break; case RemoteClientLibConstants.RDP_CONNECT_FAILURE: if (c.maintainConnection) { - c.showFatalMessageAndQuit(context.getString(R.string.error_rdp_connection_failed)); + c.showFatalMessageAndQuitTimer(context.getString(R.string.error_rdp_connection_failed)); } break; case RemoteClientLibConstants.RDP_UNABLE_TO_CONNECT: diff --git a/bVNC/src/main/java/com/undatech/opaque/ConnectionGridActivity.java b/bVNC/src/main/java/com/undatech/opaque/ConnectionGridActivity.java index 3bdaa1252..51469ab86 100644 --- a/bVNC/src/main/java/com/undatech/opaque/ConnectionGridActivity.java +++ b/bVNC/src/main/java/com/undatech/opaque/ConnectionGridActivity.java @@ -72,6 +72,8 @@ import com.undatech.opaque.util.GeneralUtils; import com.undatech.opaque.util.LogcatReader; import com.undatech.remoteClientUi.R; +import at.favre.lib.crypto.bcrypt.BCrypt; +import at.favre.lib.crypto.bcrypt.LongPasswordStrategies; import java.io.File; import java.io.InputStream; @@ -91,6 +93,8 @@ public class ConnectionGridActivity extends FragmentActivity implements GetTextF protected boolean isStarting = true; private AppCompatImageButton addNewConnection = null; + private AlertDialog activeAlertDialog = null; + private RateOrShareFragment rateOrShareFragment = new RateOrShareFragment(); @Override @@ -126,6 +130,10 @@ else if (cs[item].toString() == getString(R.string.connection_delete)) { } }); AlertDialog alertDialog = alertDialogBuilder.create(); + if(Utils.querySharedPreferenceBoolean(appContext, Constants.onlyLockConnectionEditing)) { + activeAlertDialog = alertDialog; + showGetTextFragment(getPassword); + } alertDialog.show(); return true; } @@ -147,13 +155,15 @@ public void afterTextChanged(Editable s) { getPassword = GetTextFragment.newInstance(GetTextFragment.DIALOG_ID_GET_MASTER_PASSWORD, getString(R.string.master_password_verify), this, GetTextFragment.PasswordNoKeep, R.string.master_password_verify_message, - R.string.master_password_set_error, null, null, null, false); + R.string.master_password_set_error, null, null, null, false, + false); } if (getNewPassword == null) { getNewPassword = GetTextFragment.newInstance(GetTextFragment.DIALOG_ID_GET_MATCHING_MASTER_PASSWORDS, getString(R.string.master_password_set), this, GetTextFragment.MatchingPasswordTwice, R.string.master_password_set_message, - R.string.master_password_set_error, null, null, null, false); + R.string.master_password_set_error, null, null, null, false, + false); } FileUtils.logFilesInPrivateStorage(this); FileUtils.deletePrivateFileIfExisting(this, ".config/freerdp/licenses"); @@ -446,7 +456,7 @@ public boolean onMenuOpened(int featureId, Menu menu) { try { updateInputMenu(menu.findItem(R.id.itemInputMode).getSubMenu()); MenuItem itemMasterPassword = menu.findItem(R.id.itemMasterPassword); - itemMasterPassword.setChecked(Utils.querySharedPreferenceBoolean(this, Constants.masterPasswordEnabledTag)); + itemMasterPassword.setChecked(Utils.querySharedPreferenceBoolean(this, Constants.masterPasswordEnabledTag) || Utils.querySharedPreferenceBoolean(this, Constants.onlyLockConnectionEditing)); } catch (NullPointerException e) {} return true; } @@ -500,7 +510,7 @@ public boolean onOptionsItemSelected(MenuItem item) { IntroTextDialog.showIntroTextIfNecessary(this, database, true); } else { togglingMasterPassword = true; - if (Utils.querySharedPreferenceBoolean(this, Constants.masterPasswordEnabledTag)) { + if (Utils.querySharedPreferenceBoolean(this, Constants.masterPasswordEnabledTag) || !((Utils.querySharedPreferenceString(this, Constants.masterPassword, "")).equals("")) && Utils.querySharedPreferenceBoolean(this, Constants.onlyLockConnectionEditing)) { showGetTextFragment(getPassword); } else { showGetTextFragment(getNewPassword); @@ -514,13 +524,52 @@ public boolean onOptionsItemSelected(MenuItem item) { return true; } - public void onTextObtained(String dialogId, String[] obtainedStrings, boolean wasCancelled, boolean keep) { + public void onTextObtained(String dialogId, String[] obtainedStrings, boolean wasCancelled, boolean keep, boolean[] obtainedBooleans) { Log.i(TAG, "onTextObtained"); - handlePassword(obtainedStrings[0], wasCancelled); + if (dialogId == GetTextFragment.DIALOG_ID_GET_MATCHING_MASTER_PASSWORDS && !wasCancelled) { + Utils.setSharedPreferenceBoolean(this, Constants.onlyLockConnectionEditing, obtainedBooleans[0]); + } + handlePassword(obtainedStrings[0], wasCancelled, dialogId, + Utils.querySharedPreferenceBoolean(this, Constants.onlyLockConnectionEditing) + ); } - public void handlePassword(String providedPassword, boolean dialogWasCancelled) { + public void handlePassword(String providedPassword, boolean dialogWasCancelled, String dialogId, boolean onlyLockEditingConnections) { Log.i(TAG, "handlePassword"); + boolean loadConnections = onlyLockEditingConnections ? handlePasswordOnlyLockEditingConnections(providedPassword, dialogWasCancelled, dialogId) : handlePasswordDatabaseLock(providedPassword, dialogWasCancelled); + if (loadConnections) { + removeGetPasswordFragments(); + loadSavedConnections(); + } + } + + private boolean handlePasswordOnlyLockEditingConnections(String providedPassword, boolean dialogWasCancelled, String dialogId) { + boolean loadConnections; + if (dialogId == GetTextFragment.DIALOG_ID_GET_MATCHING_MASTER_PASSWORDS) { + Utils.setSharedPreferenceString(this, Constants.masterPassword, + BCrypt.with(LongPasswordStrategies.hashSha512(BCrypt.Version.VERSION_2A)).hashToString(Constants.HASH_COST, providedPassword.toCharArray())); + loadConnections = true; + } + else { + BCrypt.Result result = BCrypt.verifyer(BCrypt.Version.VERSION_2A, LongPasswordStrategies.hashSha512(BCrypt.Version.VERSION_2A)).verify(providedPassword.toCharArray(), Utils.querySharedPreferenceString(this, Constants.masterPassword, "")); + loadConnections = result.verified; + if(!loadConnections || dialogWasCancelled) { + String message = dialogWasCancelled ? this.getResources().getString(R.string.master_password_error_password_necessary) : this.getResources().getString(R.string.master_password_error_wrong_password); + Utils.showErrorMessage(this,message); + if (activeAlertDialog != null) { + activeAlertDialog.dismiss(); + activeAlertDialog = null; + } + } else if (togglingMasterPassword) { + Utils.setSharedPreferenceBoolean(this, Constants.onlyLockConnectionEditing, false); + Utils.setSharedPreferenceString(this, Constants.masterPassword, null); + togglingMasterPassword = false; + } + } + return loadConnections; + } + + private boolean handlePasswordDatabaseLock(String providedPassword, boolean dialogWasCancelled) { boolean loadConnections; MasterPasswordDelegate passwordDelegate = new MasterPasswordDelegate(this, database); if (togglingMasterPassword) { @@ -529,10 +578,7 @@ public void handlePassword(String providedPassword, boolean dialogWasCancelled) } else { loadConnections = passwordDelegate.checkMasterPasswordAndQuitIfWrong(providedPassword, dialogWasCancelled); } - if (loadConnections) { - removeGetPasswordFragments(); - loadSavedConnections(); - } + return loadConnections; } private void showGetTextFragment(GetTextFragment f) { diff --git a/bVNC/src/main/res/layout/get_text_twice.xml b/bVNC/src/main/res/layout/get_text_twice.xml index 28ad66f92..cc794c8cc 100644 --- a/bVNC/src/main/res/layout/get_text_twice.xml +++ b/bVNC/src/main/res/layout/get_text_twice.xml @@ -33,6 +33,13 @@ android:hint="@string/master_password_hint_twice" android:inputType="textNoSuggestions|textVisiblePassword"> + Please enter and memorize a Master Password. The longer your password, the harder it is to crack. If you set your password wrong or forget it, you will lose all your settings! You may want to export your settings now to back them up (from the Menu). Store them safely as exported settings are not encrypted. Verify Master Password Please enter correct Master Password to continue. + Only lock connection editing Send Special Keys diff --git a/remoteClientLib/jni/libs/build-deps.sh b/remoteClientLib/jni/libs/build-deps.sh index d49a12e04..d9d0933db 100755 --- a/remoteClientLib/jni/libs/build-deps.sh +++ b/remoteClientLib/jni/libs/build-deps.sh @@ -497,7 +497,7 @@ build() { fi echo "Copying local recipes into cerbero" - git clone https://github.com/iiordanov/remote-desktop-clients-cerbero-recipes.git recipes || true + git clone https://github.com/iiordanov/remote-desktop-clients-cerbero-recipes.git -b bugfix/spice-usb-location recipes || true pushd recipes git pull popd diff --git a/remoteClientLib/src/main/java/com/undatech/opaque/RemoteClientLibConstants.java b/remoteClientLib/src/main/java/com/undatech/opaque/RemoteClientLibConstants.java index fd88328f2..8906c4ea5 100644 --- a/remoteClientLib/src/main/java/com/undatech/opaque/RemoteClientLibConstants.java +++ b/remoteClientLib/src/main/java/com/undatech/opaque/RemoteClientLibConstants.java @@ -100,4 +100,7 @@ public class RemoteClientLibConstants { public static final String GET_OTP_CODE_ID = "getOtpCode"; public static final String GET_PASSWORD_ID = "getPassword"; public static final int LOGCAT_MAX_LINES = 500; + + public static final int ON_SHOW_LISTENER_TIMER = 5000; + public static final int ON_SHOW_LISTENER_CHECK_INTERVAL = 100; }