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.
Related
I'm using Android Beacon Library to scan for iBeacons. I'm using the HM-10 BLE module as a iBeacon. My problem is when i used the Android Beacon Library Sample codes, Nothing happens at all.
As mentioned in the sample code for starting an App in the Background, i created a new java class named "Backgroud" and the MainActivity class.
I want my application to start when a Beacon is detected when the app is not opened. Or show a notification (Toast) when the app is open.
I'm want also to know, what do we put in the MainActivity class.
Any help will be appreciated.
This is my AndroidManifest.xml file :
<application
android:allowBackup="true"
android:name=".Background"
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:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
This is my MainActivity Java class :
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
}
}
This is my Background Java class :
public class Background extends Application implements BootstrapNotifier {
private static final String TAG = ".Background";
private RegionBootstrap regionBootstrap;
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "App started up");
BeaconManager beaconManager = BeaconManager.getInstanceForApplication(this);
// To detect proprietary beacons, you must add a line like below corresponding to your beacon
// type. Do a web search for "setBeaconLayout" to get the proper expression.
beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
// wake up the app when any beacon is seen (you can specify specific id filers in the parameters below)
Region region = new Region("com.example.myapp.boostrapRegion", null, null, null);
regionBootstrap = new RegionBootstrap(this, region);
}
#Override
public void didDetermineStateForRegion(int arg0, Region arg1) {
// Don't care
}
#Override
public void didEnterRegion(Region arg0) {
Log.d(TAG, "Got a didEnterRegion call");
// This call to disable will make it so the activity below only gets launched the first time a beacon is seen (until the next time the app is launched)
// if you want the Activity to launch every single time beacons come into view, remove this call.
//regionBootstrap.disable();
Intent intent = new Intent(this, MainActivity.class);
// IMPORTANT: in the AndroidManifest.xml definition of this activity, you must set android:launchMode="singleInstance" or you will get two instances
// created when a user launches the activity manually and it gets launched from here.
Toast.makeText(getApplicationContext(), "A Beacon is detected", Toast.LENGTH_LONG).show();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);
}
#Override
public void didExitRegion(Region arg0) {
// Don't care
}
}
This question already has an answer here:
Android: How do I temporarily handle an Intent in a different activity of my application?
(1 answer)
Closed 3 years ago.
I've got an android app with several activities. I want the current activity to handle the nfc discovered event, as the state of the app determines what i want to do with the tag
As can be seen in the attached code i've set up the intents on each activity and implemented the onResume, onPause and onNewIntent methods in each activty.
Yet, for some reason the MainActivty is the only one which gets called even though one of the other activities is the active one. Eg. it is the one with the current GUI.
You guys have any idea how to get the active activity to handle the NFC discovered?
Any help greatly appreciated :)
Here is the ApplicationManifest
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<activity
android:name=".ConfigureStableNamesActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED " />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="www.nxp.com"
android:pathPrefix="/products/identification_and_security/smart_label_and_tag_ics/ntag/series/NT3H1101_NT3H1201.html"
android:scheme="http" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<!-- <action android:name="android.nfc.action.ACTION_TAG_DISCOVERED"/>-->
<action android:name="android.nfc.action.NDEF_DISCOVERED " />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="www.nxp.com"
android:pathPrefix="/products/identification_and_security/smart_label_and_tag_ics/ntag/series/NT3H1101_NT3H1201.html"
android:scheme="http" />
</intent-filter>
</activity>
In each of my activities i have this code to handle the NFC discovered intent
#Override
protected void onResume() {
super.onResume();
ntagHandler.start();
}
#Override
protected void onPause() {
super.onPause();
ntagHandler.stop();
}
#Override
protected void onNewIntent(Intent intent) {
try {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
nfcHandler = new NearFieldCommunicationHandler(tag);
nfcHandler.connect();
// DO SOMETHING HERE
// dataModel.readStableNames(nfcHandler);
} catch(IOException e) {
Toast.makeText(this, "Caught exception: " + e.toString(), Toast.LENGTH_LONG).show();
}
}
You need to implement "The foreground dispatch system". it allows an activity to intercept an intent and claim priority over other activities. Please refer: https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc#java
First create pendingIntent globally in your each activities as below:
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
then take IntentFilter globally and init with null as below:
IntentFilter[] intentFiltersArray = null;
Write below code in onCreate() method:
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
ndef.addDataType("*/*"); /* YOU CAN TYPE HERE EITHER android.nfc.action.NDEF_DISCOVERED or
android.nfc.action.ACTION_TAG_DISCOVERED */
}
catch (IntentFilter.MalformedMimeTypeException e) {
throw new RuntimeException("fail", e);
}
IntentFilter[] intentFiltersArray = new IntentFilter[] {ndef, };
then take techListsArray globally as below:
String[][] techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
Finally write below code to enable and disable the foreground dispatch when the activity loses (onPause()) and regains (onResume()) focus. enableForegroundDispatch() must be called from the main thread and only when the activity is in the foreground (calling in onResume() guarantees this)
public void onPause() {
super.onPause();
nfcadapter.disableForegroundDispatch(this);
}
public void onResume() {
super.onResume();
nfcadapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);
}
public void onNewIntent(Intent intent) {
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//do something with tagFromIntent
}
So I have two different apps made, one sends a broadcast and another receives it and displays a toast. However, when I close the receiver app the broadcast is no longer received by the second app even though I defined the receiver in the manifest file.
The broadcast sender in the MainActivity of app1.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button b = (Button)findViewById(R.id.button2);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent();
i.setAction("com.example.ali.rrr");
i.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(i);
Log.e("Broadcast","sent");
}
});
}
App 2 broadcast receiver:
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
Toast.makeText(context, "Broadcast has been recieved!", Toast.LENGTH_SHORT).show();
Log.e("SUCCESS", "IN RECIEVER");
//throw new UnsupportedOperationException("Not yet implemented");
}
App 2s Manifest:
<?xml version="1.0" encoding="utf-8"?>
<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">
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.ali.rrr" />
</intent-filter>
</receiver>
<activity
android:name=".MainActivity"
android:label="#string/title_activity_main"
android:theme="#style/AppTheme.NoActionBar" />
<activity
android:name=".Main2Activity"
android:label="#string/title_activity_main2"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
After registering my BroadcastReceiver (BR) statically in the manifest, applying the proper intent filters, using JobIntentService (and registering it in the manifest) to handle the work that was called from my BR, I was still getting inconsistent results.
Once all of what I listed above has been done you should be able to send an ADB command to activate your broadcast and process the work in your service even if the app is closed. This was working for me some of the time, but not all of the time.
This article describes limitation to BRs.
"As of Android 3.1 the Android system excludes all receiver from receiving intents by default if the corresponding application has never been started by the user or if the user explicitly stopped the application via the Android menu" (AKA a user executes Force Stop)
When I start the app by debugging it, then swipe it closed on my device, my ADB command never activates my BR. However, after my debugging session is over, when I open up the app on my device and swipe it closed, I can activate my BR through ADB commands.
This occurs because when you debug an application, then manually swipe it closed on the device, Android considers this a Force Stop hence why my BR cannot be activated until I re-open the app on the device without debugging.
Scoured the internet for hours, and wasn't able to find my solution, so I thought I'd post it here just in case some poor unfortunate soul is encountering the same weird functionality I was.
Happy coding :)
First of all you need to use the Service for this functionality to work.
In the Activity you can start and stop the service by using the below codes.
// to start a service
Intent service = new Intent(context, MyBrodcastRecieverService.class);
context.startService(service);
// to Stop service
Intent service = new Intent(context, MyBrodcastRecieverService.class);
context.stopService(service);
Then you can use the below service
public class MyBrodcastRecieverService extends Service
{
private static BroadcastReceiver br_ScreenOffReceiver;
#Override
public IBinder onBind(Intent arg0)
{
return null;
}
#Override
public void onCreate()
{
registerScreenOffReceiver();
}
#Override
public void onDestroy()
{
unregisterReceiver(br__ScreenOffReceiver);
m_ScreenOffReceiver = null;
}
private void registerScreenOffReceiver()
{
br_ScreenOffReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
Log.d(TAG, "ACTION_SCREEN_OFF");
// do something, e.g. send Intent to main app
}
};
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
registerReceiver(br_ScreenOffReceiver, filter);
}
}
I faced this issue recently. The BroadcastReceiver was working fine even if the app was removed from the background in the emulator and Samsung phones. But it failed to start my app in Chinese manufactured phones like Realme, Mi etc. While struggling to find a way to fix this I found that in the app details page there is battery optimisation settings where the Auto-launch feature was disabled. After I enabled it the app was working fine and BroadcastReceiver was able to start the app. I was unable ti find a way to enable this setting programmatically but I found this question which helped me direct the user to that setting page.
You can go through below solution;
Activity.java
Intent intent=new Intent(MainActivity.this,BroadcastService.class);
startService(intent);
BroadcastService.java
public class BroadcastService extends Service {
private static MusicIntentReceiver br_ScreenOffReceiver;
#Override
public IBinder onBind(Intent arg0)
{
return null;
}
#Override
public void onCreate()
{
registerScreenOffReceiver();
}
#Override
public void onDestroy()
{
}
private void registerScreenOffReceiver()
{
br_ScreenOffReceiver = new MusicIntentReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
int state = intent.getIntExtra("state", -1);
switch (state) {
case 0:
Log.e("AAAAAAAAAA", "Headset is unplugged");
break;
case 1:
Log.e("AAAAAAAAA", "Headset is plugged");
break;
default:
Log.e("AAAAAAAAAAAA", "I have no idea what the headset state is");
}
}
}
};
IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
registerReceiver(br_ScreenOffReceiver, filter);
}
}
Menifest
<service android:enabled="true" android:name=".BroadcastService" />
Try this way..
Intent i = new Intent();
i.setAction("com.example.ali.rrr");
i.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
i.setComponent(
new ComponentName("PackageNameApp2","PackageNameApp2.MainActivity"));
sendBroadcast(i);
My Class NetworkListener should check, if Internet Connection is available or not. Then, It should show a Snackbar with the InternetState. For this, I'm using an interface.
My whole code:
[MainActivity.java]:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle(R.string.Inbox);
Initialize_NavigationDrawer();
Initialize_Objects();
//1. Here, I initialise my NetworkListener and register my Receiver
NetworkListener mNetworkListener = new NetworkListener();
mNetworkListener.registerReceiver(this);
}
#Override
public void ToggleSnackbar(boolean ShowHide) {
if (ShowHide) snbNoInternet.show();
else snbNoInternet.dismiss();
}
[NetworkListener.java]:
public class NetworkListener extends BroadcastReceiver {
private SnackbarInterface snbInterface;
public NetworkListener() {}
#Override
public void onReceive(Context context, Intent intent) {
//4. After toggling the Network-Connection, a NullReferenceException occurs
//5. System.out.println(snbInterface) shows: null
ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
if (ni == null || !ni.isConnected()) {
snbInterface.ToggleSnackbar(true);
}
else if (ni != null && ni.isConnected()) {
snbInterface.ToggleSnackbar(false);
}
}
public void registerReceiver(SnackbarInterface receiver) {
snbInterface = receiver; //2. Is called from onCreate
//3. System.out.println(snbInterface) shows: at.guger.email.activities.MainActivity#27472a5e
}
public interface SnackbarInterface {
public void ToggleSnackbar(boolean ShowHide);
}
}
[AndroidManifest.xml]:
<activity
android:name=".activities.MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activities.WriteActivity" />
<activity
android:name=".activities.ReadActivity" />
<receiver
android:name=".NetworkListener">
<intent-filter>
<action
android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
And the permissions:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
The code is called like the numbers are ordered!
I really can't explain, why snbInterface is set to null!
I hope you can help me!
You registered the BroadcastReceiver using the receiver element in the AndroidManifest. Android system creates the BroadcastReceiver instance and calls the onReceive method with the appropriate parameters.
If you want to use a BroadcastReceiver to manipulate UI of your application (like show/hide Snackbar ), you will have to register the receiver within your activity using the registerReceiver(BroadcastReceiver receiver,IntentFilter intentFilter) in the onCreate method and unRegister in the onDestroy method of your activity.
Remove the receiver element from the AndroidManifest.xml file and create the BroadcastReceiver through code.
This question here has a clear explanation on how BroadcastReceivers can be created and registered :
Broadcast Receiver class and registerReceiver method
In the onReceive method of the BroadcastReceiver, call the toggleSnackbar method in your Activity to toggle the Snackbar.
There is an other method for achieving what you want. It is discussed here:
Inform Activity from a BroadcastReceiver ONLY if it is in the foreground
Read more on BroadcastReceivers here
You can write the Receiver as a separate class. Instantiate the Receiver and make a call to registerReceiver with the first parameter as the Receiver and the second parameter as the IntentFilter.
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();
}