Related
I'm developing a custom keyboard for Android that will store login credentials in a local database (plan is to encrypt the DB after the thing works flawlessly).
The keyboard has a basic layout with a KeyboardView at the bottom and a 'Switch Keyboard' button at the top-right corner.
Everything works fine except that when i click the switch button, things start to fail.
Scenario:
1) My keyboard is active and everything is fine. Keyboard visibility is managed by the system.
2) I click the switch button, my keyboard is closed and previously enabled keyboard is displayed.
3) Now i open the Keyboard Chooser through Notification Tray and re-enable my custom keyboard.
My keyboard is set as default, but there's no keyboard on the screen. when i touch the bottom half of screen area where keyboard appears, the characters are being typed (yes, my keyboard is there but is invisible!).
I press the Back key on my phone and the system doesn't close the keyboard and it's stuck there.
I press the Home key and the keyboard becomes visible for a few milliseconds, is closed by the system and i'm sent to the Home Screen.
I've tried waiting for the keyboard to appear, etc. and also tried the app on 3 other phones from different make. Also, i'm developing the app for Lollipop (API 21 and higher).
Here's the code:
1) InputMethodService:
public class HomlesInputService extends InputMethodService implements KeyboardView.OnKeyboardActionListener {
CustomKeyboardView kv;
Keyboard qwertyKeyboard, symbolKeyboard;
Boolean caps;
private String myPwd, myUid;
SharedPreferences mPrefs;
SharedPreferences.Editor mEdit;
Boolean isPassword, isUid, uidFilled, pwdFilled;
Vibrator vibrator;
View keyboard;
EditorInfo sEditorInfo;
SQLiteDatabase db;
InputMethodManager imm;
IBinder token;
String defImeId;
public static final int SYM_KEYBOARD = -10;
public static final int GO_BUTTON = -4;
public static final double GO_BUTTON_ASPECT_RATIO = 1.88;
public static final double NEXT_BUTTON_ASPECT_RATIO = 1;
public HomlesInputService() {
}
#Override
public void onCreate() {
super.onCreate();
mPrefs = getSharedPreferences(Constants.APP_PREFS, MODE_PRIVATE);
mEdit = mPrefs.edit();
//storing ime id in prefs
String myId = Settings.Secure.getString(getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
mEdit.putString(Constants.MY_IME_ID, myId);
mEdit.commit();
imm = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
defImeId = mPrefs.getString(Constants.DEFAULT_KEYBOARD, null); //the id of previous IME stored in preferences (found correct in debugging)
qwertyKeyboard = new Keyboard(this, R.xml.qwerty_keyboard_layout);
symbolKeyboard = new Keyboard(this, R.xml.symbol_keyboard_layout);
db = openOrCreateDatabase(DbConstants.DATABASE_NAME, MODE_PRIVATE, null);
db.execSQL("CREATE TABLE IF NOT EXISTS " + DbConstants.TABLE_NAME + "("
+ DbConstants.APP_ID + "," + DbConstants.UID_VALUE + "," + DbConstants.PWD_VALUE + ")");
vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
}
#Override
public View onCreateInputView() {
keyboard = getLayoutInflater().inflate(R.layout.my_custom_keyboard, null);
ImageView ivRevertKeyboard = (ImageView) keyboard.findViewById(R.id.ivRevertKeyboard); //this button is used to switch to previous IME
token = this.getWindow().getWindow().getAttributes().token;
if (defImeId != null) {
ivRevertKeyboard.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
vibrator.vibrate(Constants.VIBRATE_TIME_MEDIUM);
try {
imm.hideSoftInputFromInputMethod(token, 0);
imm.setInputMethod(token, defImeId);
} catch (Throwable t) { // java.lang.NoSuchMethodError if API_level<11
t.printStackTrace();
}
}
});
} else {
ivRevertKeyboard.setVisibility(View.GONE);
}
kv = (CustomKeyboardView) keyboard.findViewById(R.id.keyboard);
kv.setKeyboard(qwertyKeyboard);
kv.setOnKeyboardActionListener(this);
caps = false;
myPwd = "";
myUid = "";
isPassword = false;
return keyboard;
}
#Override
public void onStartInputView(EditorInfo info, boolean restarting) {
sEditorInfo = info;
uidFilled = false;
pwdFilled = false;
if (info.inputType == 129) {
isPassword = true;
} else {
isPassword = false;
}
CharSequence CHARbeforeCursor = getCurrentInputConnection().getTextBeforeCursor(999, 0);
CharSequence CHARafterCursor = getCurrentInputConnection().getTextAfterCursor(999, 0);
if (CHARbeforeCursor != null && CHARafterCursor != null) {
String beforeCursor = CHARbeforeCursor.toString();
String afterCursor = CHARafterCursor.toString();
if (isPassword) {
myPwd = beforeCursor + afterCursor;
} else {
myUid = beforeCursor + afterCursor;
}
}
setGoButton();
}
private void setGoButton() {
List<Keyboard.Key> keys = kv.getKeyboard().getKeys();
Drawable dr;
int rowHeight, goBtWidth = 0, nextBtWidth = 0;
rowHeight = Utils.dpToPx(40, getApplicationContext());
goBtWidth = Integer.parseInt("" + Math.round(rowHeight * GO_BUTTON_ASPECT_RATIO));
nextBtWidth = Integer.parseInt("" + Math.round(rowHeight * NEXT_BUTTON_ASPECT_RATIO));
for (int i = 0; i < keys.size(); i++) {
Keyboard.Key key = keys.get(i);
int code = key.codes[0];
if (code == GO_BUTTON) {
if (isPassword) {
key.width = goBtWidth;
dr = getResources().getDrawable(R.drawable.keyboard_save_and_go, getApplicationContext().getTheme());
} else {
key.width = nextBtWidth;
dr = getResources().getDrawable(R.drawable.keyboard_next, getApplicationContext().getTheme());
}
key.icon = dr;
}
}
}
#Override
public void onPress(int primaryCode) {
vibrator.vibrate(Constants.VIBRATE_TIME_SHORT);
switch (primaryCode) {
case Keyboard.KEYCODE_DELETE:
kv.setPreviewEnabled(false);
break;
case Keyboard.KEYCODE_SHIFT:
kv.setPreviewEnabled(false);
break;
case Keyboard.KEYCODE_DONE:
kv.setPreviewEnabled(false);
break;
case SYM_KEYBOARD:
kv.setPreviewEnabled(false);
break;
case 32:
kv.setPreviewEnabled(false);
break;
default:
kv.setPreviewEnabled(true);
}
}
#Override
public void onRelease(int i) {
}
#Override
public void onKey(int primaryCode, int[] ints) {
InputConnection conn = getCurrentInputConnection();
uidFilled = mPrefs.getBoolean(Constants.UID_FILLED, false);
pwdFilled = mPrefs.getBoolean(Constants.PWD_FILLED, false);
switch (primaryCode) {
case Keyboard.KEYCODE_DELETE:
conn.deleteSurroundingText(1, 0);
if (isPassword) {
if (myPwd.length() > 0) {
String beforeCursor = conn.getTextBeforeCursor(myPwd.length(), 0).toString();
beforeCursor = beforeCursor.substring(0, beforeCursor.length());
String afterCursor = conn.getTextAfterCursor(myPwd.length(), 0).toString();
myPwd = beforeCursor + afterCursor;
if (uidFilled && pwdFilled) {
mEdit.putString(Constants.EDIT_NEW_PWD, myPwd);
} else {
mEdit.putString(Constants.NEW_PWD, myPwd);
}
mEdit.commit();
Log.d("##Pwd saved", "" + mPrefs.getString(Constants.NEW_PWD, ""));
}
} else {
if (myUid.length() > 0) {
String beforeCursor = conn.getTextBeforeCursor(myUid.length(), 0).toString();
beforeCursor = beforeCursor.substring(0, beforeCursor.length());
String afterCursor = conn.getTextAfterCursor(myUid.length(), 0).toString();
myUid = beforeCursor + afterCursor;
if (uidFilled && pwdFilled) {
mEdit.putString(Constants.EDIT_NEW_UID, myUid);
} else {
mEdit.putString(Constants.NEW_UID, myUid);
}
mEdit.commit();
Log.d("##Uid saved", "" + mPrefs.getString(Constants.NEW_UID, ""));
}
}
break;
case Keyboard.KEYCODE_SHIFT:
caps = !caps;
kv.setShifted(caps);
kv.invalidateAllKeys();
break;
case Keyboard.KEYCODE_DONE:
switch (sEditorInfo.imeOptions & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION)) {
case EditorInfo.IME_ACTION_GO:
conn.performEditorAction(EditorInfo.IME_ACTION_GO);
break;
case EditorInfo.IME_ACTION_NEXT:
conn.performEditorAction(EditorInfo.IME_ACTION_NEXT);
break;
case EditorInfo.IME_ACTION_SEARCH:
conn.performEditorAction(EditorInfo.IME_ACTION_SEARCH);
break;
case EditorInfo.IME_ACTION_SEND:
conn.performEditorAction(EditorInfo.IME_ACTION_SEND);
break;
case EditorInfo.IME_ACTION_DONE:
saveCredentialsToDb();
conn.performEditorAction(EditorInfo.IME_ACTION_DONE);
//triggers login
break;
default:
conn.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
break;
}
break;
case SYM_KEYBOARD:
Keyboard currentKeyb = kv.getKeyboard();
if (currentKeyb == symbolKeyboard) {
kv.setKeyboard(qwertyKeyboard);
} else {
kv.setKeyboard(symbolKeyboard);
}
setGoButton();
break;
default:
char code = (char) primaryCode;
if (Character.isLetter(code) && caps) {
code = Character.toUpperCase(code);
}
if (isPassword) {
myPwd = myPwd + code;
if (uidFilled && pwdFilled) {
mEdit.putString(Constants.EDIT_NEW_PWD, myPwd);
} else {
mEdit.putString(Constants.NEW_PWD, myPwd);
}
mEdit.commit();
Log.d("##Pwd saved", "" + mPrefs.getString(Constants.NEW_PWD, ""));
} else {
myUid = myUid + code;
if (uidFilled && pwdFilled) {
mEdit.putString(Constants.EDIT_NEW_UID, myUid);
} else {
mEdit.putString(Constants.NEW_UID, myUid);
}
mEdit.commit();
Log.d("##Uid saved", "" + mPrefs.getString(Constants.NEW_UID, ""));
}
conn.commitText(String.valueOf(code), 1);
}
}
private void saveCredentialsToDb() {
db = openOrCreateDatabase(DbConstants.DATABASE_NAME, MODE_PRIVATE, null);
String activityDbId = getCurrentInputEditorInfo().packageName;
if (myUid == null || myUid.length() <= 0) {
Toast.makeText(getApplicationContext(), "Enter the Username", Toast.LENGTH_SHORT).show();
return;
} else if (myPwd == null || myPwd.length() <= 0) {
Toast.makeText(getApplicationContext(), "Enter the Password", Toast.LENGTH_SHORT).show();
return;
} else if (activityDbId == null && activityDbId.length() <= 0) {
Toast.makeText(getApplicationContext(), "Failed to get app ID", Toast.LENGTH_SHORT).show();
return;
}
Log.d("##Activity pkg name", "" + activityDbId);
ContentValues cv = new ContentValues();
cv.put(DbConstants.APP_ID, activityDbId);
cv.put(DbConstants.UID_VALUE, myUid);
cv.put(DbConstants.PWD_VALUE, myPwd);
if (uidFilled && pwdFilled) {
String oldUid = mPrefs.getString(Constants.EDIT_OLD_UID, null);
String oldPwd = mPrefs.getString(Constants.EDIT_OLD_PWD, null);
if (oldUid != null && oldPwd != null) {
if (oldUid.contentEquals(myUid) && oldPwd.contentEquals(myPwd)) {
Toast.makeText(getApplicationContext(), "Unchanged values.", Toast.LENGTH_SHORT).show();
return;
}
db.update(DbConstants.TABLE_NAME, cv, DbConstants.APP_ID + "=?" + " AND " + DbConstants.UID_VALUE + "=?", new String[]{activityDbId, oldUid});
mEdit.remove(Constants.EDIT_OLD_UID);
mEdit.remove(Constants.EDIT_NEW_UID);
mEdit.remove(Constants.EDIT_NEW_PWD);
mEdit.putBoolean(Constants.UID_FILLED, false);
mEdit.putBoolean(Constants.PWD_FILLED, false);
mEdit.commit();
uidFilled = false;
pwdFilled = false;
Toast.makeText(getApplicationContext(), "Credentials edited!", Toast.LENGTH_SHORT).show();
}
} else {
Cursor cr2 = db.query(DbConstants.TABLE_NAME, null, DbConstants.APP_ID + "=?" + " AND " + DbConstants.UID_VALUE + "=?", new String[]{activityDbId, myUid}, null, null, null);
if (cr2.moveToNext()) {
db.update(DbConstants.TABLE_NAME, cv, DbConstants.APP_ID + "=?" + " AND " + DbConstants.UID_VALUE + "=?", new String[]{activityDbId, myUid});
Toast.makeText(getApplicationContext(), "Credentials edited!", Toast.LENGTH_SHORT).show();
} else {
db.insert(DbConstants.TABLE_NAME, null, cv);
Log.d("Stored:" + activityDbId, myUid + "->" + myPwd);
mEdit.remove(Constants.NEW_UID);
mEdit.remove(Constants.NEW_PWD);
mEdit.commit();
Toast.makeText(getApplicationContext(), "Credentials stored!", Toast.LENGTH_SHORT).show();
}
}
}
#Override
public void onFinishInput() {
super.onFinishInput();
Log.d("onFinishInput", "invoked");
}
#Override
public void onText(CharSequence charSequence) {
}
#Override
public void swipeLeft() {
}
#Override
public void swipeRight() {
}
#Override
public void swipeDown() {
}
#Override
public void swipeUp() {
}
}
2) CustomKeyboardView:
public class CustomKeyboardView extends KeyboardView {
Context mContext;
Boolean caps;
public CustomKeyboardView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
caps = false;
}
#Override
public boolean setShifted(boolean shifted) {
super.setShifted(shifted);
caps = shifted;
invalidateAllKeys();
return shifted;
}
#Override
public void onDraw(Canvas canvas) {
List<Keyboard.Key> keys = getKeyboard().getKeys();
for (Keyboard.Key key : keys) {
Drawable dr = null;
if (key.codes[0] == Keyboard.KEYCODE_DELETE) {
dr = getResources().getDrawable(R.drawable.keyboard_backspace, mContext.getTheme());
dr.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
dr.draw(canvas);
} else if (key.codes[0] == Keyboard.KEYCODE_SHIFT) {
dr = getResources().getDrawable(R.drawable.keyboard_shift, mContext.getTheme());
dr.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
dr.draw(canvas);
} else if (key.codes[0] == 32) { //space bar
int tenDp = Utils.dpToPx(10, mContext);
int twoDp = Utils.dpToPx(2, mContext);
dr = new ColorDrawable(Color.LTGRAY);
dr.setBounds(key.x + twoDp, key.y + (tenDp), key.x + key.width - twoDp, key.y + key.height - (tenDp));
dr.draw(canvas);
}
Paint mPaint = new Paint();
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setTextSize(Utils.dpToPx(18, mContext));
mPaint.setColor(Color.BLACK);
if (key.label != null) {
String keyLabel = key.label.toString();
if (caps) {
keyLabel = keyLabel.toUpperCase();
}
canvas.drawText(keyLabel, key.x + (key.width / 2),
key.y + (key.height / 2) + Utils.dpToPx(5, mContext), mPaint);
} else if (key.icon != null) {
key.icon.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
key.icon.draw(canvas);
}
}
}
}
3) R.layout.my_custom_keyboard:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:orientation="vertical"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/blue">
<ImageView
android:id="#+id/ivRevertKeyboard"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentRight="true"
android:padding="10dp"
android:scaleType="fitEnd"
android:src="#drawable/keyboard_icon" />
</RelativeLayout>
<com.prasad.CustomKeyboardView
android:id="#+id/keyboard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:layout_marginTop="5dp"
android:keyPreviewHeight="50dp"
android:background="#color/white"
android:keyPreviewLayout="#xml/keyboard_preview"
android:shadowColor="#color/transparent" />
</LinearLayout>
Expected behaviour: The keyboard should be displayed and hidden properly by the system after swapping multiple times with another keyboard.
Please let me know if you need additional details. Any clues will be appreciated.
Regards,
Prasad
how is this?
#Override
public void onClick(View view) {
vibrator.vibrate(Constants.VIBRATE_TIME_MEDIUM);
try {
// imm.hideSoftInputFromInputMethod(token, 0);
// imm.setInputMethod(token, defImeId);
this.switchInputMethod(defImeId);
} catch (Throwable t) { // java.lang.NoSuchMethodError if API_level<11
t.printStackTrace();
}
}
I try to inject some keys in a text field with a InputMethodService.
In my Manifest everything seems alright.
Everytime when I call getCurrentInputConnection() I get null.
My class extends InputMethodService and I start the service.
Can anyone help me? Is there something else I need to do to get the currentInputConnection??
Here is the code:
Its from an open source project called WifiKeyboard
And I try to inject code with the function setText(String text)
package com.example.twolibs;
/**
* WiFi Keyboard - Remote Keyboard for Android.
* Copyright (C) 2011 Ivan Volosyuk
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import java.util.HashSet;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.hardware.input.InputManager;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.RemoteException;
import android.text.InputType;
import android.util.Log;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethod;
public class WiFiInputMethod extends InputMethodService {
public static final int KEY_HOME = -1000;
public static final int KEY_END = -1001;
public static final int KEY_CONTROL = -1002;
public static final int KEY_DEL = -1003;
#Override
public void onStartInput(EditorInfo attribute, boolean restarting) {
super.onStartInput(attribute, restarting);
// boolean multiline = (attribute.inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) != 0;
// Log.d("ivan", "onStartInput "
// + "actionId=" + attribute.actionId + " "
// + "id=" + attribute.fieldId + " "
// + "name=" + attribute.fieldName + " "
// + "opt=" + Integer.toHexString(attribute.imeOptions) + " "
// + "inputType=" + Integer.toHexString(attribute.inputType) + " "
// + "priv=" + attribute.privateImeOptions
// + "multiline=" + multiline);
try {
String text = getText();
} catch (NullPointerException e) {
}
}
ServiceConnection serviceConnection;
#Override
public void onDestroy() {
// Debug.d("WiFiInputMethod onDestroy()");
if (serviceConnection != null)
unbindService(serviceConnection);
serviceConnection = null;
super.onDestroy();
}
PowerManager.WakeLock wakeLock;
HashSet<Integer> pressedKeys = new HashSet<Integer>();
#Override
public void onCreate() {
super.onCreate();
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(
PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "wifikeyboard");
// Debug.d("WiFiInputMethod started");
serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service) {
// Debug.d("WiFiInputMethod connected to HttpService.");
}
//#Override
public void onServiceDisconnected(ComponentName name) {
// Debug.d("WiFiInputMethod disconnected from HttpService.");
}
};
}
#Override
public boolean onEvaluateFullscreenMode() {
return false;
}
void receivedChar(int code) {
wakeLock.acquire();
wakeLock.release();
InputConnection conn = getCurrentInputConnection();
if (conn == null) {
// Debug.d("connection closed");
return;
}
if (pressedKeys.contains(KEY_CONTROL)) {
switch (code) {
case 'a':case 'A': selectAll(conn); return;
case 'x':case 'X': cut(conn); return;
case 'c':case 'C': copy(conn); return;
case 'v':case 'V': paste(conn); return;
}
}
String text = null;
if (code >= 0 && code <= 65535) {
text = new String(new char[] { (char) code } );
} else {
int HI_SURROGATE_START = 0xD800;
int LO_SURROGATE_START = 0xDC00;
int hi = ((code >> 10) & 0x3FF) - 0x040 | HI_SURROGATE_START;
int lo = LO_SURROGATE_START | (code & 0x3FF);
text = new String(new char[] { (char) hi, (char) lo } );
}
conn.commitText(text, 1);
}
#SuppressWarnings("unused")
void receivedKey(int code, boolean pressed) {
if (false) {
for (int key : pressedKeys) {
sendKey(key, false, false);
}
pressedKeys.clear();
resetModifiers();
return;
}
if (pressedKeys.contains(code) == pressed) {
if (pressed == false) return;
// ignore autorepeat on following keys
switch (code) {
case KeyEvent.KEYCODE_ALT_LEFT:
case KeyEvent.KEYCODE_SHIFT_LEFT:
case KeyEvent.KEYCODE_HOME:
case KeyEvent.KEYCODE_MENU: return;
}
}
if (pressed) {
pressedKeys.add(code);
System.out.println("one");
sendKey(code, pressed, false);
} else {
pressedKeys.remove(code);
sendKey(code, pressed, pressedKeys.isEmpty());
}
}
void resetModifiers() {
InputConnection conn = getCurrentInputConnection();
if (conn == null) {
return;
}
conn.clearMetaKeyStates(
KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON | KeyEvent.META_SYM_ON);
}
private long lastWake;
void sendKey(int code, boolean down, boolean resetModifiers) {
System.out.println("two");
/*
long time = System.currentTimeMillis();
if (time - lastWake > 5000) {
wakeLock.acquire();
wakeLock.release();
lastWake = time;
}
*/
InputConnection conn = getCurrentInputConnection();
System.out.println("three");
if (conn == null) {
System.out.println("kacke");
// Debug.d("connection closed");
return;
}
if (code < 0) {
if (down == false) return;
switch (code) {
case KEY_HOME: keyHome(conn); break;
case KEY_END: keyEnd(conn); break;
case KEY_DEL: keyDel(conn); break;
}
return;
}
if (pressedKeys.contains(KEY_CONTROL)) {
switch (code) {
case KeyEvent.KEYCODE_DPAD_LEFT:
if (!down) return;
wordLeft(conn);
return;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (!down) return;
wordRight(conn);
return;
case KeyEvent.KEYCODE_DEL:
if (!down) return;
deleteWordLeft(conn);
return;
case KeyEvent.KEYCODE_FORWARD_DEL:
deleteWordRight(conn);
return;
case KeyEvent.KEYCODE_DPAD_CENTER:
if (!down) return;
copy(conn);
return;
}
}
if (pressedKeys.contains(KeyEvent.KEYCODE_SHIFT_LEFT)) {
switch (code) {
case KeyEvent.KEYCODE_DPAD_CENTER:
if (!down) return;
paste(conn);
return;
}
}
if (code == KeyEvent.KEYCODE_ENTER) {
if (shouldSend()) {
if (!down) return;
Log.d("ivan", "sending submit action");
conn.performEditorAction(EditorInfo.IME_ACTION_SEND);
return;
}
}
// if (pressedKeys.contains(KEY_CONTROL)) {
// if (down == false) return;
// switch (code) {
// case KeyEvent.KEYCODE_A: selectAll(conn); break;
// case KeyEvent.KEYCODE_X: cut(conn); break;
// case KeyEvent.KEYCODE_C: copy(conn); break;
// case KeyEvent.KEYCODE_V: paste(conn); break;
// }
// return;
// }
System.out.println("four");
conn.sendKeyEvent(new KeyEvent(
android.os.SystemClock.uptimeMillis(),
android.os.SystemClock.uptimeMillis(),
down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
code,
0,
(pressedKeys.contains(KeyEvent.KEYCODE_SHIFT_LEFT)
? KeyEvent.META_SHIFT_LEFT_ON : 0) +
(pressedKeys.contains(KEY_CONTROL)? KeyEvent.META_CTRL_ON : 0) +
(pressedKeys.contains(KeyEvent.KEYCODE_ALT_LEFT)
? KeyEvent.META_ALT_LEFT_ON : 0)
));
if (resetModifiers) {
conn.clearMetaKeyStates(
KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON | KeyEvent.META_SYM_ON);
}
}
private boolean shouldSend() {
if (pressedKeys.contains(KEY_CONTROL)) {
// Log.d("ivan", "Control pressed");
return true;
}
EditorInfo editorInfo = getCurrentInputEditorInfo();
if (editorInfo == null) {
// Log.d("ivan", "No editor info");
return false;
}
if ((editorInfo.inputType & InputType.TYPE_CLASS_TEXT) == 0) {
// Log.d("ivan", "Not text, sending enter");
return false;
}
if ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) != 0) {
// Log.d("ivan", "Multi-line, sending ordinary enter");
return false;
}
int action = editorInfo.imeOptions & EditorInfo.IME_MASK_ACTION;
if (action == EditorInfo.IME_ACTION_NONE || action == EditorInfo.IME_ACTION_DONE) {
// Log.d("ivan", "No useful action, sending enter");
return false;
}
// Log.d("ivan", "Useful action to be performed");
return true;
}
private void keyDel(InputConnection conn) {
// if control key used -- delete word right
if (pressedKeys.contains(KEY_CONTROL)) {
deleteWordRight(conn);
return;
}
if (pressedKeys.contains(KeyEvent.KEYCODE_SHIFT_LEFT)) {
cut(conn);
return;
}
conn.deleteSurroundingText(0, 1);
conn.commitText("", 0);
}
private void paste(InputConnection conn) {
conn.performContextMenuAction(android.R.id.paste);
}
private void copy(InputConnection conn) {
conn.performContextMenuAction(android.R.id.copy);
}
private void cut(InputConnection conn) {
conn.performContextMenuAction(android.R.id.cut);
}
public void selectAll(InputConnection conn) {
ExtractedText text = conn.getExtractedText(req, 0);
try {
conn.setSelection(0, text.text.length());
} catch (NullPointerException e) {
// Potentially, text or text.text can be null
}
}
ExtractedTextRequest req = new ExtractedTextRequest();
{
req.hintMaxChars = 100000;
req.hintMaxLines = 10000;
}
private void deleteWordRight(InputConnection conn) {
ExtractedText text = conn.getExtractedText(req, 0);
if (text == null) return;
int end = text.selectionEnd;
String str = text.text.toString();
int len = str.length();
for (; end < len; end++) {
if (!Character.isSpace(str.charAt(end))) break;
}
for (; end < len; end++) {
if (Character.isSpace(str.charAt(end))) break;
}
conn.deleteSurroundingText(0, end - text.selectionEnd);
}
private void deleteWordLeft(InputConnection conn) {
// TODO: what is the correct word deleting policy?
// delete until next space? until next different character type?
ExtractedText text = conn.getExtractedText(req, 0);
if (text == null) return;
int end = text.selectionEnd - 1;
String str = text.text.toString();
for (; end >= 0; end--) {
if (!Character.isSpace(str.charAt(end))) break;
}
for (; end >= 0; end--) {
if (Character.isSpace(str.charAt(end))) break;
}
end++;
conn.deleteSurroundingText(text.selectionEnd - end, 0);
}
private void wordRight(InputConnection conn) {
boolean shift = pressedKeys.contains(KeyEvent.KEYCODE_SHIFT_LEFT);
ExtractedText text = conn.getExtractedText(req, 0);
if (text == null) return;
int end = text.selectionEnd;
String str = text.text.toString();
int len = str.length();
for (; end < len; end++) {
if (!Character.isSpace(str.charAt(end))) break;
}
for (; end < len; end++) {
if (Character.isSpace(str.charAt(end))) break;
}
int start = shift ? text.selectionStart : end;
Log.d("wifikeyboard", "start = " + start + " end = " + end);
conn.setSelection(start, end);
}
private void wordLeft(InputConnection conn) {
boolean shift = pressedKeys.contains(KeyEvent.KEYCODE_SHIFT_LEFT);
ExtractedText text = conn.getExtractedText(req, 0);
if (text == null) return;
int end = text.selectionEnd - 1;
String str = text.text.toString();
for (; end >= 0; end--) {
if (!Character.isSpace(str.charAt(end))) break;
}
for (; end >= 0; end--) {
if (Character.isSpace(str.charAt(end))) break;
}
end++;
int start = shift ? text.selectionStart : end;
Log.d("wifikeyboard", "start = " + start + " end = " + end);
conn.setSelection(start, end);
}
private void keyEnd(InputConnection conn) {
boolean control = pressedKeys.contains(KEY_CONTROL);
boolean shift = pressedKeys.contains(KeyEvent.KEYCODE_SHIFT_LEFT);
ExtractedText text = conn.getExtractedText(req, 0);
if (text == null) return;
int end;
if (control) {
end = text.text.length();
} else {
end = text.text.toString().indexOf('\n', text.selectionEnd);
if (end == -1) end = text.text.length();
}
int start = shift ? text.selectionStart : end;
Log.d("wifikeyboard", "start = " + start + " end = " + end);
conn.setSelection(start, end);
}
private void keyHome(InputConnection conn) {
boolean control = pressedKeys.contains(KEY_CONTROL);
boolean shift = pressedKeys.contains(KeyEvent.KEYCODE_SHIFT_LEFT);
ExtractedText text = conn.getExtractedText(req, 0);
if (text == null) return;
int end;
if (control) {
end = 0;
} else {
end = text.text.toString().lastIndexOf('\n', text.selectionEnd - 1);
end++;
}
int start = shift ? text.selectionStart : end;
Log.d("wifikeyboard", "start = " + start + " end = " + end);
conn.setSelection(start, end);
}
boolean setText(String text) {
// FIXME: need feedback if the input was lost
InputConnection conn = getCurrentInputConnection();
if (conn == null) {
// Debug.d("connection closed");
return false;
}
conn.commitText(text, text.length());
conn.beginBatchEdit();
// FIXME: hack
conn.deleteSurroundingText(100000, 100000);
conn.commitText(text, text.length());
conn.endBatchEdit();
return true;
}
String getText() {
String text = "";
try {
InputConnection conn = getCurrentInputConnection();
ExtractedTextRequest req = new ExtractedTextRequest();
req.hintMaxChars = 1000000;
req.hintMaxLines = 10000;
req.flags = 0;
req.token = 1;
text = conn.getExtractedText(req, 0).text.toString();
} catch (Throwable t) {
}
return text;
}
}
Thank you
I'm currently trying to debug my application, however, I'm running into some difficulties with a NullPointerException. I have a dialog box with 4 checkboxes, a save button, and a cancel button. When the user marks their choices in the dialog box then presses the save button it returns a NullPointerException and I don't understand why. Below is the XML file in case it's relevant to finding the solution:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scrollviewPref"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true"
android:layout_marginTop="10dip">
<LinearLayout
android:layout_width="300dip"
android:layout_height="wrap_content"
android:orientation="vertical"
android:minWidth="200dip">
<CheckBox
android:id="#+id/altitudeCheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Altitude"
android:layout_gravity="left" />
<CheckBox
android:id="#+id/latitudeCheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Latitude"
android:layout_gravity="left" />
<CheckBox
android:id="#+id/longitudeCheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Longitude"
android:layout_gravity="left" />
<CheckBox
android:id="#+id/velocityCheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Velocity"
android:layout_gravity="left" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:layout_marginBottom="10dip"
android:layout_marginTop="10dip">
<Button
android:id="#+id/saveBtn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Save"
android:layout_weight="1" />
<Button
android:id="#+id/cancelBtn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Cancel"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>
</ScrollView>
The set set of code is the code that saves the user's choices to a SharedPreferences.
package shc_BalloonSat.namespace;
import android.app.Dialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
public class CheckPreferences extends Dialog
{
Context shc;
private CheckBox altitudeCheckBox = null;
private CheckBox latitudeCheckbox = null;
private CheckBox longitudeCheckbox = null;
private CheckBox velocityCheckbox = null;
private CheckPreferencesListener listener;
SharedPreferences pref;
Editor prefEditor;
String userAltitudePreference = "";
String userLatitudePreference = "";
String userLongitudePreference = "";
String userVelocityPreference = "";
String userAltitudeChoice = "";
String userLatitudeChoice = "";
String userLongitudeChoice = "";
String userVelocityChoice = "";
public interface CheckPreferencesListener
{
public void onSettingsSaved();
public void onCancel();
}
public CheckPreferences(Context context, CheckPreferencesListener l)
{
super(context);
shc = context;
this.listener = l;
this.setContentView(R.layout.custompreferences);
altitudeCheckBox = (CheckBox) findViewById(R.id.altitudeCheckbox);
latitudeCheckbox = (CheckBox) findViewById(R.id.latitudeCheckbox);
longitudeCheckbox = (CheckBox) findViewById(R.id.longitudeCheckbox);
velocityCheckbox = (CheckBox) findViewById(R.id.velocityCheckbox);
this.setCancelable(false);
this.setCanceledOnTouchOutside(false);
this.setTitle("Data View Settings");
pref = shc.getSharedPreferences("shared_prefs", 1);
prefEditor = pref.edit();
initOnClick();
}
private void initOnClick()
{
View.OnClickListener click = new View.OnClickListener()
{
public void onClick(View v)
{
switch (v.getId())
{
case R.id.saveBtn:
{
saveSettings();
listener.onSettingsSaved();
dismiss();
break;
}
case R.id.cancelBtn:
{
listener.onCancel();
dismiss();
break;
}
}
}
};
// Save Button
this.findViewById(R.id.saveBtn).setOnClickListener(click);
// Cancel Button
this.findViewById(R.id.cancelBtn).setOnClickListener(click);
}
// This function is called when the user chooses the save their preferences
public void saveSettings()
{
try
{
if (altitudeCheckBox.isChecked())
{
userAltitudeChoice = "true";
prefEditor.putString(userAltitudePreference, userAltitudeChoice);
prefEditor.commit();
}
else if (latitudeCheckbox.isChecked())
{
userLatitudeChoice = "true";
prefEditor.putString(userLatitudePreference, userLatitudeChoice);
prefEditor.commit();
}
else if (longitudeCheckbox.isChecked())
{
userLongitudeChoice = "true";
prefEditor.putString(userLongitudePreference, userLongitudeChoice);
prefEditor.commit();
}
else if (velocityCheckbox.isChecked())
{
userVelocityChoice = "true";
prefEditor.putString(userVelocityPreference, userVelocityChoice);
prefEditor.commit();
}
else
{
}
}
catch (NullPointerException npe)
{
if (npe.getMessage() != null)
{
Log.e("<tag>", npe.getMessage());
}
else
{
Log.e("<tag>", "Save Settings e.getMessage() was null");
}
}
}
}
The NullPointerException happens in the savedSettings function. All that function does is put data in the SharedPreferences. Any idea why that wold cause something to be null? Thanks in advance for any help given.
Update: Below is the error log.
05-09 17:25:28.835: E/<tag>(5447): Save Settings e.getMessage() was null
05-09 17:25:28.866: E/AndroidRuntime(5447): FATAL EXCEPTION: main
05-09 17:25:28.866: E/AndroidRuntime(5447): java.lang.NullPointerException
05-09 17:25:28.866: E/AndroidRuntime(5447): at shc_BalloonSat.namespace.CheckPreferences$1.onClick(CheckPreferences.java:59)
05-09 17:25:28.866: E/AndroidRuntime(5447): at android.view.View.performClick(View.java:2408)
05-09 17:25:28.866: E/AndroidRuntime(5447): at android.view.View$PerformClick.run(View.java:8816)
05-09 17:25:28.866: E/AndroidRuntime(5447): at android.os.Handler.handleCallback(Handler.java:587)
05-09 17:25:28.866: E/AndroidRuntime(5447): at android.os.Handler.dispatchMessage(Handler.java:92)
05-09 17:25:28.866: E/AndroidRuntime(5447): at android.os.Looper.loop(Looper.java:123)
05-09 17:25:28.866: E/AndroidRuntime(5447): at android.app.ActivityThread.main(ActivityThread.java:4627)
05-09 17:25:28.866: E/AndroidRuntime(5447): at java.lang.reflect.Method.invokeNative(Native Method)
05-09 17:25:28.866: E/AndroidRuntime(5447): at java.lang.reflect.Method.invoke(Method.java:521)
05-09 17:25:28.866: E/AndroidRuntime(5447): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
05-09 17:25:28.866: E/AndroidRuntime(5447): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
05-09 17:25:28.866: E/AndroidRuntime(5447): at dalvik.system.NativeStart.main(Native Method)
Below is my main class where the listener is instantiated.
package shc_BalloonSat.namespace;
import java.text.DecimalFormat;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.widget.TextView;
import android.widget.Toast;
public class Shc_BalloonSat_Activity extends Activity
{
int historyCountFromUser;
httpAPI api;
mapAPI map;
runDialog dialog;
CheckPreferences check;
DecimalFormat df = new DecimalFormat("##.#####");
DecimalFormat decimalf = new DecimalFormat("##.######");
AlertDialog alert;
SharedPreferences pref;
Editor prefEditor;
String lastpacketsPHP;
TextView historyTV;
TextView infoTV;
// User to determine how many packet the user would like to see.
int userDefinedCount = 5;
/*
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
loadApp();
}
public boolean isNetworkConnected(Context context)
{
ConnectivityManager connectionManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectionManager.getActiveNetworkInfo() != null && connectionManager.getActiveNetworkInfo().isAvailable() && connectionManager.getActiveNetworkInfo().isConnected())
{
return true;
}
else
{
return false;
}
}
public void showAlert()
{
alert.setTitle("Sorry!");
alert.setMessage("Please connect to the internet to access the full functionality of this app.");
alert.setButton("OK", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
}
});
alert.show();
}
public void loadApp()
{
setContentView(shc_BalloonSat.namespace.R.layout.main);
alert = new AlertDialog.Builder(this).create();
lastpacketsPHP = "";
pref = getSharedPreferences("shared_prefs", 1);
prefEditor = pref.edit();
//prefEditor.putString(lastpacketsPHP, "\* PHP file location goes here. */");
//prefEditor.commit();
// These next two lines are used to test the PHP files on the SHC server by determining if PHP is set up correctly.
prefEditor.putString(lastpacketsPHP, "\* PHP file location goes here. */");
prefEditor.commit();
if (!isNetworkConnected(this))
{
showAlert();
}
else
{
api = new httpAPI(this);
map = new mapAPI(this);
dialog = new runDialog(this, api, new runDialog.OnDataLoadedListener()
{
public void dataLoaded(String textViewString)
{
infoTV = (TextView)findViewById(shc_BalloonSat.namespace.R.id.info);
infoTV.setText(textViewString);
}
});
dialog.execute();
}
CheckPreferences cp = new CheckPreferences(this, new CheckPreferences.CheckPreferencesListener()
{
public void onSettingsSaved()
{
// This function let's the activity know that the user has saved their preferences and
// that the rest of the app should be now be shown.
check.saveSettings();
assignInfoToInfoTextView();
assignInfoToHistoryTextView();
}
public void onCancel()
{
Toast.makeText(getApplicationContext(), "Settings dialog cancelled", Toast.LENGTH_LONG).show();
}
});
cp.show();
}
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(shc_BalloonSat.namespace.R.menu.mainmenu, menu);
SubMenu submenu = menu.addSubMenu(0, Menu.FIRST, Menu.NONE, "Preferences");
submenu.add(0, 5, Menu.NONE, "Get Last 5 Packets");
submenu.add(0, 10, Menu.NONE, "Get Last 10 Packets");
submenu.add(0, 20, Menu.NONE, "Get Last 20 Packets");
inflater.inflate(shc_BalloonSat.namespace.R.menu.mainmenu, submenu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
// Handle item selection
switch (item.getItemId())
{
case shc_BalloonSat.namespace.R.id.viewKML:
viewKML();
return true;
case 5:
viewLast5Packets();
return true;
case 10:
viewLast10Packets();
return true;
case 20:
viewLast20Packets();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void assignInfoToInfoTextView()
{
try
{
String result = api.result.substring(1, api.result.length()-2);
JSONObject json_data = new JSONObject(result);
String infoText = "";
if (check.userAltitudePreference.equals("true"))
{
double altitudeData = json_data.getDouble("altitude");
double altitudeInFeet = altitudeData * 3.281;
infoText = "Last Known Altitude: " + df.format(altitudeInFeet) + " ft\n";
}
else if (check.userVelocityPreference.equals("true"))
{
Double speedData = json_data.optDouble("speed");
if (speedData.isNaN())
{
speedData = 0.00;
}
Double direction = json_data.optDouble("heading");
String directionUnits = " degrees from N";
String directionText = "";
if (direction == 0)
{
directionText = ", N";
}
else if (direction > 0 && direction < 45)
{
directionText = ", N NE";
}
else if (direction == 45)
{
directionText = ", NE";
}
else if (direction > 45 && direction < 90)
{
directionText = ", E NE";
}
else if (direction == 90)
{
directionText = ", E";
}
else if (direction > 90 && direction < 135)
{
directionText = ", E SE";
}
else if (direction == 135)
{
directionText = ", SE";
}
else if (direction > 135 && direction < 180)
{
directionText = ", S SE";
}
else if (direction == 180)
{
directionText = ", S";
}
else if (direction > 180 && direction < 225)
{
directionText = ", S SW";
}
else if (direction == 225)
{
directionText = ", SW";
}
else if (direction > 225 && direction < 270)
{
directionText = ", W SW";
}
else if (direction == 270)
{
directionText = ", W";
}
else if (direction > 270 && direction < 315)
{
directionText = ", W NW";
}
else if (direction == 315)
{
directionText = ", NW";
}
else if (direction > 315 && direction < 360)
{
directionText = ", N NW";
}
else if (direction.isNaN())
{
directionText = " Invalid direction";
}
else
{
}
infoText += "Last Known Velocity: " + df.format(speedData) + " m/s " + direction + directionUnits + directionText + "\n";
}
else if (check.userLatitudePreference.equals("true"))
{
double recentLatitudeData = json_data.getDouble("latitude");
infoText += "Last Known Latitude: " + df.format(recentLatitudeData) + "\n";
}
else if (check.userLongitudePreference.equals("true"))
{
double recentLongitudeData = json_data.getDouble("longitude");
infoText += "Last Known Longtitude: " + df.format(recentLongitudeData) + "\n";
}
infoTV.setText(infoText);
}
catch (JSONException e)
{
if (e.getMessage() != null)
{
Log.e("<tag>", e.getMessage());
}
else
{
Log.e("<tag>", "Last Known Textview 1 e.getMessage() was null");
}
Toast.makeText(this,"JSON Error in (Last Known) method!",Toast.LENGTH_SHORT).show();
}
catch (Exception e)
{
//broke here, not getting a message for some reason
if (e.getMessage() != null)
{
Log.e("<tag>", e.getMessage());
}
else
{
Log.e("<tag>", "Last Known Textview 2 e.getMessage() was null");
}
Toast.makeText(this,"Error in (Last Known) method!",Toast.LENGTH_SHORT).show();
}
}
public void assignInfoToHistoryTextView()
{
try
{
JSONArray jArray = new JSONArray(api.result);
historyTV = (TextView)findViewById(shc_BalloonSat.namespace.R.id.history);
for (int count = 1; count < userDefinedCount; count++)
{
JSONObject json_data = jArray.getJSONObject(count);
double altitudeData = json_data.getDouble("altitude");
double altitudeInFeet = altitudeData * 3.281;
String historyText = "Altitude: " + decimalf.format(altitudeInFeet) + " ft\n";
Double speedData = json_data.optDouble("speed");
if (speedData.isNaN())
{
speedData = 0.00;
}
Double direction = json_data.optDouble("heading");
String directionUnits = " degrees from N";
String directionText = "";
if (direction == 0)
{
directionText = ", N";
}
else if (direction > 0 && direction < 45)
{
directionText = ", N NE";
}
else if (direction == 45)
{
directionText = ", NE";
}
else if (direction > 45 && direction < 90)
{
directionText = ", E NE";
}
else if (direction == 90)
{
directionText = ", E";
}
else if (direction > 90 && direction < 135)
{
directionText = ", E SE";
}
else if (direction == 135)
{
directionText = ", SE";
}
else if (direction > 135 && direction < 180)
{
directionText = ", S SE";
}
else if (direction == 180)
{
directionText = ", S";
}
else if (direction > 180 && direction < 225)
{
directionText = ", S SW";
}
else if (direction == 225)
{
directionText = ", SW";
}
else if (direction > 225 && direction < 270)
{
directionText = ", W SW";
}
else if (direction == 270)
{
directionText = ", W";
}
else if (direction > 270 && direction < 315)
{
directionText = ", W NW";
}
else if (direction == 315)
{
directionText = ", NW";
}
else if (direction > 315 && direction < 360)
{
directionText = ", N NW";
}
else if (direction.isNaN())
{
directionText = " Invalid direction";
}
else
{
}
if (direction.isNaN())
{
historyText += "Velocity: " + df.format(speedData) + " m/s " + directionText + "\n";
}
else
{
historyText += "Velocity: " + df.format(speedData) + " m/s,\n" + direction + directionUnits + directionText + "\n";
}
double latitudeData = json_data.getDouble("latitude");
historyText += "Latitude: " + df.format(latitudeData) + "\n";
double longitudeData = json_data.getDouble("longitude");
historyText += "Longtitude: " + df.format(longitudeData) + "\n\n";
historyTV.setText(historyTV.getText().toString() + historyText);
}
}
catch (JSONException e)
{
if (e.getMessage() != null)
{
Log.e("log_tag", "Error parsing data: " + e.toString());
}
else
{
Log.e("<tag>", "History TextView 1 e.getMessage() was null");
}
}
catch(Exception e)
{
if (e.getMessage() != null)
{
Log.e("log_tag", "Error parsing data: " + e.toString());
}
else
{
Log.e("<tag>", "History TextView 2 e.getMessage() was null");
}
}
}
void viewLast5Packets()
{
if (!isNetworkConnected(this))
{
showAlert();
}
else
{
historyTV.setText("");
userDefinedCount = 5;
//prefEditor.putString(lastpacketsPHP, "\* PHP file location goes here. */");
//prefEditor.commit();
// These next two lines are used to test the PHP files on the SHC server by determining if PHP is set up correctly.
prefEditor.putString(lastpacketsPHP, "\* PHP file location goes here. */");
prefEditor.commit();
dialog = new runDialog(this, api, new runDialog.OnDataLoadedListener()
{
public void dataLoaded(String textViewString)
{
TextView infoTV = (TextView)findViewById(shc_BalloonSat.namespace.R.id.info);
infoTV.setText(textViewString);
assignInfoToInfoTextView();
assignInfoToHistoryTextView();
}
});
dialog.execute();
}
}
void viewLast10Packets()
{
if (!isNetworkConnected(this))
{
showAlert();
}
else
{
historyTV.setText("");
userDefinedCount = 10;
//prefEditor.putString(lastpacketsPHP, "\* PHP file location goes here. */");
//prefEditor.commit();
// These next two lines are used to test the PHP files on the SHC server by determining if PHP is set up correctly.
prefEditor.putString(lastpacketsPHP, \* PHP file location goes here. */");
prefEditor.commit();
dialog = new runDialog(this, api, new runDialog.OnDataLoadedListener()
{
public void dataLoaded(String textViewString)
{
TextView infoTV = (TextView)findViewById(shc_BalloonSat.namespace.R.id.info);
infoTV.setText(textViewString);
assignInfoToInfoTextView();
assignInfoToHistoryTextView();
}
});
dialog.execute();
}
}
void viewLast20Packets()
{
if (!isNetworkConnected(this))
{
showAlert();
}
else
{
historyTV.setText("");
userDefinedCount = 20;
//prefEditor.putString(lastpacketsPHP, "\* PHP file location goes here. */");
//prefEditor.commit();
// These next two lines are used to test the PHP files on the SHC server by determining if PHP is set up correctly.
prefEditor.putString(lastpacketsPHP, "\* PHP file location goes here. */");
prefEditor.commit();
dialog = new runDialog(this, api, new runDialog.OnDataLoadedListener()
{
public void dataLoaded(String textViewString)
{
TextView infoTV = (TextView)findViewById(shc_BalloonSat.namespace.R.id.info);
infoTV.setText(textViewString);
assignInfoToInfoTextView();
assignInfoToHistoryTextView();
}
});
dialog.execute();
}
}
public void viewKML()
{
if (!isNetworkConnected(this))
{
showAlert();
}
else
{
map.openKML();
}
}
}
My thought is that
private CheckBox altitudeCheckBox = (CheckBox) findViewById(R.id.altitudeCheckbox);
private CheckBox latitudeCheckbox = (CheckBox) findViewById(R.id.latitudeCheckbox);
private CheckBox longitudeCheckbox = (CheckBox) findViewById(R.id.longitudeCheckbox);
private CheckBox velocityCheckbox = (CheckBox) findViewById(R.id.velocityCheckbox);
are called BEFORE your dialog's constructor so they are null because findViewById doesn't find the layout views. Try to change them in:
private CheckBox altitudeCheckBox = null;
private CheckBox latitudeCheckbox = null;
private CheckBox longitudeCheckbox = null;
private CheckBox velocityCheckbox = null;
and then add the following just after the this.setContentView(R.layout.custompreferences) line in your constructor:
altitudeCheckBox = (CheckBox) findViewById(R.id.altitudeCheckbox);
latitudeCheckbox = (CheckBox) findViewById(R.id.latitudeCheckbox);
longitudeCheckbox = (CheckBox) findViewById(R.id.longitudeCheckbox);
velocityCheckbox = (CheckBox) findViewById(R.id.velocityCheckbox);
Hope this helps...
The easiest to find the reason for your NullPointerException is to look at the debug logcat output. It will show you exactly the line where the problem occured.
Just look for the first line starting with your package name, that should be it.
ps you need to take the catch out of your code so the code crashes
Or post the log if you can't find it.
From a cursory inspection of your code, it seems like you never initialized listener, so when you do listener.onSettingsSaved(); it crashes.
EDIT:
Dude, you still NEVER set it:
public CheckPreferences(Context context, CheckPreferencesListener l)
{
super(context);
shc = context;
this.setContentView(R.layout.custompreferences);
this.setCancelable(false);
this.setCanceledOnTouchOutside(false);
this.setTitle("Data View Settings");
pref = shc.getSharedPreferences("shared_prefs", 1);
prefEditor = pref.edit();
initOnClick();
}
You're missing this.listener = l;!
So I had a thread that was taking care of some network stuff for the app behind the scenes, but for some reason the IDE kept thinking that I was updating the UI so it wouldn't stop crashing. I changed it over to an AsyncTask and now I get an Index Out of Range error. Before I had started working with threading the app worked fine, it just was really slow. I haven't changed any of the original code that worked I've just added to it with the threads. So I know that my original code isn't the issue it's the code that creates the AsyncTask or at least it should be that code. My code is shown below along with the errors:
Main Java file
package shc_BalloonSat.namespace;
import java.text.DecimalFormat;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.widget.TextView;
import android.widget.Toast;
public class Shc_BalloonSat_Activity extends Activity
{
int historyCountFromUser;
httpAPI api;
mapAPI map;
runDialog dialog;
DecimalFormat df = new DecimalFormat("##.#####");
DecimalFormat decimalf = new DecimalFormat("##.######");
AlertDialog alert;
SharedPreferences pref;
Editor prefEditor;
String lastpacketsPHP;
// User to determine how many packet the user would like to see.
int userDefinedCount = 5;
/*
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
loadApp();
}
public boolean isNetworkConnected(Context context)
{
ConnectivityManager connectionManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectionManager.getActiveNetworkInfo() != null && connectionManager.getActiveNetworkInfo().isAvailable() && connectionManager.getActiveNetworkInfo().isConnected())
{
return true;
}
else
{
return false;
}
}
public void showAlert()
{
alert.setTitle("Sorry!");
alert.setMessage("Please connect to the internet to access the full functionality of this app.");
alert.setButton("OK", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
}
});
alert.show();
}
/* public void onResume()
{
super.onResume();
loadApp();
}
public void onRestart()
{
super.onRestart();
loadApp();
}*/
public void loadApp()
{
setContentView(shc_BalloonSat.namespace.R.layout.main);
alert = new AlertDialog.Builder(this).create();
String returned = "";
lastpacketsPHP = "";
pref = getSharedPreferences("shared_prefs", 0);
prefEditor = pref.edit();
prefEditor.putString(lastpacketsPHP, "/* PHP file URL goes here */");
prefEditor.commit();
// These next two lines are used to test the PHP files on the SHC server by determining if PHP is set up correctly.
//prefEditor.putString(lastpacketsPHP, "/* PHP file URL goes here */");
//prefEditor.commit();
if (!isNetworkConnected(this))
{
showAlert();
}
else
{
api = new httpAPI(this);
map = new mapAPI(this);
dialog = new runDialog();
try
{
returned = api.getData();
}
catch (Exception e)
{
Toast.makeText(this, "Error: Unable to receive data at this time.", Toast.LENGTH_SHORT).show();
}
TextView infoTV = (TextView)this.findViewById(shc_BalloonSat.namespace.R.id.info);
infoTV.setText(returned);
assignInfoToInfoTextView();
assignInfoToHistoryTextView();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(shc_BalloonSat.namespace.R.menu.mainmenu, menu);
SubMenu submenu = menu.addSubMenu(0, Menu.FIRST, Menu.NONE, "Preferences");
submenu.add(0, 5, Menu.NONE, "Get Last 5 Packets");
submenu.add(0, 10, Menu.NONE, "Get Last 10 Packets");
submenu.add(0, 20, Menu.NONE, "Get Last 20 Packets");
inflater.inflate(shc_BalloonSat.namespace.R.menu.mainmenu, submenu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
// Handle item selection
switch (item.getItemId())
{
case shc_BalloonSat.namespace.R.id.viewKML:
viewKML();
return true;
case 5:
viewLast5Packets();
return true;
case 10:
viewLast10Packets();
return true;
case 20:
viewLast20Packets();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void assignInfoToInfoTextView()
{
TextView infoTV = (TextView)this.findViewById(shc_BalloonSat.namespace.R.id.info);
try
{
String result = api.result.substring(1, api.result.length()-2);
JSONObject json_data = new JSONObject(result);
double altitudeData = json_data.getDouble("altitude");
double altitudeInFeet = altitudeData * 3.281;
String infoText = "Last Known Altitude: " + df.format(altitudeInFeet) + " ft\n";
Double speedData = json_data.optDouble("speed");
if (speedData.isNaN())
{
speedData = 0.00;
}
Double direction = json_data.optDouble("heading");
String directionUnits = " degrees from N";
String directionText = "";
if (direction == 0)
{
directionText = ", N";
}
else if (direction > 0 && direction < 45)
{
directionText = ", N NE";
}
else if (direction == 45)
{
directionText = ", NE";
}
else if (direction > 45 && direction < 90)
{
directionText = ", E NE";
}
else if (direction == 90)
{
directionText = ", E";
}
else if (direction > 90 && direction < 135)
{
directionText = ", E SE";
}
else if (direction == 135)
{
directionText = ", SE";
}
else if (direction > 135 && direction < 180)
{
directionText = ", S SE";
}
else if (direction == 180)
{
directionText = ", S";
}
else if (direction > 180 && direction < 225)
{
directionText = ", S SW";
}
else if (direction == 225)
{
directionText = ", SW";
}
else if (direction > 225 && direction < 270)
{
directionText = ", W SW";
}
else if (direction == 270)
{
directionText = ", W";
}
else if (direction > 270 && direction < 315)
{
directionText = ", W NW";
}
else if (direction == 315)
{
directionText = ", NW";
}
else if (direction > 315 && direction < 360)
{
directionText = ", N NW";
}
else if (direction.isNaN())
{
directionText = " Invalid direction";
}
else
{
}
infoText += "Last Known Velocity: " + df.format(speedData) + " m/s " + direction + directionUnits + directionText + "\n";
double recentLatitudeData = json_data.getDouble("latitude");
infoText += "Last Known Latitude: " + df.format(recentLatitudeData) + "\n";
double recentLongitudeData = json_data.getDouble("longitude");
infoText += "Last Known Longtitude: " + df.format(recentLongitudeData) + "\n";
infoTV.setText(infoText);
}
catch (JSONException e)
{
// TODO Auto-generated catch block
Log.e("<tag>", e.getMessage());
Toast.makeText(this,"Error in (Last Known) method!",Toast.LENGTH_SHORT).show();
}
catch (Exception e)
{
Log.e("<tag>", e.getMessage());
Toast.makeText(this,"Error in (Last Known) method!",Toast.LENGTH_SHORT).show();
}
}
public void assignInfoToHistoryTextView()
{
try
{
JSONArray jArray = new JSONArray(api.result);
TextView historyTV = (TextView)this.findViewById(shc_BalloonSat.namespace.R.id.history);
for (int count = 1; count < userDefinedCount; count++)
{
JSONObject json_data = jArray.getJSONObject(count);
double altitudeData = json_data.getDouble("altitude");
double altitudeInFeet = altitudeData * 3.281;
String historyText = "Altitude: " + decimalf.format(altitudeInFeet) + " ft\n";
Double speedData = json_data.optDouble("speed");
if (speedData.isNaN())
{
speedData = 0.00;
}
Double direction = json_data.optDouble("heading");
String directionUnits = " degrees from N";
String directionText = "";
if (direction == 0)
{
directionText = ", N";
}
else if (direction > 0 && direction < 45)
{
directionText = ", N NE";
}
else if (direction == 45)
{
directionText = ", NE";
}
else if (direction > 45 && direction < 90)
{
directionText = ", E NE";
}
else if (direction == 90)
{
directionText = ", E";
}
else if (direction > 90 && direction < 135)
{
directionText = ", E SE";
}
else if (direction == 135)
{
directionText = ", SE";
}
else if (direction > 135 && direction < 180)
{
directionText = ", S SE";
}
else if (direction == 180)
{
directionText = ", S";
}
else if (direction > 180 && direction < 225)
{
directionText = ", S SW";
}
else if (direction == 225)
{
directionText = ", SW";
}
else if (direction > 225 && direction < 270)
{
directionText = ", W SW";
}
else if (direction == 270)
{
directionText = ", W";
}
else if (direction > 270 && direction < 315)
{
directionText = ", W NW";
}
else if (direction == 315)
{
directionText = ", NW";
}
else if (direction > 315 && direction < 360)
{
directionText = ", N NW";
}
else if (direction.isNaN())
{
directionText = " Invalid direction";
}
else
{
}
if (direction.isNaN())
{
historyText += "Velocity: " + df.format(speedData) + " m/s " + directionText + "\n";
}
else
{
historyText += "Velocity: " + df.format(speedData) + " m/s,\n" + direction + directionUnits + directionText + "\n";
}
double latitudeData = json_data.getDouble("latitude");
historyText += "Latitude: " + df.format(latitudeData) + "\n";
double longitudeData = json_data.getDouble("longitude");
historyText += "Longtitude: " + df.format(longitudeData) + "\n\n";
historyTV.setText(historyText);
}
}
catch (JSONException e)
{
// TODO Auto-generated catch block
Log.e("log_tag", "Error parsing data: " + e.toString());
}
catch(Exception e)
{
Log.e("log_tag", "Error parsing data: " + e.toString());
}
}
void viewLast5Packets()
{
if (!isNetworkConnected(this))
{
showAlert();
}
else
{
userDefinedCount = 5;
prefEditor.putString(lastpacketsPHP, "http://space.uah.edu/balloonsat_app/get_last_5_BS_packets.php");
prefEditor.commit();
assignInfoToInfoTextView();
assignInfoToHistoryTextView();
}
}
void viewLast10Packets()
{
if (!isNetworkConnected(this))
{
showAlert();
}
else
{
userDefinedCount = 10;
prefEditor.putString(lastpacketsPHP, "http://space.uah.edu/balloonsat_app/get_last_10_BS_packets.php");
prefEditor.commit();
assignInfoToInfoTextView();
assignInfoToHistoryTextView();
}
}
void viewLast20Packets()
{
if (!isNetworkConnected(this))
{
showAlert();
}
else
{
userDefinedCount = 20;
prefEditor.putString(lastpacketsPHP, "http://space.uah.edu/balloonsat_app/get_last_20_BS_packets.php");
prefEditor.commit();
assignInfoToInfoTextView();
assignInfoToHistoryTextView();
}
}
public void viewKML()
{
if (!isNetworkConnected(this))
{
showAlert();
}
else
{
map.openKML();
}
}
}
httpAPI.java
package shc_BalloonSat.namespace;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import android.util.Log;
public class httpAPI
{
Shc_BalloonSat_Activity shc;
HttpClient client;
ArrayList<NameValuePair> nameValuePairs;
HttpResponse response;
HttpEntity entity;
InputStream is;
String result = "";
HttpPost httppost;
public httpAPI(Shc_BalloonSat_Activity aContext)
{
shc = aContext;
}
public String getData() throws Exception
{
try
{
client = new DefaultHttpClient();
nameValuePairs = new ArrayList<NameValuePair>();
httppost = new HttpPost(shc.pref.getString(shc.lastpacketsPHP, shc.lastpacketsPHP.toString()));
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
response = client.execute(httppost);
entity = response.getEntity();
is = entity.getContent();
}
catch (Exception e)
{
Log.e("log_tag", "Error in HTTP connection: " + e.toString());
}
try
{
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"iso-8859-1"),8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null)
{
sb.append(line + "\n");
}
is.close();
result = sb.toString();
}
catch(Exception e)
{
Log.e("log_tag", "Error converting result: " + e.toString());
}
return result;
}
}
runDialog.java
package shc_BalloonSat.namespace;
import android.app.ProgressDialog;
import android.os.AsyncTask;
public class runDialog extends AsyncTask<String, Integer, String>
{
Shc_BalloonSat_Activity shc;
httpAPI api;
ProgressDialog progressDialog;
public void onPreExecute()
{
progressDialog = new ProgressDialog(shc);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.show();
}
#Override
protected String doInBackground(String... params)
{
for (int count = 0; count < 20; count++)
{
publishProgress(5);
try
{
api.getData();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
progressDialog.dismiss();
return null;
}
protected void onProgressUpdate(Integer...progress)
{
progressDialog.incrementProgressBy(progress[0]);
}
protected void onPostExecute()
{
}
}
Errors:
org.json.JSONException: Index 5 out of range [0..5)
I get this error each time it tries to get the data from the URL. I can also email the apk file to anyone who would like to see what it's actually doing. I also have reason to believe that what ever the cause of this is might also be causing my ProgressDialog to not show up. Any ideas on why I could be getting this error?
Turns out that I was passing an activity, not calling the ProgressDialog correctly, and set up my AsyncTask incorrectly. Thank goodness I found someone who had the time to sort debug my project for me.
I made a statement and if it is true it continues, I want to stop this "continue" and make another statement for example touchdown and touchup.
here is my code
private void updateRunning(float deltaTime) {
List<TouchEvent> touchEvents = game.getInput().getTouchEvents();
int len = touchEvents.size();
for (int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
if (event.type != TouchEvent.TOUCH_UP)
continue;
world.doodle.DOODLE_Y = 3;
touchPoint.set(event.x, event.y);
guiCam.touchToWorld(touchPoint);
if (OverlapTester.pointInRectangle(pauseBounds, touchPoint)) {
Assets.clicks();
state = GAME_PAUSED;
return;
}
}
world.update(deltaTime, game.getInput().getAccelX());
if (world.score != lastScore) {
lastScore = world.score;
scoreString = "" + lastScore;
}
if (world.state == World.WORLD_STATE_NEXT_LEVEL) {
state = GAME_LEVEL_END;
}
if (world.state == World.WORLD_STATE_GAME_OVER) {
state = GAME_OVER;
if (lastScore >= Settings.highscores[4])
scoreString = "new highscore: " + lastScore;
else
scoreString = "score: " + lastScore;
Settings.addScore(lastScore);
Settings.save(game.getFileIO());
}
}
Little confused by what you are asking, but perhaps an else if?
if (event.type == TouchEvent.TOUCH_UP) {
/* do something for TOUCH_UP event */
} else if (event.type == TouchEvent.TOUCH_DOWN) {
/* do something for TOUCH_DOWN event */
} else {
/* do something else */
}
You can't stop a continue after you execute it.
Try adding break; where you want it to stop.