In short, I'm working on a "DurationPickerDialog" that works similarly to how the DatePickerDialog works but works based on the xsd:duration type, so the user specifies the number of years, months, days, etc.
I've also implemented a "fuzzy duration" function that gives me durations as a string like "one month ago". I would really like to be able to update the DurationPickerDialog's title in the same manner that the DatePickerDialog's title is updated, but there seems to be a problem. In the DatePickerDialog, they have it set to be a single line all the time, so that it doesn't get "jumpy." Here's how Android's source does the DatePickerDialog's title.
// Note: before the skim-readers look at this bit, realize that this is NOT my
// code but Android's internal code for the DatePickerDialog.
#Override
public void show() {
super.show();
/* Sometimes the full month is displayed causing the title
* to be very long, in those cases ensure it doesn't wrap to
* 2 lines (as that looks jumpy) and ensure we ellipsize the end.
*/
TextView title = (TextView) findViewById(R.id.alertTitle);
title.setSingleLine();
title.setEllipsize(TruncateAt.END);
}
Unfortunately, I cannot access their R.id.alertTitle, because it is part of com.android.internal.R.
I have seen implementations like this stackoverflow post where it would have me modify the Window.FEATURE_CUSTOM_TITLE attribute, but that doesn't seem to let me modify the title (easily) after that.
There was also another stackoverflow post that mentioned how to change the title at runtime between two different XML layouts, but that also doesn't seem like it would be all too helpful, since the title should be modified every time the duration changes, and creating an XML layout for each duration is obviously not a good idea.
So, since they "cheated" by accessing a value that us mere mortals don't have access to, is there another way that I could go about doing it?
Edit: And through some black magic, it seems that it now does ellipsize the text like I was wanting? Only earlier it wasn't, and now I can't seem to reproduce the problem. So, I suppose while we're at it, can someone explain to me how I might have accomplished this magic?
I would implement a custom dialog by extending the Dialog class and creating a custom xml layout for it.
You'd need some custom button backgrounds for the plus / minus and top / bottom combos and some button listeners to manipulate the values.
Since you are going for a duration value, you are probably going to need more space than the existing dialog gives you anyway.
This way you can set the title to whatever you like. Let me know if you need a code example.
Example:
The dialog class:
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class DurationDialog extends Dialog {
private Button yearButtonPlus;
private Button yearButtonMinus;
private TextView dialogBody;
private TextView dialogTitle;
private String dialogBodyString;
private String dialogTitleString;
public DurationDialog(final Context context, String dialogBody, String dialogTitle) {
super(context,R.style.CustomDialogTheme);
this.dialogBodyString = dialogBody;
this.dialogTitleString = dialogTitle;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.my_dialog);
yearButtonPlus = (Button) findViewById(R.id.dialog_year_button_plus);
yearButtonMinus = (Button) findViewById(R.id.dialog_year_button_minus);
dialogBody = (TextView) findViewById(R.id.dialog_body);
dialogTitle = (TextView) findViewById(R.id.dialog_title);
dialogBody.setText(dialogBodyString);
dialogTitle.setText(dialogTitleString);
yearButtonPlus.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//do year increment here
}
});
//etc...
}
}
In your activity, you call this.showDialog(DURATION_DIALOG); // DURATION_DIALOG is just an integer specified at the top of your Activity to identify the dialog for the next peice of code, which handles actually creating the dialog:
import android.app.Activity;
import android.app.Dialog;
public class MyActivity extends Activity {
private Dialog dialog;
//Lots of other activity stuff...
protected Dialog onCreateDialog(int id) {
switch (id) {
case DURATION_DIALOG:
dialog = new DurationDialog(Activity.this, "your title", "your body");
dialog.setOnDismissListener(onDismissListener);
break;
default:
dialog = null;
}
return dialog;
}
}
//put this listener in as an inner class of MyActivity:
private DialogInterface.OnDismissListener onDismissListener = new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
DurationDialog dialog = (DurationDialog) dialog;
//grab the duration stuff out of your dialog and do stuff with it....
}
};
Finally, you can set your dialog theme as is done above in your styles.xml file. For example:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="CustomDialogTheme" parent="#android:style/Theme.Dialog">
<item name="android:windowBackground">#drawable/bgnd_transparent</item>
<item name="android:windowIsFloating">true</item>
</style>
</resources>
Good luck!
If you want to modify the dialog title's TextView (change text, style, behaviour...) without using a custom view, just do it that way:
TextView tv = (TextView) dialog.findViewById(android.R.id.title);
tv.setText("New title");
...
Related
First I must say that I'm not good in English and "completely new" to Android Programming.
I want to create an app that can monitor server performance. I have use the navigation drawer as my app interface. Each have a few fragment running with different sets of activity. One of the fragment, I would like to create an activity that can calculate the server performance using some if else statement calculation with a button to submit the results. When I run my app, I have trouble with this fragment (FuzFragment) where my app stopped immediately with an error "Unfortunately, ServerMonitorApp has stopped".
Below, is the fragment class (FuzFragment) that I used to display the layout:
package com.example.servermonitorapp;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
public class FuzFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (container == null) {
return null;
}
LinearLayout mLinearLayout = (LinearLayout) inflater.inflate(R.layout.fragment_fuz,
container, false);
Button sumButton = (Button) mLinearLayout.findViewById(R.id.submitButton);
sumButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EditText cpu = (EditText) v.findViewById(R.id.textCPU);
EditText ram = (EditText) v.findViewById(R.id.textRAM);
TextView res = (TextView) v.findViewById(R.id.txtResult);
int cpuslow = Integer.parseInt(cpu.getText().toString());
int cpusmedium = Integer.parseInt(cpu.getText().toString());
int cpushigh = Integer.parseInt(cpu.getText().toString());
int ramlow = Integer.parseInt(ram.getText().toString());
int rammedium = Integer.parseInt(ram.getText().toString());
int ramhigh = Integer.parseInt(ram.getText().toString());
if (cpuslow > 0 && cpuslow <= 30 | ramlow > 0 && ramlow <= 23) {
res.setText("Safe");
} else if (cpusmedium > 30 && cpusmedium <= 60 | rammedium > 23 && rammedium <= 38) {
res.setText("Risk");
} else if (cpushigh > 60 | ramhigh > 38) {
res.setText("Very Risk");
} else {
res.setText("Invalid Number");
}
}
});
return mLinearLayout;
}
}
Is there any wrong with my code that can cause my app stopped responding? Need help so much on this since I'm still in learning in Android programming.
Welcome to Android the dark side of development haha ;).
Ok let's go through a few basics.
The onCreate method is used to inflate or draw your layout. prior to having your layout drawn (if you do a findViewById) it won't exist.
In order for the onCreate method to draw the picture it needs the setContent method called that is created by default. This should be one of if not the first line of code you call. It ensures everything is available after that line for UI related interactions of an ACTIVITY. Emphasis on activity because the rules change when you get into fragments.
Now, the next issue is you have bloated code. Doing things like.
EditText myText = findViewById(R.id.myText);
int myValue = Integer.parseInt(myText.getText.toString());
etc.. can all be done in the same line and you are not using the reference to myText anywhere else so just do it like:
int myValue = Integer.parseInt(findViewById(R.id.myText).getText.toString());
Keep in mind I am doing Pseudo code. Please don't be that person that replies with " you have an error in your code " haha or I will not help.
Next up, it appears you never did the setContentView method in your onCreate, please put that back and set the content to your activity.xml code that matches the layout that you are inflating.
Next up, you are doing findViewById inside a button click. This is unnecessary repeat code. If you need to use the textView over and over then store a reference to it to avoid the repetitive lookup.
class MyClass{
EditText myTextBox;
protected void onCreate(stuff){
myTextBox = findViewById(R,id.myTextBox);
findViewById(R.id.myButton).setOnClickListener(new OnClickListener()){
#Override
protected void onClick(){
int myValue = Integer.parseInt(myTextBox.getText().toString());
}
});
}
Also for the record the onCreate should be very clean and to the point. I typically have a syncUI method that does my findViewById calls "prior to data binding days". I don't do that anymore, but new guys learning is fine.
Then in my syncUI I call wrapper methods to handle click listening instead of nesting in onCreate, but for now you are learning. But if you want a quick example..
onCreate(){
setContentView(myViewPointer from the R File);
syncUI();
}
private void syncUI(){
//SETUP TEXTVIEWS OR OTHER UIs that you need
//get btnSubmit reference from findViewByid
btnSubmit_onClick(); //called one time in onCreate to wrap the click listener into method that allows it to collapse and be easily found.
}
private btnSubmit_onClick(){
btnSubmit.setOnClickListener(new OnClickListener(){
#Override
protected void onClick(){
//handle Clicks
}
});
}
Thanks Sam for your reply..
I hv figure out one of my issue that cause my fragment stop responding when I run my app is might due to I mistaken declare my layout as LinearLayout in code above where else my actual layout in the myfragment.xml file is in Relativelayout (shown below).
LinearLayout mLinearLayout = (LinearLayout) inflater.inflate(R.layout.fragment_fuz,
container, false);
After I correct it my app can be open and the fragment related above also able to open. Only there's still problem when I try to run the code using few number sample and my app stop responding with same error "Unfortunately, ServerMonitorApp has stopped".
How, in general, do you find what theme attribute to override in order to alter the look of any UI element?
Currently I rely on trawling through framework source files: the theme definitions in values.xml (usually the support library variant), attribute definitions in attrs.xml, and the R.styleable class docs.
But this is totally hit-and-miss. It's not only unduly time-consuming, but sometimes I miss entirely, for instance I've been trying without success to find out how to change the text styles in a DatePickerDialog's OK and Cancel buttons. Feel free to use that as an example, but if you do, please outline your discovery process. The answer I'm looking for is how to discover the applied styles for any UI element,
Or is there just no deterministic way to find out? Do you just have to know?
Finding how to change styles for widgets on Android has always been troublesome. For example, DatePickerDialog has different styles for Holo and Material design. So, the style of the dialog may depend on the SDK value or if you are using the AppCompat library. There is also little documentation.
It would be nice if tools like Hierarchy Viewer showed attributes and the current theme of a widget. However, I haven't come across such a tool.
We can get the current theme and attributes after the view is created. Here are a couple methods I wrote and tested on a DatePickerDialog to find the style being used:
Get the current theme from a context:
static String getThemeNameFromContext(Context context) {
Resources.Theme theme = context.getTheme();
String themeName;
try {
Field field = theme.getClass().getDeclaredField("mThemeResId");
if (!field.isAccessible()) {
field.setAccessible(true);
}
int themeResId = field.getInt(theme);
themeName = context.getResources().getResourceEntryName(themeResId);
} catch (Exception e) {
// If we are here then the context is most likely the application context.
// The theme for an application context is always "Theme.DeviceDefault"
themeName = "Theme.DeviceDefault";
}
return themeName;
}
Get the name/value of an attribute:
static String getResourceName(Context context, int attribute) {
TypedArray typedArray = context.obtainStyledAttributes(new int[]{attribute});
try {
int resourceId = typedArray.getResourceId(0, 0);
return context.getResources().getResourceEntryName(resourceId);
} finally {
typedArray.recycle();
}
}
In the example below I created a DatePickerDialog and got the theme and attribute values being used by the dialog and the dialog's positive button using the above methods:
// Create the DatePickerDialog
DatePickerDialog datePickerDialog = new DatePickerDialog(getActivity(),
new DatePickerDialog.OnDateSetListener() {
#Override
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
}
}, 2015, Calendar.SEPTEMBER, 9);
// Show the dialog
datePickerDialog.show();
// Get the positive button from the dialog:
Button positiveButton = datePickerDialog.getButton(DatePickerDialog.BUTTON_POSITIVE);
// Get the theme used by this dialog
String theme = getThemeNameFromContext(datePickerDialog.getContext());
// Get the date picker style used by the dialog
String datePickerStyle = getResourceName(datePickerDialog.getContext(), android.R.attr.datePickerStyle);
// Get the style of the positive button:
String buttonStyle = getResourceName(positiveButton.getContext(), android.R.attr.buttonStyle);
Log.i("LOGTAG", "Theme: " + theme);
Log.i("LOGTAG", "datePickerStyle: " + positiveButton);
Log.i("LOGTAG", "buttonStyle: " + buttonStyle);
In my test project I got these values for the theme, datePickerStyle, and buttonStyle:
Theme: ThemeOverlay.Material.Dialog
datePickerStyle: Widget.Material.Light.DatePicker
buttonStyle: Widget.Material.Light.Button
This is somewhat helpful, but we still haven't changed the positive and negative button style. If we view the source for DatePickerDialog we can see that it extends AlertDialog. This makes things harder as you will need to set a custom style in your theme which would affect all AlertDialog buttons. If you need an example on how to change the buttonStyle please leave a comment.
A better approach would be to style the positive and negative buttons after the dialog is visible. For example, in your DialogFragment you can place the following code in onStart() to style the buttons:
#Override
public void onStart() {
super.onStart();
DatePickerDialog dialog = (DatePickerDialog) getDialog();
Button btnPos = dialog.getButton(DatePickerDialog.BUTTON_POSITIVE);
Button btnNeg = dialog.getButton(DatePickerDialog.BUTTON_NEGATIVE);
/* customize the buttons here */
btnPos.setText("CUSTOM");
btnPos.setTextAppearance(android.R.style.TextAppearance_Large);
btnNeg.setTextColor(Color.RED);
}
Conclusion:
You can use the above methods on other views to find the style that applies for that view. It is still a pain in the a** to style widgets on Android. You may need to dig through source code and XML from time to time.
On my T-mobile MOVE, dialogs have black background and black text - not good.
On the emulator, all the dialogs have white background and black text.
How to fix this? Why is this happening? Am I supposed to explicitly set background for dialogs?
As Royston said you have to make custom dialogs. To remove black corner use following line
super(context, android.R.style.Theme_Translucent_NoTitleBar);
instead of making custom style.
A simple example of custom dialog will be like
public class customDialog extends Dialog implements android.view.View.OnClickListener{
Context context = null;
public customDialog(Context context, String etc ) {
super(context, android.R.style.Theme_Translucent_NoTitleBar);
this.context = context;
setBasicContents();
}
private void setBasicContents() {
this.setContentView(R.layout.custom_dialog);
this.findViewById(R.id.btn_ok).setOnClickListener(this);
this.findViewById(R.id.btn_cancel).setOnClickListener(this);
((RelativeLayout)this.findViewById(R.id.img_bg_tranperant)).
setBackgroundColor(Color.argb(166, 0, 0, 0));
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_ok:
anythingyouwant();
break;
case R.id.btn_cancel:
this.dismiss();
break;
default:
break;
}
}
}
A line
((RelativeLayout)this.findViewById(R.id.parent_layout)).
setBackgroundColor(Color.argb(166, 0, 0, 0));
is important. It take parent layout of dialog xml and set its background to little transparent. So when user see the dialog the back screen will little dim. In additon to this layout over dialog xml will contain two buttons and one textView to show message. You can customize this custom class and xml to your requirements.
Thanks,
Basically if you use default Dialog in Android, then since each vendor have set the colors and backgrounds for their dialogs, they will look different. For e.g. Samsung dialog is completely different from the default Nexus devices dialogs.
The best bet for you is to define your own colors and theme for dialogs to look uniform across all devices. Else brace yourself for a new look dialog as the vendor wants it to be displayed.
I have an AlertDialog with just some text, a NumberPicker, an OK, and a Cancel.
package org.dyndns.schep.example;
import android.os.Bundler;
import android.view.View;
import android.widget.NumberPicker;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
public class FooFragment extends DialogFragment {
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mParent = (MainActivity) activity;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
mParent.setFoo(foo());
}
})
.setNegativeButton(android.R.string.cancel, null);
View view = getActivity().getLayoutInflater.inflate(
R.layout.dialog_foo, null);
mPicker = (NumberPicker) view.findViewById(R.id.numberPicker1);
mPicker.setValue(mParent.getFoo());
builder.setView(view);
return builder.create();
}
public int foo() {
return mPicker.getValue();
}
private MainActivity mParent;
private NumberPicker mPicker;
}
(This dialog doesn't yet do the things it should to preserve state on Pause and Resume, I know.)
I would like the "Done" action on the soft keyboard or other IME to dismiss the dialog as though "OK" were pressed, since there's only the one widget to edit.
It looks like the best way to deal with an IME "Done" is usually to setOnEditorActionListener on a TextView. But I don't have any TextView variable, and NumberPicker doesn't obviously expose any TextView, or similar editor callbacks. (Maybe NumberPicker contains a TextView with a constant ID I could search for using findViewById?)
NumberPicker.setOnValueChangedListener does get triggered on the "Done" action, but it also fires when tapping or flicking the list of numbers, which definitely should not dismiss the dialog.
Based on this question, I tried checking out setOnKeyListener, but that interface didn't trigger at all when using the soft keyboard. Not a total surprise, since the KeyEvent documentation suggests it's meant more for hardware events, and in recent APIs the soft keyboard won't send them at all.
How can I connect the IME "Done" to my dialog's "OK" action?
Edit: From the looks of the source, a NumberPicker layout does contain a EditText, but its id is id/numberpicker_input in package com.android.internal. Using that would not be easy, and is obviously discouraged. But it seems like there might only be hack ways to get the behavior I want.
How can I connect the IME "Done" to my dialog's "OK" action?
The problem is that you can't pass the IME's events if you don't have a listener set on the TextView widget which currently works with the IME. One way to do what you want is to hook our own logic to the NumberPicker's child which works with the IME(like you already talked in the last part of your question). To avoid using certain ids or other layout tricks(which can be problematic) to get a hold of that widget, you could use a greedy tactic, setting the listener to any widget from the NumberPicker which could trigger the desired event(TextViews or any subclass of TextView). Something like this:
private AlertDialog mCurrentDialog;
private List<TextView> mTargets = new ArrayList<TextView>();
private OnEditorActionListener mListener = new OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
// if a child of NumberPicker triggers the DONE editor event
// get a reference to the positive button(which you use in your
// code) and click it
mCurrentDialog.getButton(Dialog.BUTTON_POSITIVE).performClick();
}
return false;
}
};
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// ...
mPicker = (NumberPicker) view.findViewById(R.id.numberPicker1);
mPicker.setValue(mParent.getFoo());
// clear any previous targets
mTargets.clear();
// find possible targets in the NumberPicker
findTextViews(mPicker);
// setup those possible targets with our own logic
setupEditorListener();
builder.setView(view);
// get a reference to the current showed dialog
mCurrentDialog = builder.create();
return mCurrentDialog;
}
Where the methods are:
private void findTextViews(ViewGroup parent) {
final int count = parent.getChildCount();
for (int i = 0; i < count; i++) {
final View child = parent.getChildAt(i);
if (child instanceof ViewGroup) {
findTextViews((ViewGroup) child);
} else if (child instanceof TextView) {
mTargets.add((TextView) child);
}
}
}
private void setupEditorListener() {
final int count = mTargets.size();
for (int i = 0; i < count; i++) {
final TextView target = mTargets.get(i);
target.setOnEditorActionListener(mListener);
}
}
The other possible(and reasonable) solution(like Naveen already mentioned in his comment) is to use one of the ports of the NumberPicker class(or modify the one from the SDK) out there and insert your own widget ids(which will make getting a reference to the widget a simple task). This would be easier to implement now but inconvenient to maintain on the long run.
My search and experimentation has also turned out empty handed. That said I wouldn't encourage this kind of behavior as it doesn't make much sense.
If you need a NumberPicker it is because you want the feature of scrolling to the value that you need, not entering it (though it is possible). If you want to enter the value you'd use an EditText and you would be able to implement what you need without problems.
Alternatively you have to copy NumberPicker implementation from the source and then change it according to your needs (fx by adding ime options).
I have a DialogPreference and I want to avoid the user from closing it when pressing "OK", "Cancel", etc.
How should I do that?
EDIT:
I tried to reach the OK button to disable when the dialog is created. But I couldn't make it :(
The solution is quite easy. Overwrite showDialog and set your own click listener to the buttons you want to intercept.
#Override
protected void showDialog(Bundle bundle) {
super.showDialog(bundle);
Button pos = ((AlertDialog) getDialog()).getButton(DialogInterface.BUTTON_POSITIVE);
pos.setOnClickListener(...);
}
In your click listener you can do the validation you want.
A tweak could be to create a custom dialog where you define your own buttons (OK and Close).
public class YourClass implements OnClickListener {
private Button DialogButton;
private Dialog dialog;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.MainLayout);
/* Your code... */
DialogButton = (Button) findViewById(R.id.DialogButtonId);
DialogButton.setOnClickListener(this);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.DialogButtonId:
LayoutInflater inflater = LayoutInflater.from(YourClass.this);
final View inflay = inflater.inflate(R.layout.DialogLayout, (ViewGroup) findViewById(R.id.RootIdOfDialogLayout));
TextView YourTextView = (TextView) inflay.findViewById(R.id.TextViewId);
Button cancel = (Button) inflay.findViewById(R.id.CancelButtonId);
cancel.setOnClickListener(YourClass.this);
Button ok = (Button) inflay.findViewById(R.id.OkButtonId);
ok.setOnClickListener(YourClass.this);
dialog = new Dialog(YourClass.this);
dialog.setContentView(inflay);
dialog.setTitle(getString(R.string.TitleStringId));
dialog.show();
break;
case R.id.CancelButtonId:
/* Checking if the user selected an option if true call dialog.dismiss() */
break;
case R.id.OkButtonId:
/* Here handle your preferences (e.g. putString(String key, String value)) */
/* Checking if the user selected an option if true call dialog.dismiss() */
break;
}
}
}
Check out http://developer.android.com/reference/android/content/SharedPreferences.Editor.html in order to handle your preference in onClick. I didn't test this code just wrote it to show you how you could solve it!
The dialog stays open until you call dialog.dismiss();. In that case you'll have to create your drop-down-menu, polls or what ever you want to display in your layout file. After pressing ok or cancel you should check if the user made a choice, and parse that choice into your preferences. (check link above)
Rgds
Layne
You could try opening it again.
Why would you want to prevent users to close the dialog? Users should be able to have 'full' control of their device.
You can see the source code of DialogPreferences here:
https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/preference/DialogPreference.java
And then, copy most of it to your code, modifying the code as needed.
How about overriding the onDismiss() method and implementing a canExit() method with the validations you want to occcur? E.g. :
public class MyDialogPref extends DialogPreference {
#override public void onDismiss(DialogInterface dialog) {
if (canExit()) {
super.onDismiss(dialog);
}
}
...
}
A good UI should have a default selection/option already selected (the previously user-entered options or a program default).
Presenting a dialog asking for a change in options without any indication of what you already have is bad UI design.
This way if the user clicks Cancel, nothing changes and they saw what the option selected was. If they make no change and click OK then nothing really changes either.
Software is supposed to make doing specific tasks easier, not force the user to process the apps logic themselves.