NavUtils navigateUpFromSameTask() and onSaveInstanceState() - java

I'm new to android development and I'm facing a problem.
I've got an activity, which contains a ListFragment. After the list item is tapped the new Activity starts. I decided to do a back Navigation in a child Activity with the Home button on the Menu bar.
if (NavUtils.getParentActivityName(getActivity()) != null){
((AppCompatActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
....
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
if (NavUtils.getParentActivityName(getActivity()) != null){
NavUtils.navigateUpFromSameTask(getActivity());
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
And I also need to save the integer value into the parent Activity. So I override the onSaveInstanceState()
#Override
protected void onSaveInstanceState(Bundle savedInstanceState){
super.onSaveInstanceState(savedInstanceState);
Log.d("ACTIVITY_STATE", " onSave");
savedInstanceState.putInt(TAG_INT_VAL_SAVED, intVal);
}
But when I tap on the back arrow at the child Activity and the onCreate method of the parent Activity is called, it appears that the parameter Bundle is null, up to the Log of the following code...
if (savedInstanceState != null){
intVal = savedInstanceState.getInt(TAG_INT_VAL_SAVED);
Log.d("ACTIVITY_STATE", " onSave getting data"); // Never appears at LogCat
} else {
// code to assign intVal as if the Activity was just started
}
I'm a little confused, so I'd like to ask you, guys, for a help. Could you please tell, what's going on, when the navigation happens? And what should I do, to keep the intVal saved? Thank you!
B.t.w. It works fine on the screen rotation - the value is successfully retrieved.
The manifest looks like:
<activity
android:name=".ListItemActivity"
android:label="#string/app_name" >
<meta-data android:name="android.support.PARENT_ACTIVITY"
android:value=".MyListActivity"/>
</activity>
<activity android:name=".MyListActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

Related

Launch-Activity onCreate() called again when start another Activity

My Android App launches with a simple Activity with shows a ProgressBar. Some data is initialized and then the MainActivity is loaded for the user to interact with.
The Problem:
When startActivity() in showMainActivity() is called, the onCreate() - Method in the InitActivity is called again. This causes the data to be initialized again and the MainActivity to be started twice. Strangely this does not end in an endless loop but happens only once.
Does anyone have an idea why startActivity() causes onCreate() of InitActivity to be called again?
Code:
public class InitActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
//GETS CALLED AGAIN AFTER showMainActivity()
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_init);
DatabaseInitTask databaseInitTask = new DatabaseInitTask(getApplicationContext(), new DatabaseInitTask.DatabaseInitCallback() {
#Override
public void onInitCompleted() {
showMainActivity();
});
}
});
databaseInitTask.execute("");
}
private void showMainActivity() {
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
}
Manifest:
<activity
android:name=".Ui.MainActivity"
android:label="#string/app_name"
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:host="mydomain.com"
android:scheme="https"/>
</intent-filter>
</activity>
<activity
android:name=".Ui.InitActivity"
android:label="#string/app_name"
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Have you made changes in the AndroidManifest file? If not, try this
<activity android:name="com.android.example.sleepcodelab.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Cut the intent-filter part from the "MainActivity" activity tag and paste it to the "InitActivity" activity tag.
I have found the solution in my case. I present my solution here in case someone has a similar problem.
The problem was that I called the following function in MainActivity:
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
This caused the complete activity stack (including InitActivity) to be recreated. savedInstanceState of InitActivity was null and I could not react to it.
The faulty program flow was as follows:
App starts with InitActivity -> Some data are initialized -> InitActivity starts MainAcitvity -> MainActivity calls setDefaultNightMode which results in InitActivity being restarted but savedInstanceState being null -> InitActivity starts MainAcitvity again
The reason why this didn't end in an infinite loop is that MainAcitvity checked if the night mode is already set correctly and called setDefaultNightMode only if it was necessary to change the setting.
How I solved it:
I moved setDefaultNightMode to InitActivity. This still causes onCreate to be called a second time in InitActivity but this time savedInstanceState is not null and I can use savedInstanceState to prevent MainActivity from being started a second time.
I don't fully understand why savedInstanceState is not null anymore but it works now.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_init);
if(savedInstanceState == null) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
} else
{
DatabaseInitTask databaseInitTask = new DatabaseInitTask(getApplicationContext(), new DatabaseInitTask.DatabaseInitCallback() {
#Override
public void onInitCompleted() {
showMainActivity();
}
});
}
});
databaseInitTask.execute("");
}
}

Back button throws NullPointerException [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 5 years ago.
I have a flow that goes MainActivity -> StoriesActivity -> StoryBodyActivity.
Each passes variables to the next using Intents.
I'm trying to implement a back button from StoryBodyActivity to StoriesActivity, but it's throwing a NullPointerException on authorID = extras.getInt("author_id"); in StoriesActivity.
I know what a NullPointer is. I just don't know how to fix this one! Could it be because it no longer has access to the data that was passed from the MainActivity?
Note: This works fine when clicking the back button on the device, just not the icon on the ActionBar.
Snippet from StoryBodyActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_story_body);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
Snippet from StoriesActivity:
private int authorID;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stories);
Bundle extras = getIntent().getExtras();
authorID = extras.getInt("author_id"); // NullPointerException here
final String authorName = extras.getString("author_name");
Log.i("click", Integer.toString(authorID));
}
Snippet from MainActivity:
Intent intent = new Intent(MainActivity.this, StoriesActivity.class);
intent.putExtra("author_id", author.getID());
intent.putExtra("author_name", author.getName());
startActivity(intent);
Snippet from AndroidManifest:
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity" android:theme="#style/AppTheme.NoActionBar" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".StoriesActivity"
android:theme="#style/AppTheme.NoActionBar" />
<activity
android:name=".StoryBodyActivity"
android:theme="#style/AppTheme"
android:parentActivityName="com.example.apple.bookshelf.StoriesActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.apple.bookshelf.StoriesActivity" />
</activity>
</application>
The problem you are facing is because of the line: 'android:parentActivityName="com.example.apple.bookshelf.StoriesActivity"'
When you are pressing the custom back button, it is creating a new instance of StoriesActivity. I'll suggest to remove:
android:parentActivityName="com.example.apple.bookshelf.StoriesActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.apple.bookshelf.StoriesActivity" />
And replace
NavUtils.navigateUpFromSameTask(this);
with
onBackPressed();
You have two solutions here:
First is to check if extras exists:
if(extras != null){
authorID = extras.getInt("author_id"); // NullPointerException here
final String authorName = extras.getString("author_name");
Log.i("click", Integer.toString(authorID));
}
Second solution is to use finish() instead of NavUtils.navigateUpFromSameTask(this); in StoryBodyActivity.
Hope it helps :)
To prevent this you can check if extra exists when coming to this activity.
if (getIntent().hasExtra("author_id")){
authorID = getIntent().getIntExtra("author_id",0);
}
try this way
SharedPreferences sharedPref1 = getSharedPreferences("alldetais", Context.MODE_PRIVATE);
SharedPreferences.Editor editor1 = sharedPref1.edit();
editor1.putString("author_id", author.getID());
editor1.putString("author_name", author.getName())
editor1.commit();
and get this way
SharedPreferences sharedPref1 = getApplicationContext().getSharedPreferences("alldetais", Context.MODE_PRIVATE);
author_id=sharedPref1.getString("author_id","");
I didn't see your code, but I can give an advice, you have to go with the flow of the activities, visualize it as follows,
Activity A -> activity B -> activity C
So now, if you want to pass data using intent from A to C, then you have to send the data from A to B, then receive it in B, then again pass the received data in from B to C and then use it. Also, i think the more correct way of getting data in B from A will be
Intent intent = getIntent();
intent.getExtras();

NFC app crashing on enableForegroundDispatch

I am trying to get NFC foreground dispatch to work in my app using the tutorials
here and here. From what I can deduce all other functions seem to be working a it is just the NFC forground dispatch system that isn't working.
In my onCreate, I check NFC exists and initialise pending Intents and filters and get the NFC adapter:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Set some stuff
mTV = (TextView) findViewById(R.id.textView);
mButt = (Button) findViewById(R.id.button);
//mNfcAd = NfcAdapter.getDefaultAdapter(this);
//Init elsewhere
getAdapter();
//Hide button until its needed
mButt.setVisibility(View.INVISIBLE);
//Make sure NFC actually exists.....
if (mNfcAd == null) {
//Not going to work without NFC
Toast.makeText(this, "This device does not support NFC\nGet used to the pen and paper for now :/", Toast.LENGTH_LONG).show();
finish();
return;
}
Intent nfcIntent = new Intent(this, getClass());
nfcIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
nfcPendingIntent =
PendingIntent.getActivity(this, 0, nfcIntent, 0);
IntentFilter tagIntentFilter =
new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
tagIntentFilter.addDataType("text/plain");
intentFiltersArray = new IntentFilter[]{tagIntentFilter};
}
catch (Throwable t) {
t.printStackTrace();
}
//Init the prefs
initPrefs();
}
public void initPrefs() {
prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
}
private NfcAdapter getAdapter(){
if(mNfcAd ==null){
NfcManager manager = (NfcManager) getSystemService(NFC_SERVICE);
mNfcAd = manager.getDefaultAdapter();
}
return mNfcAd;
}
I then go on to check NFC is enabled (if not display a button that shows NFC settings) as well as checking if the app is in its first run. At the end of onResume(), I (attempt to) enable Foreground Dispatch for the adapter.
protected void onResume() {
super.onResume();
//Define the mButt action
mButt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent dialogIntent = new Intent(Settings.ACTION_NFC_SETTINGS);
dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(dialogIntent);
}
});
//If NFC exists, check if its turned on
if (!mNfcAd.isEnabled()) {
Toast.makeText(this, "You have NFC, but you haven't enabled it...", Toast.LENGTH_LONG).show();
mTV.setText("NFC not enabled");
mButt.setVisibility(View.VISIBLE);
}
//Check if first run
if (prefs.getInt("firstTime", 1) == 1 ||
prefs.getString("fullName", null) == null ||
prefs.getString("house", null) == null) {
Intent intent = new Intent(this, First_Run.class);
startActivity(intent);
prefs.edit().putInt("firstTime", 0);
prefs.edit().putString("fullName", getIntent().getExtras().getString("fullName"));
prefs.edit().putString("house", getIntent().getExtras().getString("house"));
prefs.edit().apply();
}
getAdapter().enableForegroundDispatch(this, nfcPendingIntent, intentFiltersArray, null);
handleIntent(getIntent());
}
The error is:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.nfc.NfcAdapter.enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], java.lang.String[][])' on a null object reference
However, all the arguments seem to be initialised;
It is being called in onResume, so the activity context is created;
nfcPendingIntent and intentFiltersArray are initialised in onCreate;
I do not have a techlist so I use null as the final argument;
The adapter itself is initialised in getAdapter.
I am currently combing through other tutorials to see the different methods that can be used, however I would really like to get this fixed.
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<uses-feature
android:name="android.hardware.nfc"
android:required="true" />
<uses-permission android:name="android.permission.NFC" />
<application
android:fullBackupContent="false"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
<activity
android:name=".First_Run"
android:label="#string/title_activity_first__run">
</activity>
</application>
Try creating your NfcAdapter object like below.
NfcAdapter mNfcAdapter = NfcAdapter.getDefaultAdapter(this);

Maintaining service in an android application

I am quite new to android development and I have started making a music player. I have two activities. One is the main that you can see all the albums in the local storage and select one. Upon selecting one a second activity launches that displays the songs from that album. The user can tap on a song and the song plays. I have binded a service to that activity and the song is playing. However when the song starts playing I can not navigate to the previous activity.
I would like to be able to return to the main activity and be able to keep browsing albums and picking another song. I am not sure if I have to bind the service again and how to do it.
Here are some segments of the code.
Manifest file
<activity
android:name=".AlbumActivity"
android:label="#string/app_name"
android:launchMode="singleTop"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SongActivity"
android:screenOrientation="portrait">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="realm.chaos.mplayer.AlbumActivity" />
</activity>
<service android:name="realm.chaos.mplayer.MusicService"/>
And here is the part in the SongActivity that I define and use the musicService.
private ServiceConnection musicConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicBinder binder = (MusicBinder)service;
musicSrv = binder.getService();
musicSrv.setList(songList);
musicBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
musicBound = false;
}
};
#Override
protected void onStart() {
super.onStart();
if(playIntent == null) {
playIntent = new Intent(this, MusicService.class);
bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE);
}
}
I do not know if that is of help but to play songs I extend the default media controller of Android. I plan to change that in the future since it seems buggy and I am not happy with its functionality.
My code is based on this tutorial http://code.tutsplus.com/tutorials/create-a-music-player-on-android-song-playback--mobile-22778. My SongActivity is actually the activity described there and the AlbumActivity is a new activity that I use as parent.
*** Also I have read that you can either bind or start a service. I am not sure which one is suggested. I want to leave the music playing and do change views. Probably I will add more views that might be used to update the service.

Game doesn't Quit properly

I have an app that so far consists of two Activities:
The Main Menu Activity.
The Game Activity
The Main Menu Activity contains a button that starts the Game Activity with the following code:
public void onClick(View clickedButton)
{
switch(clickedButton.getId())
{
case R.id.buttonPlay:
Intent i = new Intent("apple.banana.BouncingBallActivity");
startActivity(i);
break;
}
When the user is done with the Game Activity, he presses the back button. This calls the onPause() method first, which pauses the animation thread of the game. It then calls the onStop() which calls finish() on the activity altogether. The user is returned to the Main Menu activity. The code is outlined below:
public class BouncingBallActivity extends Activity{
private BouncingBallView bouncingBallView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bouncingBallView = new BouncingBallView(this);
bouncingBallView.resume();
setContentView(bouncingBallView);
}
#Override
protected void onPause()
{
super.onPause();
bouncingBallView.pause();
}
#Override
protected void onResume()
{
super.onResume();
bouncingBallView.resume();
}
#Override
protected void onStop()
{
super.onStop();
this.finish();
}
}
The problem is that this only works if I launch the application from Eclipse.
When I click on the app icon, the game starts from the Game Activity. The main menu activity does not appear.
I am not clear about why this happens. It could be something to do with the manifest. I've pasted the relevant portions below:
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<activity
android:name=".BouncingBallActivity"
android:label="#string/app_name"
android:screenOrientation="landscape" >
<intent-filter>
<action android:name="apple.banana.BouncingBallActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".MainMenu"
android:label="#string/app_name"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
I'd really appreciate any help with this. Thanks.
The call to finish() does not belong in your onStop() method. If you want to end the game when the user presses back, place it in your onPause(). The reason the app picks up from the game activity on subsequent launches (through the Android launcher interface) is it never left there.
If you only want to end the game when the user presses the Back key, and not from other pauses, then you'll need to catch incoming keys and finish() if the key is Back.
You can do this, when you press your back button.
#Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if ((keyCode == KeyEvent.KEYCODE_BACK))
{
this.finish();
}
return super.onKeyDown(keyCode, event);
}
or
#Override
public void onBackPressed() {
this.finish();
}

Categories