I'm developing a network app, which is group oriented. The thing is that when I'm about to join a group, it first checks if the group is secure, and if so, it asks for user and password. Getting group security may take a couple of seconds so I spawn a new thread for the whole process. I'd like to pop up a Dialog in case the group requires security. I'm thinking it may have to do with background threads, they may not be able to pop up Dialogs... But the thing is that I need to check the group security in the background thread because it takes a bit time.
Hope anyone can come up with the solution or any way to ask user/pass only when the group is secure. Here's the background thread:
public void run() {
secInf = mGroupId.getSecurityInformation();
if (secInf.getAdmissionLevel() == CreateGroupDialog.PRIVATE_KEY_ACCESS) {
showUserPasswordDialog();
} else {
mService.joinGroup(mGroupId);
// Notifies handler to dismiss ProgresDialog and start activity
mHandler.sendMessage(Message.obtain(mHandler,
GroupsActivity.JOIN_SUCCESSFUL));
}
Where showUserPasswordDialog does (mActivity is the activity that spawned this thread):
private void showUserPasswordDialog() {
AlertDialog dialog;
// add this to your code
// This example shows how to add a custom layout to an AlertDialog
LayoutInflater factory = LayoutInflater.from(mActivity);
final View textEntryView = factory.inflate(
R.layout.alert_dialog_text_entry, null);
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setIcon(R.drawable.alert_dialog_icon);
builder.setTitle(R.string.ask_user_password);
builder.setView(textEntryView);
builder.setPositiveButton(R.string.ok_text,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
String userName = ((EditText) mActivity
.findViewById(R.id.username_edit_alert_dialog))
.getText().toString();
String password = ((EditText) mActivity
.findViewById(R.id.password_edit_alert_dialog))
.getText().toString();
Credentials cred = new CredentialsL1(userName, password);
mSmeppService.joinGroup(mGroupId, cred);
mHandler.sendMessage(Message.obtain(mHandler,
GroupsActivity.JOIN_SUCCESSFUL));
});
builder.setNegativeButton(R.string.cancel_text,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.cancel();
mHandler.sendMessage(Message.obtain(mHandler,
GroupsActivity.DISMISS_PROGRESS_DIALOG));
}
});
dialog = builder.create();
/* I found this somewhere, but didn't work either */
// Window window = dialog.getWindow();
// WindowManager.LayoutParams lp = window.getAttributes();
// lp.token = textEntryView.getWindowToken();
// lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
// window.setAttributes(lp);
// window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
dialog.show();
}
I'm getting this exception:
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): FATAL EXCEPTION: Thread-38
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): android.view.InflateException: Binary XML file line #17: Error inflating class android.widget.EditText
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.view.LayoutInflater.createView(LayoutInflater.java:513)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:563)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.view.LayoutInflater.rInflate(LayoutInflater.java:618)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.view.LayoutInflater.inflate(LayoutInflater.java:407)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.view.LayoutInflater.inflate(LayoutInflater.java:320)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.view.LayoutInflater.inflate(LayoutInflater.java:276)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at org.pfc.threads.GroupJoinerThread.showUserPasswordDialog(GroupJoinerThread.java:76)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at org.pfc.threads.GroupJoinerThread.run(GroupJoinerThread.java:52)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): Caused by: java.lang.reflect.InvocationTargetException
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.widget.EditText.<init>(EditText.java:51)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at java.lang.reflect.Constructor.constructNative(Native Method)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at java.lang.reflect.Constructor.newInstance(Constructor.java:446)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.view.LayoutInflater.createView(LayoutInflater.java:500)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): ... 8 more
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.os.Handler.<init>(Handler.java:121)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.text.method.MetaKeyKeyListener$MetaKeyDropbackHandler.<init>(MetaKeyKeyListener.java:605)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.text.method.MetaKeyKeyListener.<init>(MetaKeyKeyListener.java:96)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.text.method.BaseKeyListener.<init>(BaseKeyListener.java:25)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.text.method.TextKeyListener.<init>(TextKeyListener.java:66)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.text.method.TextKeyListener.getInstance(TextKeyListener.java:83)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.widget.TextView.<init>(TextView.java:806)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): at android.widget.EditText.<init>(EditText.java:55)
12-13 19:18:31.823: ERROR/AndroidRuntime(1702): ... 12 more
The XML layout file is:
<EditText
android:id="@+id/username_edit_alert_dialog"
android:enabled="false"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:scrollHorizontally="true"
android:autoText="false"
android:capitalize="none"
android:gravity="fill_horizontal"
android:textAppearance="?android:attr/textAppearanceMedium" />
<!-- Password -->
<TextView
android:id="@+id/password_view"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:text="@string/password_view_text"
android:gravity="left"
android:textAppearance="?android:attr/textAppearanceMedium" />
<EditText
android:id="@+id/password_edit_alert_dialog"
android:enabled="false"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:scrollHorizontally="true"
android:autoText="false"
android:capitalize="none"
android:gravity="fill_horizontal"
android:password="true"
android:textAppearance="?android:attr/textAppearanceMedium" />
You are correct that you cannot perform UI operations from a background thread. The way to do it is to use the Handler
that you have already implemented to popup the dialog. So something like:
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch(msg.what){
case GroupsActivity.DISMISS_PROGRESS_DIALOG:
//your existing dismiss code
break;
case GroupsActivity.CREATE_PROGRESS_DIALOG:
//create the dialog
break;
}
}
};
Then your run method would be:
public void run() {
secInf = mGroupId.getSecurityInformation();
if (secInf.getAdmissionLevel() == CreateGroupDialog.PRIVATE_KEY_ACCESS) {
// do stuff
mHandler.sendMessage(Message.obtain(mHandler,
GroupsActivity.CREATE_PROGRESS_DIALOG));
// do more stuff
} else {
mService.joinGroup(mGroupId);
// Notifies handler to dismiss ProgresDialog and start activity
mHandler.sendMessage(Message.obtain(mHandler,
GroupsActivity.JOIN_SUCCESSFUL));
}
}