How does "this" in work in Java when calling a method? - java

How does this.C() work exactly? method C is non-static so it has to be called via an instance. this refers to the current object but what object and how is it created? I didn't explicitly create an object so does compiling implicitly creates one behind the scene?...
public class MainActivity extends Activity {
public static final String EXTRA_MESSAGE = "com.example.myfirstapp.MESSAGE";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.C();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if(id ==R.id.action_search){
this.openSearch();
}
else if (id == R.id.action_settings) {
// openSettings();
}
return super.onOptionsItemSelected(item);
}
public void sendMessage(View view){
Intent intent = new Intent(this, DisplayMessageActivity.class);
EditText editText = (EditText) findViewById(R.id.edit_message);
String message= editText.getText().toString();
intent.putExtra(EXTRA_MESSAGE, message);
startActivity(intent);
}
public void C(){
}
public void openSearch(){
getActionBar().hide();
}
}

For Android when you start an activity for the first time, the onCreate is called by the Android OS. This is where you should write code to initialize all your views and variable. In the case of this.c() -or just c() - Android will automatically call it since it is in the onCreate method. Also since c() is part of the class, the instance of the class at run time is referred by "this" implicitly.
I recommend understanding life cycle of android apps. it will help you with developing good apps. http://developer.android.com/training/basics/activity-lifecycle/starting.html

I hope I understand your question correctly.
In the onCreate method you already have a this pointer, it is given when it is called. It is not sure where the caller gets it from, and it does not matter anyway.

Related

Can we get onClickListener functionality of buttons defined in Layout from onOptionsItemSelected() click

This is the most simplified version in which I could entitled my question.
Problem: Suppose two buttons are working fine with their functionality in an Activity. What i want is that if we click an item from menu items, how could we (if possible) integrate it one of the the buttons onClickListener so that it would perform the same functionality as one of those buttons are performing already.
Scenario: Lets assume we have two buttons in XML files implementing onCLickListener interface. While clicking the menu item, the inner class constructor has been initialized but it doesn't go further which makes sense. Can anyone guide me what are the options here to work with the logic I want to embed here.
cameraButton.setOnClickListener(new MyButtonClickListener(MyConstants.OPEN_CAMERA));
mediaButton.setOnClickListener(new MyButtonClickListener(MyConstants.OPEN_MEDIA));
where MyButtonClickListener is an inner class
private class MyButtonClickListener implements View.OnClickListener {
private int preference;
public MyButtonClickListener(int preference) {
this.preference = preference;
Log.e("Pref in Constructor:", String.valueOf(preference));
}
public MyButtonClickListener() {
}
#Override
public void onClick(View v) {
Log.e("Pref in onClick:", String.valueOf(preference));
startScan(preference);
}
}
and the method startScan is :
public void startScan(int preference) {
Log.e("Pref: StartScan Method:", String.valueOf(preference));
Intent intent = new Intent(this, MyActivity.class);
intent.putExtra(MyConstants.OPEN_INTENT_PREFERENCE, preference);
startActivityForResult(intent, REQUEST_CODE);
}
onOptionsItemSelected:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.gallery:
new MyButtonClickListener(MyConstants.OPEN_MEDIA);
//return true;
As your startScan() method belongs to your activity/fragment class, you can call it directly in the required case block of your onOptionsItemSelected() method. You do not need to go via an interface for that.

Force menu to inflate before custom method call

This issue is giving me nightmares :(
Please consider the following code snippet:
MainActivity.java
public class MainActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
mMenu = menu;
return super.onPrepareOptionsMenu(menu);
}
protected void updateActionBar() {
mMenu.findItem(R.id.action_some).setVisible(state); <---------------- mMenu is null
}
}
BaseActivity.java
public abstract class BaseActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
updateActionBar(); <---------------- This causes crash
}
protected abstract void updateActionBar();
}
Problem
After debugging, I found that dynamic dispatch of updateActionBar() happens before call to onPrepareOptionsMenu(). Hence, Menuinflation is kind of delayed and mMenu is not updated.
Question
How do I make sure that mMenu gets populated before call to updateActionBar()?
Note: For sake of focusing on the core problem and improve readability, I have pasted only portion of code that causes crash.
How do I make sure that mMenu gets populated before call to updateActionBar()?
Call updateActionBar() sometime later, such as in an override of onPrepareOptionsMenu() in BaseActivity.
updateActionBar() is being called inside a listener and moving the whole listener inside onresume doesn't make sense
Where the listener is does not matter. What event you are listening for matters. Apparently, you are using a listener for some event that can occur before onPrepareOptionsMenu(). That will not work. Either:
Choose a different event, or
Do not register the listener until at least onPrepareOptionsMenu() has been called, or
If the event is triggered, and onPrepareOptionsMenu() has not been called, instead of actually doing your work, set a boolean value (e.g., needToDoMenuStuff) to true, and then do that work in onPrepareOptionsMenu()
Something like this might serve you well:
final LinearLayout layout = (LinearLayout) findViewById(R.id.yourLayout); // might not be linearLayout
ViewTreeObserver vto = layout.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Code to be executed after layout complete. (updateActionBar)
}
});

Make the extra action bar icon go away

I've managed to work around an issue in which an extra icon appears in the action bar after a fragment transaction, but it was a clumsy solution and I wonder if there is a better way to solve it.
I have a fragment class hierarchy in which ContentFragment is an abstract fragment which occupies the whole screen (except for the action bar) and its subclasses contribute with additional action bar icons (by means of their respective onCreateOptionsMenu() and onOptionsItemSelected() methods). E.g. ContentFragmentA contributes with icon A, ContentFragmentB with icon B, ContentFragmentB may or may not be a child class of ContentFragmentA (if it is, then the action bar will contain both icon A and icon B side by side), and so on.
Initially (after the user has just logged in) the screen contains only ContentFragmentA and the action bar has icon A. As the user navigates through the app other content fragments (or more precisely fragment transactions) are added to the back stack and icons are correspondingly added or removed from the action bar.
It all behaves nicely until the user decides to log out which prompts the app to clear the whole back stack (bringing ContentFragmentA back after the oldest transaction is rolled back) and immediately add a LoginContentFragment, which contributes with a New Profile icon to the action bar. However at this moment icon A is also being shown beside the New Profile icon and I don't want it to be shown; that is the issue I'm facing. It should go away when the user logs out.
I solved the issue by clearing the back stack as usual and then including an extra transaction which replaces the ContentFragmentA with a blank, icon-less content fragment with setHasOptionsMenu(false), so icon A will be gone when the blank fragment is replaced with the Login fragment. But I find this clumsy and think there might be a better way.
I have tried out calling Menu.clear() in the ContentFragment superclass and Activity.supportInvalidateOptionsMenu() in the fragment replacement step but neither seems to work. Menu.clear() in particular will just make all the icons go away leaving none in the action bar.
Does anyone know an alternative?
Relevant code:
ContentFragment.java:
public abstract class ContentFragment extends Fragment {
public static interface Callbacks {
public abstract void setCurrentContentFragment(ContentFragment contentFragment);
}
protected Callbacks mCallbacks;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public void onStart() {
super.onStart();
mCallbacks = (Callbacks)getActivity();
mCallbacks.setCurrentContentFragment(this);
}
#Override
public void onStop() {
super.onStop();
mCallbacks = null;
}
public boolean handleBackPressed() {
return false;
}
}
ContentFragmentA.java:
public abstract class ContentFragmentA extends ContentFragment {
protected abstract void handleIconATouched();
...
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (menu.findItem(R.id.icon_a) == null) {
inflater.inflate(R.menu.icon_a, menu);
}
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.icon_a) {
handleIconATouched();
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
}
BlankFragment.java:
public class BlankFragment extends LoggedInContentFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(false);
}
}
In MainActivity.java:
...
private void addContentFragmentNotAddingTransactionToBackStackIfCurrentFragment(ContentFragment fragment, boolean clearBackStack) {
// mCurrentContentFragment changes as the back stack is cleared, thus addToBackStack is calculated before the clearBackStack() step.
boolean addToBackStack = (false == clearBackStack && (mCurrentContentFragment != null && fragment.getClass() != mCurrentContentFragment.getClass()));
if (clearBackStack) {
clearBackStack();
}
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (addToBackStack) {
ft.addToBackStack(null);
}
ft.replace(R.id.container, fragment);
ft.commit();
getSupportFragmentManager().executePendingTransactions();
}
public void clearBackStack() {
while (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStackImmediate();
}
// This is the step I would like to avoid
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.container, new BlankFragment());
ft.commit();
getSupportFragmentManager().executePendingTransactions();
}
...
I solved the issue. It was a problem with the fragment transaction back stack. I was mixing transactions which were added to the back stack with ones that weren't, and these were causing the back stack popping to work in an unexpected way and prevent fragments from being correctly removed so they were adding extra icons to the action bar. The issue is explained in this SO question. The solution I adopted is this one.

Android app for Wifi automation

I want to develop an android app that triggers Wifi..
When we open the app, if our Wifi is on, it will toast Connected Message Else, a button having a text connect will be displayed and
when you click that button, button text will change to connected and your Wifi is turned on.
I have done this .. but
my sir asked me to introduce such change that once we press button it changes from connect to connected and Wifi is on..
Now, if we manually turn off the Wifi in our setting and then we open our paused app, then the button will show connect option again.
I want to introduce automation in my app. My sir gave me hint that there is some helper class in android which keep on calling the method or some event handler that handles the event that occurs outside the app, but I still have no idea how to do that.
Please help me, thanks!
Here is the java code of my app:
public class MainActivity extends ActionBarActivity implements OnClickListener {
WifiManager wf;
static Button buttn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttn = (Button) findViewById(R.id.button);
buttn.setOnClickListener(this);
wf = (WifiManager) getSystemService(Context.WIFI_SERVICE);
}
public void onClick(View v) {
if (v == buttn) {
wf.setWifiEnabled(true);
buttn.setText("connected");
Toast.makeText(this, "Wifi Connected", Toast.LENGTH_LONG).show();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
// noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
I'd take a look at an Activity's Life cycle here
I believe you'll be interested in the "onResume" and able to detect if WiFi is still connected there.

Trouble with inserting JavaScript statement

I am currently creating an android application.
The aim of the application is to fire off some injected JavaScript code into the WebView that I have created within my android application. The problem that I am having is that the action that has been injected doesn't work.
You should also be aware that the html page that I am loading has been created locally. aim of the application is to have a toast message display.The code listing is below:
//uses javascript that is in the local HTML file
public class MainActivity extends Activity {
final String URL = "file:///android_asset/index.html";
private WebView myWebView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
myWebView.addJavascriptInterface(new WebAppInterface(this), "Android");
String test= "test";
String javascript ="javascript:document.addEventListener('click', function(){Android.showToast(toast)})";
myWebView.loadUrl(javascript);
myWebView.loadUrl(URL);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public class WebAppInterface {
Context mContext;
/** Instantiate the interface and set the context */
WebAppInterface(Context c) {
mContext = c;
}
/** Show a toast from the web page */
#JavascriptInterface
public void showToast(String toast) {
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
}
}
}
There are several issues with your implementation.
The toast variable might not be defined in the scope of your callback. Also, you are missing two semicolons, one right after calling the showToast method, and the other one at the end of the addEventListener method which results in a syntax error.
Finally, you are loading the Javascript code before loading the page, which gets it invalidated. You should be injecting your javascript code after the webpage has finished loading such as in the below example.
I've taken your example and rewrote it in a custom app to make sure it was working. Here's what I've written :
Android :
// Registering the JS interface
mWebView.addJavascriptInterface(new JSInterface(), "JSInterface");
// Waiting for the page to be fully loaded, and injecting the JS code
mWebView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
#Override
public void onPageFinished(WebView view, String url) {
view.loadUrl(INJECTED_CODE);
}
});
// Load your custom url
mWebView.loadUrl(YOUR_ACTUAL_URL);
/**
* Interface between Java and Javascript.
*/
public class JSInterface {
#JavascriptInterface
public void showToast(String toast) {
Log.d("debug", "I got : " + toast);
}
}
Injected javascript :
public static final String INJECTED_CODE = "javascript:"
+ "(function() { "
+ "document.addEventListener('click', function(){JSInterface.showToast('Hello Android !');});"
+ "})()";
You are attempting to call that JS code before loading your html page.
I would guess that the document object is not ready.
Change your event listener and bind to the window
From:
String javascript ="javascript:document.addEventListener('click', function(){Android.showToast(toast)})";
To:
String javascript ="javascript:window.addEventListener('click', function(){Android.showToast(toast)})";
And you should get a result. If not what is logcat spitting out when you click?
EDIT: Just saw Halim Qarroums onPageFinished solution, that is probably a better one in your case.

Categories