I have a PreferenceScreen following the docs here and here.
One of the options is a SwitchPreferenceCompat that does some risky work in my app, for example, to send sensitive information periodically. This is the simple switch:
<SwitchPreferenceCompat
app:key="SendInfoToServer"
app:title="Send information on your device to our servers" />
What I need, is to ask the user with a question like this:
This will periodically send sensitive information of
your device to our servers.
Are you sure you want to enable this option?
If the user answer YES then I want the SwitchPreferenceCompat to get activated, otherwise not.
I have investigated a bit a I found this code:
findPreference("SendInfoToServer").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if((boolean) newValue == true){
Toast("Activating this function will safe sensitive information");
}
return true;
}
});
But this message is just a warning message being displayed to the user. What I am trying to achieve is to prompt a question. If the user agrees then the switch should be turned ON, if the user doesn't agree then the switch should remain OFF.
Any ideas on how to do that.
You can modify it using the isChecked method. Here is an example doing that with a popup dialog like you described. This uses the onPreferenceChangeListener, and if the option is being disabled it shows a dialog and returns false for the listener (so the preference not immediately updated).
If the user selects the right option in the dialog then it gets disabled using the isChecked method. The dialog would not be shown if the option is being re-enabled, only when it gets disabled.
Some of the logic might be reversed if you wanted to show the dialog when the option is enabled, but the concept overall is the same. The key is to return false when showing the dialog so it doesn't change, then change it later with isChecked if the user confirms they want to change it.
// replace this with getting the switch if you defined it in the XML
val enableChecks = SwitchPreference(context)
enableChecks.key = Constants.ENABLE_CHECKS
enableChecks.title = getString(R.string.enable_checks)
enableChecks.isPersistent = true
enableChecks.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _, value ->
val disabling = !(value as Boolean)
if( disabling ) {
SimpleDialogs.twoButton(
context,
title = getString(R.string.confirm_disable_check),
message = getString(R.string.confirm_disable_check_msg),
posButton = getString(R.string.disable),
negButton = getString(R.string.cancel),
onPos = {
// user confirmed they want to disable it, set to false
enableChecks.isChecked = false
},
onNeg = {
// user changed their mind, keep it true
enableChecks.isChecked = true
},
icon = SimpleDialogs.ICON_WARNING
)
// return false so the change isn't applied here if showing a dialog
false
}
else {
true
}
}
appearanceCat.addPreference(enableChecks)
Similar answer in Java based on Tyler V answer:
SwitchPreferenceCompat oSwitchPreferenceCompat = findPreference("MyKey");
oSwitchPreferenceCompat.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if((boolean) newValue == false){
return true; //If the check is disabled we do not need to ask the user. In this case we allow the normal flow and let the system disable it. Returning "true" allows the System to disable it.
}
//Here the user is enabling the check, so we prompt to confirm it:
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setMessage("Are you sure you want to enable this?");
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
oSwitchPreferenceCompat.setChecked(true);
}
});
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
oSwitchPreferenceCompat.setChecked(false);
}
});
builder.setCancelable(false);
Dialog d = builder.create();
d.show();
return false;//returning false we are telling the System to not modify the check yet.
}
});
Related
I'm trying to implement a check box in a MaterialDialog using this library, and the check box asks the user if they don't want to see that dialog again. The dialog appears if the user's phone has NFC, but it is deactivated.
If the user presses the positive button in the dialog and has the box ticked, then it accesses a Realm object with a Boolean attribute named "NfcStatus", and sets that to true. If they press the negative button with the box ticked, then that Realm object's NfcStatus is set to false.
Here's the code of the MaterialDialog:
new MaterialDialog.Builder(context)
.title("NFC")
.content("NFC is disabled. Would you like to activate it?")
.items(R.array.checkbox) //this just has one string in it which says "Please don't show me this again"
.itemsCallbackMultiChoice(null, new MaterialDialog.ListCallbackMultiChoice() {
#Override
public boolean onSelection(MaterialDialog dialog, Integer[] which, CharSequence[] text) {
/**
* If you use alwaysCallMultiChoiceCallback(), which is discussed below,
* returning false here won't allow the newly selected check box to actually be selected.
* See the limited multi choice dialog example in the sample project for details.
**/
checkboxIsChecked = true; //TODO: checkboxIsChecked isn't being passed into onPositive or onNegative
return true;
}
})
.positiveText(R.string.accept)
.positiveColorRes(R.color.main_theme_color)
.negativeText(R.string.decline)
.negativeColorRes(R.color.main_theme_color)
.callback(new MaterialDialog.ButtonCallback() {
#Override
public void onPositive(MaterialDialog dialog) {
//this was how I was checking if checkboxIsChecked was true or false
Log.d("checkboxIsChecked", checkboxIsChecked?"true":"false"); }
if (checkboxIsChecked) {begins
if (ks.contains(KEY_NAME)) {
realmKey = ks.get(KEY_NAME);
}
realm = Realm.getInstance(context, realmKey);
RealmPhone realmPhone = realm.where(RealmPhone.class).findFirst(); realmPhone.setNfcStatus(true);
}
activity.finish();
startNfcSettingsActivity();
Toast.makeText(context, R.string.nfc_disabled_message, Toast.LENGTH_LONG).show();
}
#Override
public void onNegative(MaterialDialog dialog) {
if (checkboxIsChecked) {
if (ks.contains(KEY_NAME)) {
realmKey = ks.get(KEY_NAME);
}
realm = Realm.getInstance(context, realmKey);
RealmPhone realmPhone = realm.where(RealmPhone.class).findFirst();
realmPhone.setNfcStatus(false);
}
}
})
.cancelable(false)
.show();
The problem was that even if the check box was ticked, the checkboxIsChecked variable was still false when using it in onPositive or onNegative, so it was never being written to the Realm object. Am I doing this the wrong way?
For changing and saving the RealmObject, you need to use transactions. Related documents can be found here.
In you case, it would be something like:
realm = Realm.getInstance(context, realmKey);
// I'm not quite sure how did you create the realmPhone at the first time,
// just assume you have one realmPhone in the Realm.
RealmPhone realmPhone = realm.where(RealmPhone.class).findFirst();
realm.beginTransaction();
realmPhone.setNfcStatus(false);
realm.commitTransaction();
// Close the realm instance after using it is very important! To avoid leaks.
realm.close();
BTW, it seems code:
RealmPhone realmPhone = realm.where(RealmPhone.class).findFirst();
realmPhone.setNfcStatus(false);
is not called. If it does, a IllegalStateException should be thrown since you didn't call it in a Realm transaction. Or maybe RealmPhone is not inherited from RealmObject?
In my app i have a function that checks the entered text from a displayed AlertDialog with an input text. If the text is equal to a string variable, return True, else return False, and catch this resulting value to continue conditional code.
But it seems its a little difficult to do this as i've read in other posts asking how to solve the same problem.
I've already done this:
private boolean checkAdministratorPassword() {
final enterPasswordResult[0] = false;
AlertDialog.Builder alert = new AlertDialog.Builder(mContext);
alert.setTitle("Confirm action");
alert.setIcon(R.drawable.ic_launcher);
alert.setMessage("Enter administrator pass to continue");
final EditText input = new EditText(mContext);
input.setPadding(5, 0, 5, 0);
alert.setView(input);
alert.setPositiveButton("Accept", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
String strPass = input.getEditableText().toString();
if (strPass.length() == 0) {
dialog.cancel();
}
if (strPass.equalsIgnoreCase(Constantes.ADMIN_PASS)) {
enterPasswordResult[0] = true;
dialog.cancel();
} else {
Toast.makeText(mContext, "Invalid pass..!!", Toast.LENGTH_LONG).show();
dialog.cancel();
}
}
});
alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.cancel();
}
});
AlertDialog alertDialog = alert.create();
alertDialog.show();
return enterPasswordResult[0];
}
And i call the function this way:
If ( checkAdministratorPassword() == True ){
//true conditions
}
But the problem is that the check function doesnt wait for the result to continue with the code, it just continue by itself and i dont get the appropiate behavior.
The issue is you're trying to handle an async event in the logcal flow of your program. You can do this if you make the Dialog it's own class and use an Interface to callback to your host activity. Check out the documentation on DialogFragment.
public interface PasswordCheckListener{
public void valid(boolean check);
}
private static class PasswordDialog extends DialogFragment {
private PasswordCheckListener listener;
public static PaswordDialog newInstance(PasswordCheckListener listener){
this.listener = listener;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
//Put your dialog creation code here
}
private checkAdminPassword(){
//Whatever your check passowrd code is
listener.valid(result);
}
}
I realize I didn't implement all the code for you but that's the general idea. By using an interface you can call back to your host Activity or Fragment when the user enters the password and presses submit. You can then handle the event as it happens, rather than having to deal with it in your program flow.
Thank you all for your answers!! i've found the right way to achieve this problem by creating an Activity whith theme "Theme.Dialog", an input text and two buttons (Accept, Cancel), i start this activity for result asking the user to enter the administrator pass to continue, checking the string and then returning again to onActivityResult() from previous activity with the correct information to proceed.
We have to show a license agreement dialog when user use the application at the first time, now I have two questions about this:
1 Where to put this dialog?
Add another activity or put the dialog just at the MainActivity which is the launch acitivty?
2 How to close the app if user hit "Reject"
Once user hit the "Reject" button which means that he/she does not agree our license, then we have to exit the application completely. How to make it?
According to the answer of "Ahmad", I will decide to open a dialog or not at the beginning of the activity(the onCreate method):
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.map);
this.setupLicense();
this.setupViews();
this.initSomeJob();
}
private void setupLicense() {
SharedPreferences setting = getSharedPreferences(IConstant.Map_License, 0);
boolean mapLicenseAccept = setting.getBoolean(IConstant.Map_License, false);
if (!mapLicenseAccept) {
//user does not accept the license yet, we will open the dialog
showDialog(Dialog_Map_License);
}
}
#Override
protected Dialog onCreateDialog(int id) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
switch (id) {
case Dialog_Map_License:
builder.setIconAttribute(android.R.attr.alertDialogIcon)
.setTitle(R.string.map_license_title)
.setMessage(R.string.map_license_content)
.setPositiveButton(R.string.map_license_accept, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
//License accepted, persisted it.
SharedPreferences settings = getSharedPreferences(IConstant.Map_License, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean(IConstant.Map_License, true);
editor.commit();
}
})
.setNegativeButton(R.string.map_license_reject, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
//do nothing and exit
Process.killProcess(Process.myPid());
System.exit(0);
}
});
Dialog target = builder.create();
target.setCanceledOnTouchOutside(false);
return target;
}
return null;
}
But now I have meet two problem:
1 Event I choose the "Accept" button, once I open my app the second time, the dialog will show.
It seems that the following code does not work:
editor.putBoolean(IConstant.Map_License, true);
editor.commit();
2 When I show the dialog, the code:
this.setupViews();
this.initSomeJob();
will still run , they are not blocked which is out of expected, since nothing should be done before user hit the "Accept" button.
Any idea to fix it?
onCreateDialog has been deprecated. Use dialog fragment instead. The advantage will be that the code for displaying dialog will be moved from activity and you can then display dialog from any activity. Also move
SharedPreferences setting = getSharedPreferences(IConstant.Map_License, 0);
boolean mapLicenseAccept = setting.getBoolean(IConstant.Map_License, false);
to a utility method like isLicenceAccepted and similarly for storing the data
SharedPreferences settings = getSharedPreferences(IConstant.Map_License, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean(IConstant.Map_License, true);
editor.commit();
to method like acceptLicence in utility.
You can find how to make communication between dialog Fragment and your activity here. In your interface instead of onArticleSelected you will have to implement two methods onLicence accepted and onLicenceRejected. Implement the interface in you activity override these two methods and take appropriate action.
Where to put this dialog?
Right at the beginning, when the user opens the app for the first time. Keep track of that by saving it in your shared preferences, if this dialog has been already shown or not. You don't have to create a separate activity for this. You could, but most apps I've seen out there don't.
How to close the app if user hit "Reject"
Just finish the Activity and also save that in your shared preferences as well. So every time the user opens your app you check weather the boolean value for "hasUserAcceptedOurAgreement" is true or not and proceed depending on that.
I'm only answering from a technical standpoint on how this could be done reliably. I'm not a lawyer, but as far as I know, it's perfectly valid to just submit your license agreement to the play store, so it's available from the applications application page (Why else would there be this option?).
I am trying to have a disclaimer pop up when the app is first run, and after each update. I did a bunch of googling and reviewed some questions here as well, and this is what my code looks like:
SharedPreferences pref = getSharedPreferences("Preferences", MODE_PRIVATE);
SharedPreferences.Editor edit = pref.edit();
String lver = pref.getString("Version", "");
String ver = this.getString(R.string.version);
if(ver != lver)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Disclaimer")
.setMessage(this.getString(R.string.disclaimer))
.setCancelable(false)
.setPositiveButton("Accept", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
accepted = true;
dialog.cancel();
}
})
.setNegativeButton("Decline", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
MainMenu.this.finish();
}
});
AlertDialog disc = builder.create();
disc.show();
if(accepted == true)
{
edit.putString("Version", this.getString(R.string.version));
edit.commit();
}
}
This code actually worked at first, but when I changed my apps starting activity, and moved this code from the original starting activity to the new one, it no longer works. The disclaimer appears on every run.
I want the popup only to show on first run and after updates. What do I need to do to achieve this? I am very frustrated and confused by the fact that it was working, then wasnt.
Comparing Strings with .equals() is the correct way (see: How do I compare strings in Java? for a good explanation) , although because I'm not sure how the android internals work and you said it worked before, that isn't your problem. Your problem is that your check
if (accepted == true) {/** code */}
isn't run on the on click listener. Because it isn't, that thread (I'm assuming it spawns a new thread to show the dialog) keeps running.
I'm also assuming before you moved this code, you had declared a
boolean accepted = true; //or initialized it to true somewhere
But when you moved it you didn't reinitialize it. Now, because the default value of a primitive is false, in your new code it gets to the check before you press a dialog button, and never commit the new version.
My advice would be put what's in the
accepted == true
block simply into your listener for the positive button click.
I am working on an android project where I am trying to show a AlertDialog in a separate normal java class and return the result that the user enters. I can display the dialog fine but the problem I am having is it always returns the value before the dialog has had one of the buttons pressed.
Below is the code that calls the function in the standard java class to show the dialog
private void showDiagreeError()
{
Common common = new Common(this);
boolean dialogResult = common.showYesNoDialog();
Toast.makeText(getApplicationContext(), "Result: " + dialogResult, Toast.LENGTH_LONG).show();
}
And below is the code that shows the actual dialogue
public boolean showYesNoDialog()
{
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage("Are you sure you do not want to agree to the terms, if you choose not to, you cannot use Boardies Password Manager")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialogResult = true;
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialogResult = false;
}
});
AlertDialog alert = builder.create();
alert.show();
return dialogResult;
}
dialogResult is a global variable visible throughout the class and being set to false. As soon as the dialog is shown the toast message is shown showing the result is false, but I was expecting the return statement to block until the user has pressed one of the buttons too set the variable to the correct value.
How can I get this to work.
After many hours hunting through the inner depths of google pages, I found this Dialogs / AlertDialogs: How to "block execution" while dialog is up (.NET-style).
It does exactly the job I was after and tested to make sure there are no ANR errors, which there isn't