// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package com.google.appinventor.components.runtime;
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.DesignerProperty;
import com.google.appinventor.components.annotations.PropertyCategory;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.SimpleProperty;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.common.PropertyTypeConstants;
import com.google.appinventor.components.common.YaVersion;
import android.content.Context;
import android.text.InputType;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
/**
* A box in which the user can enter text.
*
* @author sharon@google.com (Sharon Perl)
* @author halabelson@google.com (Hal Abelson)
*/
@DesignerComponent(version = YaVersion.TEXTBOX_COMPONENT_VERSION,
description = "<p>A box for the user to enter text. The initial or " +
"user-entered text value is in the <code>Text</code> property. If " +
"blank, the <code>Hint</code> property, which appears as faint text " +
"in the box, can provide the user with guidance as to what to type.</p>" +
"<p>The <code>MultiLine</code> property determines if the text can have" +
"more than one line. For a single line text box, the keyboard will close" +
"automatically when the user presses the Done key. To close the keyboard " +
"for multiline text boxes, the app should use the HideKeyboard method or " +
" rely on the user to press the Back key.</p>" +
"<p>The <code> NumbersOnly</code> property restricts the keyboard to accept" +
"numeric input only.</p>" +
"<p>Other properties affect the appearance of the text box " +
"(<code>TextAlignment</code>, <code>BackgroundColor</code>, etc.) and " +
"whether it can be used (<code>Enabled</code>).</p>" +
"<p>Text boxes are usually used with the <code>Button</code> " +
"component, with the user clicking on the button when text entry is " +
"complete.</p>" +
"<p>If the text entered by the user should not be displayed, use " +
"<code>PasswordTextBox</code> instead.</p>",
category = ComponentCategory.USERINTERFACE)
@SimpleObject
public final class TextBox extends TextBoxBase {
/* TODO(user): this code requires Android SDK M5 or newer - we are currently on M3
enables this when we upgrade
// Backing for text during validation
private String text;
private class ValidationTransformationMethod extends TransformationMethod {
@Override
public CharSequence getTransformation(CharSequence source) {
BooleanReferenceParameter accept = new BooleanReferenceParameter(false);
Validate(source.toString, accept);
if (accept.get()) {
text = source.toString();
}
return text;
}
}
*/
// If true, then accept numeric keyboard input only
private boolean acceptsNumbersOnly;
// If true, then text box is multiline
private boolean multiLine;
/**
* Creates a new TextBox component.
*
* @param container container, component will be placed in
*/
public TextBox(ComponentContainer container) {
super(container, new EditText(container.$context()));
NumbersOnly(false);
MultiLine(false);
// We need to set the IME options here. Otherwise, Android's default
// behavior is that the action button will be Done or Next, depending on
// whether there is a next textbox on the screen, That might be convenient,
// but it seems a little obscure to be the standard behavior for
// App Inventor. Perhaps we could make that a property.
// This same line must be in to PasswordTextBox. We could have put it into
// TextBoxBase, but we might later be adding
// other flavors of text boxes that might want their own defaults.
view.setImeOptions(EditorInfo.IME_ACTION_DONE);
}
/**
* NumbersOnly property getter method.
*
* @return {@code true} indicates that the textbox accepts numbers only, {@code false} indicates
* that it accepts any text
*/
@SimpleProperty(
category = PropertyCategory.BEHAVIOR,
description = "If true, then this text box accepts only numbers as keyboard input. " +
"Numbers can include a decimal point and an optional leading minus sign. " +
"This applies to keyboard input only. Even if NumbersOnly is true, you " +
"can use [set Text to] to enter any text at all.")
public boolean NumbersOnly() {
return acceptsNumbersOnly;
}
/**
* NumersOnly property setter method.
*
* @param acceptsNumbersOnly {@code true} restricts input to numeric,
* {@code false} allows any text
*/
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = "False")
@SimpleProperty(
description = "If true, then this text box accepts only numbers as keyboard input. " +
"Numbers can include a decimal point and an optional leading minus sign. " +
"This applies to keyboard input only. Even if NumbersOnly is true, you " +
"can use [set Text to] to enter any text at all.")
public void NumbersOnly(boolean acceptsNumbersOnly) {
if (acceptsNumbersOnly) {
view.setInputType(
InputType.TYPE_CLASS_NUMBER |
InputType.TYPE_NUMBER_FLAG_SIGNED |
InputType.TYPE_NUMBER_FLAG_DECIMAL);
} else {
view.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
}
this.acceptsNumbersOnly = acceptsNumbersOnly;
}
/**
* Hide the soft keyboard
*/
@SimpleFunction(
description = "Hide the keyboard. Only multiline text boxes need this. " +
"Single line text boxes close the keyboard when the users presses the Done key.")
public void HideKeyboard() {
InputMethodManager imm =
(InputMethodManager) container.$context().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
/**
* Multi line property getter method.
*
* @return {@code true} indicates that the textbox accepts multiple lines
* {@code false} lines that the textbox accepts only a single line of input,
*/
@SimpleProperty(
category = PropertyCategory.BEHAVIOR,
description = "If true, then this text box accepts multiple lines of input, which are " +
"entered using the return key. For single line text boxes there is a Done " +
"key instead of a return key, and pressing Done hides the keyboard. " +
"The app should call the HideKeyboard method to hide the keyboard for " +
"a mutiline text box.")
public boolean MultiLine() {
return multiLine;
}
/**
* MultiLine property setter method.
*
* @param multiLine {@code true} lets to textbox accept multiple lines of input
* {@code false} restricts the textbox to only a single line
*/
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN, defaultValue = "False")
@SimpleProperty()
public void MultiLine(boolean multiLine) {
this.multiLine = multiLine;
view.setSingleLine(!multiLine);
}
// TODO(halabelson): We might also want a method to show the keyboard.
// Currently the text box keyboard will open when the text field becomes
// active, and that may be the best simple thing. If we implement show keyboard,
// note that showSoftInputFromWindow seems to open the keyboard only if it
// has been previously opened and closed.
}