I am creating a home automation app that has allows plugin views. I have been able to create a class as a sample plugin in a separate project (apk):
public class MyTestClass_IRDroidUIPlugIn extends Button implements IRDroidInterface{
Context mContext;
public MyTestClass_IRDroidUIPlugIn(Context context) {
super(context);
mContext = context;
setText("I was loaded dynamically! (1)");
setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// how do I show the dialog from here?
Activity.showDialog(1);
}}
);
}
public Dialog buildConfigDialog(int ID){
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setMessage("Click the Button...(1)")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
});
return builder.create();
}
}
I can load this class at run time and create an instance of it:
try {
final File filesDir = this.getFilesDir();
final File tmpDir = getDir("dex", 0);
final DexClassLoader classloader = new DexClassLoader( filesDir.getAbsolutePath()+"/testloadclass.apk",
tmpDir.getAbsolutePath(),
null, this.getClass().getClassLoader());
final Class<View> classToLoad =
(Class<View>) classloader.loadClass("com.strutton.android.testloadclass.MyTestClass_IRDroidUIPlugIn");
mybutton = (View) classToLoad.getDeclaredConstructor(Context.class).newInstance(this);
mybutton.setId(2);
main.addView((View)mybutton);
} catch (Exception e) {
e.printStackTrace();
}
setContentView(main);
}
protected Dialog onCreateDialog(int id) {
switch (id) {
case 1:
return ((IRDroidInterface) mybutton).buildConfigDialog(id);
}
return null;
}
I want the plugin to be able to show a configuration dialog. Is there a way I can pass the Activity object to this class so it can use .showDialog(ID). This would be ideal so that the dialog life cycle can be managed properly.
Thanks in advance.
Maybe I'm missing something, but why can't you do something like this?
public class MyTestClass_IRDroidUIPlugIn extends Button implements IRDroidInterface{
Activity mContext;
public MyTestClass_IRDroidUIPlugIn(Activity context) {
super(context);
mContext = context;
...
setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mContext.showDialog(1);
}}
);
.....
}
When you're creating this class, you're already passing Activity into this class - so just use it.
Related
I am trying to send some data from a DialogFragment to a TextView from a Fragment.
After inserting the data in the available input and pressing SAVE, the app crashes.
I assume there is something wrong with the IncomeDialogListener.
I would appreciate some hints where I did wrong.
This is the Dialog Class
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.layout_incomedialog, null);
builder.setView(view)
.setTitle("Add Income")
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setPositiveButton("Save", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
String Amount = enter_income_amount.getText().toString();
String Note = enter_income_note.getText().toString();
String Date = enter_income_date.getText().toString();
incomeDialogListener.addDetails(Amount, Note, Date);
}
});
enter_income_amount = view.findViewById(R.id.enter_income_amount);
enter_income_note = view.findViewById(R.id.enter_income_note);
enter_income_date = view.findViewById(R.id.enter_income_date);
return builder.create();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
incomeDialogListener = (IncomeDialogListener) getTargetFragment();
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + "Must Implement IncomeDialogListener");
}
}
public interface IncomeDialogListener {
void addDetails(String Amount, String Note, String Date);
}
This is the Fragment to which I want to send the data
public class IncomeFragment extends Fragment implements
IncomeDialog.IncomeDialogListener {
DatabaseHelper myDB;
Button btn_add_income;
TextView display_income;
public IncomeFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_placeholder2 , container, false);
display_income = (TextView) v.findViewById(R.id.display_income);
btn_add_income = (Button) v.findViewById(R.id.btn_add_income);
myDB = new DatabaseHelper(getActivity());
btn_add_income.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
openIncomeDialog();
}
});
return v;
}
private void openIncomeDialog() {
android.support.v4.app.FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
IncomeDialog incomeDialog = new IncomeDialog();
incomeDialog.show(fragmentTransaction, "income dialog" );
}
#Override
public void addDetails(String Amount, String Note, String Date) {
display_income.setText(Amount);
}
}
Here is my solution for you:
IncomeFragment.java
public static final int INCOME_DIALOG_FRAGMENT = 1; // Add this line
private void openIncomeDialog() {
android.support.v4.app.FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
IncomeDialog incomeDialog = new IncomeDialog();
incomeDialog.setTargetFragment(IncomeFragment.this, INCOME_DIALOG_FRAGMENT); // Add this line
incomeDialog.show(fragmentTransaction, "income dialog");
}
IncomeDialog.java
#Override
public void onClick(DialogInterface dialog, int which) {
String Amount = enter_income_amount.getText().toString();
String Note = enter_income_note.getText().toString();
String Date = enter_income_date.getText().toString();
IncomeDialogListener listener = (IncomeDialogListener) getTargetFragment();
listener.addDetails(Amount, Note, Date);
}
Update: There is no magic behind, when you open dialog from fragment, you passed itself to dialog by calling setTargetFragment. Then in the dialog you can refer to the fragment that opened it by calling getTargetFragment. Actually there are 2 solutions you can use.
IncomeFragment incomeFragment = (IncomeFragment) getTargetFragment();
incomeFragment.addDetails(Amount, Note, Date);
or
IncomeDialogListener listerner = (IncomeDialogListener) getTargetFragment();
listerner.addDetails(Amount, Note, Date);
I prefer to use the second one because the dialog don't need to know about specific fragment that opened it. This makes the dialog is usable. Imagine a situation, three days later, you would like to open the dialog from another fragment, in that case you don't need to modify the dialog again, just let the another fragment implements IncomeDialogListener. If you use the first one, you must go to the dialog and modify it to make sure it works for the another fragment.
How can I create a custom popup class that accepts a simple string message? Im new to Android and help with code will be appreciated.
When a button is pushed in the main layout, the popup must pop up on the screen.
Custom popup class
public class CustomPopup extends PopupWindow {
private String message;
private Double anchorX;
private Double anchorY;
PopupWindow popup;
public CustomPopup(String message) {
super();
this.message = message;
}
public void showPopup(Activity context) {
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
}
Main Class
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText messageTxt = (EditText) findViewById(R.id.messageTxt);
Button generateBtn = (Button) findViewById(R.id.generateBtn);
String message = messageTxt.getText().toString();
final CustomPopup popup = new CustomPopup(message);
generateBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
popup.showPopup();
}
});
}
}
You can change the following code any way you need. This is just an example of how you make and implement a custom DialogFragment.
He is the code I use. I find it quite flexible because you can create several similar dialogs for slightly different tasks. You will need to create a layout file - this gives you a great deal of flexibility on function and style.
My layout file is fragment_ok_cancel_dialog.
To satisfy your requirements just create your own layout file with all the elements in it you need (like your image).
In the Activity that calls the dialog you will need to implement the Listener.
implements OkCancelDialogFragment.OkCancelDialogListener
Another advantage is with my code you can change the title and the message to fit the needs of any Activity.
private void callMyDialog(){
//Customize the title and message as needed
String title = "This is my dialog title";
String mess = "This is my dialog message";
OkCancelDialogFragment dialog = OkCancelDialogFragment.newInstance(title, mess);
dialog.show(getFragmentManager(), "OkCancelDialogFragment2");
}
Now you need to implement the dialog callback in the Activity that calls the DialogFragment.
#Override
public void onFinishOkCancelDialog(boolean submit) {
if(submit){
// Do something positive
}
else{
// Do something negative
}
}
Now the code for the DialogFragment:
public class OkCancelDialogFragment extends DialogFragment {
private static final String ARG_TITLE = "title";
private static final String ARG_MESSAGE = "message";
Context context = null;
private String title;
private String message;
private boolean submitData = false;
private OkCancelDialogListener mListener;
public OkCancelDialogFragment() {
}
public static OkCancelDialogFragment newInstance(String title, String message) {
OkCancelDialogFragment fragment = new OkCancelDialogFragment();
Bundle args = new Bundle();
args.putString(ARG_TITLE, title);
args.putString(ARG_MESSAGE, message);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
title = getArguments().getString(ARG_TITLE);
message = getArguments().getString(ARG_MESSAGE);
}
}
#Override
public Dialog onCreateDialog(Bundle saveIntsanceState){
context = getActivity();
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View rootView = inflater.inflate(R.layout.fragment_ok_cancel_dialog, null, false);
final TextView titleView = (TextView)rootView.findViewById(R.id.tvTitle);
final TextView messView = (TextView)rootView.findViewById(R.id.tvMessage);
titleView.setText(title);
messView.setText(message);
builder.setView(rootView)
// .setTitle(title)
.setPositiveButton(R.string.ok_button_dialog_title, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
submitData = true;
if(mListener == null) mListener = (OkCancelDialogListener) context;
mListener.onFinishOkCancelDialog(submitData);
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
submitData = false;
if(mListener == null) mListener = (OkCancelDialogListener) context;
mListener.onFinishOkCancelDialog(submitData);
}
});
return builder.create();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
if(mListener == null) mListener = (OkCancelDialogListener) context;
}
catch (Exception ex){
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public interface OkCancelDialogListener {
void onFinishOkCancelDialog(boolean submit);
}
}
Please note that .setTitle(title) is valid for API 23 or higher (or maybe API 21 or higher?).
You can create your custom xml layout
and in the OnClickListener of the button you can put this :
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
alertLayout = inflater.inflate(R.layout.YOUR_CUSTOM_POPUP_LAYOUT, null);
final AlertDialog alert = new AlertDialog.Builder(this).create();
alert.setView(alertLayout);
TextView msg= alertLayout.findViewById(R.id.YOUR_TEXTVIEW_ID);
alert.show();
after that you can add another button in your popup and set a listener on it to dismiss the layout after the click.
Could you please help with the below:
I am trying to call the method deletePlayer inside the fragment PlayersActivityFragment from the alertdialog NameAlertDialogFragment.
The code is below:
public static class PlayersActivityFragment extends Fragment {
ArrayList<Player> arrayPlayers;
ListView listViewPlayers;
//PlayerAdapter adapter;
public PlayersActivityFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
arrayPlayers = new ArrayList<Player>();
View rootView = inflater.inflate(R.layout.fragment_activity_players, container, false);
Button buttonAddPlayer = (Button) rootView.findViewById(R.id.button_addplayers);
buttonAddPlayer.setOnClickListener(new View.OnClickListener(){
public void onClick(View view) {
arrayPlayers.add(new Player("Player", 0));
Player selectedPlayer = arrayPlayers.get(arrayPlayers.size()-1);
((PlayersActivity)getActivity()).showNameDialogFragment(selectedPlayer);
}
});
listViewPlayers = (ListView) rootView.findViewById(R.id.listView_playername);
return rootView;
}
public void deletePlayer(){
arrayPlayers.remove(arrayPlayers.size()-1);
}
}
void showNameDialogFragment(Player player) {
mDialog = NameAlertDialogFragment.newInstance(player);
mDialog.show(getFragmentManager(),"SCORE DIALOG");
}
// Class that creates the AlertDialog
public static class NameAlertDialogFragment extends DialogFragment {
static Player selectedPlayer;
public static NameAlertDialogFragment newInstance(Player player) {
selectedPlayer = player;
return new NameAlertDialogFragment();
}
// Build AlertDialog using AlertDialog.Builder
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Get the layout inflater
LayoutInflater inflater = getActivity().getLayoutInflater();
final View view = inflater.inflate(R.layout.alertdialog_name, null);
final EditText editTextName = (EditText) view.findViewById(R.id.edittext_name);
return new AlertDialog.Builder(getActivity())
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
.setView(view)
.setMessage("Enter Player's Name:")
//Set up Yes Button
.setPositiveButton("Done", new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, int id) {
mName = editTextName.getText().toString().trim();
selectedPlayer.setName(mName);
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
//PlayersActivityFragment playersActivityFragment = (PlayersActivityFragment) getFragmentManager().findFragmentById(R.id.container);
//playersActivityFragment.deletePlayer();
//((PlayersActivityFragment)getTargetFragment()).deletePlayer();
NameAlertDialogFragment.this.getDialog().cancel();
}
})
.create();
}
}
The two different ways I have tried to call the methods are commented out in the .setNegativeButton onClickListener:
PlayersActivityFragment playersActivityFragment = (PlayersActivityFragment) getFragmentManager().findFragmentById(R.id.container);
playersActivityFragment.deletePlayer();
and
((PlayersActivityFragment)getTargetFragment()).deletePlayer();
Thank you!
First of all, why are all of your classes static? Anyway, here's an answer that should work...
Try using an interface as a callback. For example:
First create an interface.
public interface NameAlertDialogListener {
public void onNegativeClick();
}
Then have PlayersFragment implement NameAlertDialogListener.
public static class PlayersActivityFragment extends Fragment implements NameAlertDialogListener
Next, in the PlayersFragment, create a method called onNegativeClick.
#Override
public void onNegativeClick() {
//delete or whatever you want to do.
}
Create a member variable for the listener:
static Player selectedPlayer;
static NameAlertDialogListener mCallBack;
Next create a method in the dialog fragment called setListener.
public void setListener(NameAlertDialogListener callback) {
try {
mCallBack = callback;
} catch (ClassCastException e){
throw new ClassCastException(callback.toString() + " must implement NameAlertDialogListener" );
}
}
Then, when you create the dialog fragment call the setListener method.
void showNameDialogFragment(Player player) {
mDialog = NameAlertDialogFragment.newInstance(player);
mDialog.setListener(this);
mDialog.show(getFragmentManager(),"SCORE DIALOG");
}
Lastly, in your negative click listener:
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mCallBack.onNegativeClick() ;
NameAlertDialogFragment.this.getDialog().cancel();
}
})
I am not sure if this is the correct way of doing things, but I have come to a working solution.
First I moved ArrayList<Player> arrayPlayers; outside of the PlayersActivityFragment fragment.
Then I moved the method:
public void deletePlayer(){
arrayPlayers.remove(arrayPlayers.size()-1);
}
outside of the PlayersActivityFragment fragment.
I then called the deletePlayer() method inside the alertdialog with the line ((PlayersActivity)getActivity()).deletePlayer();.
Actually, I have a little hack, it's not really good, but it's easy to implement: declare PlayersActivityFragment variable in your DialogFragment. Then change your constructor to:
public static NameAlertDialogFragment newInstance(Player player,PlayersActivityFragment fragment ){
selectedPlayer = player;
NameAlertDialogFragment test = new NameAlertDialogFragment();
test.playerActivityFragment = fragment;
return test;
}
Then you can call playerActivityFragment.deletePlayer() everywhere in your DialogFragment.
P/s: The best way is implement interface, but for lazy coder like me, the method above is better lol!
I have used code below:
AlertDialog.Builder bld;
if (android.os.Build.VERSION.SDK_INT <= 10) {
//With default theme looks perfect:
bld = new AlertDialog.Builder(AndroidLauncher.this);
} else {
//With Holo theme appears the double Dialog:
bld = new AlertDialog.Builder(AndroidLauncher.this, android.R.style.Theme_Holo_Dialog_MinWidth);
}
bld.setIcon(R.drawable.ic_launcher);
bld.setTitle("Exit");
bld.setMessage("Are you sure you want to exit?");
bld.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); }
});
bld.setPositiveButton("Exit", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) { finish(); }
});
bld.setCancelable(false);
bld.create().show();
It seems fine, but it says "import android.app.AlertDialog cannot resolve".
It is a standard libGDX project in Android Studio.
In libgdx you should use a scene2d dialog instead of the native Android DialogInterface. Below is how you would add a completely skinned dialog to the stage in libgdx with custom button images and background image. You would just need to substitute your own background and button image textures and font, then call quitGameConfirm() when you're ready to display the dialog...
import com.badlogic.gdx.scenes.scene2d.ui.Dialog;
public void quitGameConfirm() {
LabelStyle style = new LabelStyle(_fontChat, Color.WHITE);
Label label1 = new Label("Are you sure that you want to exit?", style);
label1.setAlignment(Align.center);
//style.font.setScale(1, -1);
style.fontColor = Color.WHITE;
Skin tileSkin = new Skin();
Texture tex = new Texture(myButtontexture);
tex.setFilter(TextureFilter.Linear, TextureFilter.Linear);
tileSkin.add("white", tex);
tileSkin.add("default", new BitmapFont());
TextButton.TextButtonStyle textButtonStyle = new TextButton.TextButtonStyle();
textButtonStyle.up = tileSkin.newDrawable("white");
textButtonStyle.down = tileSkin.newDrawable("white", Color.DARK_GRAY);
textButtonStyle.checked = tileSkin.newDrawable("white",
Color.LIGHT_GRAY);
textButtonStyle.over = tileSkin.newDrawable("white", Color.LIGHT_GRAY);
textButtonStyle.font = _myTextBitmapFont;
textButtonStyle.font.setScale(1, -1);
textButtonStyle.fontColor = Color.WHITE;
tileSkin.add("default", textButtonStyle);
TextButton btnYes = new TextButton("Exit", tileSkin);
TextButton btnNo = new TextButton("Cancel", tileSkin);
// /////////////////
Skin skinDialog = new Skin(Gdx.files.internal("data/uiskin.json"));
final Dialog dialog = new Dialog("", skinDialog) {
#Override
public float getPrefWidth() {
// force dialog width
// return Gdx.graphics.getWidth() / 2;
return 700f;
}
#Override
public float getPrefHeight() {
// force dialog height
// return Gdx.graphics.getWidth() / 2;
return 400f;
}
};
dialog.setModal(true);
dialog.setMovable(false);
dialog.setResizable(false);
btnYes.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
// Do whatever here for exit button
_parent.changeState("StateMenu");
dialog.hide();
dialog.cancel();
dialog.remove();
return true;
}
});
btnNo.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
//Do whatever here for cancel
dialog.cancel();
dialog.hide();
return true;
}
});
TextureRegion myTex = new TextureRegion(_dialogBackgroundTextureRegion);
myTex.flip(false, true);
myTex.getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
Drawable drawable = new TextureRegionDrawable(myTex);
dialog.setBackground(drawable);
float btnSize = 80f;
Table t = new Table();
// t.debug();
dialog.getContentTable().add(label1).padTop(40f);
t.add(btnYes).width(btnSize).height(btnSize);
t.add(btnNo).width(btnSize).height(btnSize);
dialog.getButtonTable().add(t).center().padBottom(80f);
dialog.show(stage).setPosition(
(MyGame.VIRTUAL_WIDTH / 2) - (720 / 2),
(MyGame.VIRTUAL_HEIGHT) - (MyGame.VIRTUAL_HEIGHT - 40));
dialog.setName("quitDialog");
stage.addActor(dialog);
}
The problem is that you are trying to create an Android widget which I suspect you are doing it in the Libgdx-core implementation. The core implementation does not have any references to Android SDK.
That is because it is the Android project which inherits the core project. As a result the core project is not aware of any dependencies loaded to the Android implementation.
To overcome this you need to create an interface between Android project and Core Project. That will allow you to call methods inside the Android Project.
The interface must be created inside the core Project in order for both projects to have access to it.
For example you create CrossPlatformInterface.java inside core Project. But first let's create a callback to get feedback from the Ui Thread inside the Libgdx Thread. It is important to remember that Libgdx has a seperate thread that Android main thread!!! If you try to run Widgets of Android from Libgdx threads the Application will crush.
Let's make the callback for the AlertDialog. I will suggest an Abstract class here in order to be able to override only the methods you want because sometimes Alertdialog can have 1,2 or 3 buttons.
In Core Project create AlertDialogCallback.java:
public abstract class AlertDialogCallback{
public abstract void positiveButtonPressed();
public void negativeButtonPressed(){}; // This will not be required
public void cancelled(){}; // This will not be required
}
In Core Project also create CrossPlatformInterface.java:
public interface CrossPlatformInterface{
public void showAlertDialog(AlertDialogCallback callback);
}
You notice that in the showAlertDialog method we pass the callback to get feedback when buttons are pressed!
Then you create a Class inside Android project that will implement the CrossPlatformInterface like:
public ClassInsideAndroidProject implements CrossPlatFormInterface{
private AndroidLauncher mActivity; // This is the main android activity
public ClassInsideAndroidProject(AndroidLauncher mActivity){
this.mActivity = mActivity;
}
public void showAlertDialog(final AlertDialogCallback callback){
mainActivity.runOnUiThread(new Runnable(){
#Override
public void run() {
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setTitle("Test");
builder.setMessage("Testing");
builder.setPositiveButton("OKAY", new OnClickListener(){
#Override
public void onClick(DialogInterface dialog, int which) {
callback.positiveButtonPressed();
}
});
builder.setNegativeButton(negativeButtonString, new OnClickListener(){
#Override
public void onClick(DialogInterface dialog, int which) {
callback.negativeButtonPressed();
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
});
}
}
Important notes
The CrossPlatformInterface will be instantiated inside the MainActivity (AndroidLauncher) as you will see below.
The AlertDialog will be created inside the android UI thread. Because we are coming from the Libgdx thread to create the AlertDialog we need to use runOnUiThread to ensure the AlertDialog is created in ui thread.
Finally how to execute this:
Instantiate CrossPlatform interface inside Android main Activity and pass the Activity to the Interface instance which is passed inside the MyGdxGame:
public class MainActivity extends AndroidApplication {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration();
cfg.useGL20 = false;
initialize(new MyGdxGame(new ClassInsideAndroidProject(this)), cfg);
}
}
Finally when the MyGDxGame is created we get the instance of the crossplatform interface and we can the call any functions we want to the android ui thread.
public class MyGdxGame extends Game {
ClassInsideAndroidProject crossPlatformInterface;
public MyGdxGame(ClassInsideAndroidProject crossPlatformInterface){
this.crossPlatformInterface = crossPlatformInterface;
}
#Override
public void create() {
crossPlatformInterface.showAlertDialog(new AlertDialogCallback(){
#Override
public void positiveButtonPressed(){
//IMPORTANT TO RUN inside this method the callback from the ui thread because we want everything now to run on libgdx thread! this method ensures that.
Gdx.app.postRunnable(new Runnable().....)
}
#Override
public void negativeButtonPressed(){
}; // This will not be required
#Override
public void cancelled(){
}; // This will not be required
});
}
#Override
public void render() {
super.render();
}
public void dispose() {
super.dispose();
}
public void pause() {
super.pause();
}
}
I think it was much more writing I first intended. It might look daunting but actually is fairly simple. Well after you've done it everything looks simpler :).
The advantage of this effort is after you make this interface any call to android widget will be very easy and thread safe.
Hope it gives a good picture.
This works (tested). Simply pass in the FragmentActivity or Activity
via your game constructor. You have to pass something in (like
ClassInsideAndroidProject). Why not pass in a really useful element !.
//---------------------------------------------------------------------------
/** INSIDE the libgdc core, create a custom NATIVE android dialog
* :- breaks the rules somewhat for the core,
* but if you ONLY using Android, why not use android Native!
* #member_var private final FragmentActivity m_fa;
* #constructor public xx_your_app_xx(FragmentActivity m_fa)
*{
* this.m_fa = m_fa;
*}
* #called_with if(m_fa != null) showCustomDialog(m_fa);
* #param fa
*/
public static void showCustomDialog(final FragmentActivity fa) //or Activity
{
fa.runOnUiThread(new Runnable()
{
// boolean[] info;
#Override
public void run()
{
LinearLayout ll_Main = new LinearLayout(fa);
LinearLayout ll_Row01 = new LinearLayout(fa);
LinearLayout ll_Row02 = new LinearLayout(fa);
LinearLayout ll_Row09 = new LinearLayout(fa);
LinearLayout ll_Row10 = new LinearLayout(fa);
ll_Main.setOrientation(LinearLayout.VERTICAL);
ll_Row01.setOrientation(LinearLayout.HORIZONTAL);
ll_Row02.setOrientation(LinearLayout.HORIZONTAL);
ll_Row09.setOrientation(LinearLayout.HORIZONTAL);
ll_Row10.setOrientation(LinearLayout.HORIZONTAL);
final CheckBox checkBox = new CheckBox(fa);
final CheckBox cb_debug = new CheckBox(fa);
final EditText et_User = new EditText(fa);
final EditText et_Pass = new EditText(fa);
TextView tv_Check = new TextView(fa);
TextView tv_Debug = new TextView(fa);
TextView tv_User = new TextView(fa);
TextView tv_Pass = new TextView(fa);
tv_Check.setText("rotation lock: ");
tv_Debug.setText("debug: ");
tv_User.setText("Username: ");
tv_Pass.setText("Password: ");
ll_Row01.addView(tv_Check);
ll_Row01.addView(checkBox);
ll_Row02.addView(tv_Debug);
ll_Row02.addView(cb_debug);
ll_Row09.addView(tv_User);
ll_Row09.addView(et_User);
ll_Row10.addView(tv_Pass);
ll_Row10.addView(et_Pass);
ll_Main.addView(ll_Row01);
ll_Main.addView(ll_Row02);
// ll_Main.addView(ll_Row09);
// ll_Main.addView(ll_Row10);
AlertDialog.Builder alert = new AlertDialog.Builder(fa);//this.getActivity()
alert.setTitle("Camera settings");
alert.setView(ll_Main);
alert.setCancelable(false);
alert.setPositiveButton("Ok", new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
// info1[0] = checkBox.isChecked();
// info1[1] = cb_debug.isChecked();
// String user = et_User.getText().toString();
// String pass = et_Pass.getText().toString();
//do something with the data
Gdx.app.log("INFO", "**** positiveButtonPressed works here too! ***");
Toast.makeText(fa,
"checkBox: " + checkBox.isChecked() +
", cb_debug: " + cb_debug.isChecked(),
Toast.LENGTH_LONG).show();
//IMPORTANT TO RUN inside this {} means everything now run's on libgdx thread!.
Gdx.app.postRunnable( new Runnable()
{
public void run()
{
//do something with the data
Gdx.app.log("INFO", "**** positiveButtonPressed works here ****");
}//run
});//postRunnable
}//onClick
});//setPositiveButton
alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}//setPositiveButton
});//setNegativeButton
AlertDialog dialog = alert.create();
dialog.show();
}//run
});//runOnUiThread
}//showCustomDialog
//--------------------------------------------------------------------------------
Im at my wits end here. I have a Class which Implements the OnClickListener cous i need the same action on Buttons accros my Application. This used to work just fine. But since I added some functionality by getting some needed data from the app preferences. startActivity throws a null pointer exception.Here is the class:
//Imports
public class CallClickListener extends Activity implements View.OnClickListener {
protected AppPreferences appPrefs;
String contactPersonName;
String contactPersonTelephone;
String name;
public CallClickListener(Context context){
Log.d("TRACE", "init CallClick");
appPrefs = new AppPreferences(context);
try {
JSONObject object = appPrefs.getConsultantObject();
contactPersonName = object.getString("contactPersonName");
contactPersonTelephone = object.getString("contactPersonTelephone");
name = object.getString("name");
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onClick(View view) {
final View v = view;
AlertDialog.Builder alert = new AlertDialog.Builder(view.getContext());
alert.setTitle("Anrufen");
alert.setMessage("Kontakt für " + name + ", " + contactPersonName + " anrufen");
alert.setPositiveButton("Anrufen", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:"+contactPersonTelephone));
startActivity(callIntent);// this line throws the exception
}
});
alert.setNegativeButton("Abbrechen", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Toast.makeText(v.getContext(), "Abbruch", Toast.LENGTH_SHORT).show();
}
});
alert.show();
}
}
The Strings are all there from appPrefs, i also tried with hardcoding a phonenumber just incase. the Alert works fine, but as soon as i hit the positive Button, the app crashes
I add the Listener like this:
bCall.setOnClickListener(new CallClickListener(getApplicationContext()));
I added the necessary Call permissions.
I'm fairly new to Android dev, what am I missing?
Do this.... make the context object that you passed in the constructor into a field variable. and change startActivity to context.startActivity. It will work then.
EDIT: Highlighting the full solution.
bCall.setOnClickListener(new CallClickListener(getApplicationContext()));
should be changed to YourActivityClass.this instead of getApplicationContext.
Start Activity in the same task does not work with a context object that is not an Activity. So you need to either change the context to Activity or you start the activity in a new task. Also without calling startActivity on the context provided to your constructor you were getting the NPE because your CallClickListerner has no context.
Use activity context. Also check if you have initialized bCall. If you have not you will get NullPointerException.
bCall.setOnClickListener(ActivityName.this);
Also check this link to know when to use activity context and when to use application context
When to call activity context OR application context?
Edit:
Make sure you have added permission in manifest file
<uses-permission android:name="android.permission.CALL_PHONE" />
For reference use the below. My Class extends Activity
Button b= (Button) findViewById(R.id.button1);
b.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v1) {
// TODO Auto-generated method stub
final View v = v1;
AlertDialog.Builder alert = new AlertDialog.Builder(v.getContext());
alert.setTitle("Anrufen");
alert.setMessage("Kontakt für " );
alert.setPositiveButton("Anrufen", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:8095992052"));
startActivity(callIntent);// this line throws the exception
}
});
alert.setNegativeButton("Abbrechen", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Toast.makeText(v.getContext(), "Abbruch", Toast.LENGTH_SHORT).show();
}
});
alert.show();
}
});