Prevent a new Activity from spawning when a Bluetooth device connects - android

Goals
If a bluetooth device connects, and no Activity is running, start Activity
If a bluetooth device connects, and an Activity is already running, connect to the already running Activity
Problem
As soon as a device connects, a new Activity starts. I have not been able to make the app reuse the same Activity.
What I have managed to solve
If a bluetooth device connects, and no Activity is running, start Activity
The problem manifests itself in the use of BroadCastReceivers which in turn starts the Activity using intents. For some reason the Activity keep running through its lifecycle, spawning up new windows, when a new device connects.
I've tested this solely on a Nexus 6P with Android N. I have no idea yet what kind of implications this implementation means for any other devices yet. But I at least need to get this working on one device.
Manifest
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".BtActivity" />
<receiver android:name=".BtConnectionBroadcastReceiver" android:priority="100000">
<intent-filter>
<action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED" />
<action android:name="android.intent.action.MEDIA_BUTTON" />
<action android:name="android.media.VOLUME_CHANGED_ACTION" />
</intent-filter>
</receiver>
</application>
BtConnectionBroadcastReceiver
public class BtConnectionBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "BT";
public static final String BROADCAST_ACTION_CONNECTED = "CONNECTED";
public static final String BROADCAST_ACTION_DISCONNECTED = "DISCONNECTED";
SharedPreferences mSharedPreferences;
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
// Get the BluetoothDevice object from the Intent
Log.d(TAG, "DEVICE CONNECTED");
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d("DEVICE NAME", device.getName());
Log.d("DEVICE ADDRESS", device.getAddress());
Intent i = new Intent(context, BtActivity.class);
context.startActivity(i);
} else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
Log.d(TAG, "DEVICE DISCONNECTED");
intent = new Intent();
intent.setAction(BtConnectionBroadcastReceiver.BROADCAST_ACTION_DISCONNECTED);
context.sendBroadcast(intent);
}
}
BtActivity
public class BtActivity extends AppCompatActivity {
private static final String TAG = "BT";
Window mWindow;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bt);
Log.d(TAG, "onCreate");
IntentFilter filter = new IntentFilter(BtConnectionBroadcastReceiver.INTENT_FILTER);
filter.addAction(BtConnectionBroadcastReceiver.BROADCAST_ACTION_CONNECTED);
filter.addAction(BtConnectionBroadcastReceiver.BROADCAST_ACTION_DISCONNECTED);
//registerReceiver(mReceiver, filter);
mWindow = getWindow();
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
//params.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF;
params.screenBrightness = 0.2f;
mWindow.setAttributes(params);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
mWindow.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE);
}
#Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
}
When I run this code, I get the following chain:
Start MainActivity (not included, it only contains an activity with the default main layout, so that the applications receiver is registered)
Switch on a bluetooth device (This has been paired earlier, so android knows about it)
Wait until it connects and get this:
DEVICE CONNECTED
onCreate
onResume
I can't grasp why the activity is restarting at this point. The activity is already running, the BroadcastReceiver only sends a broadcast to an already running activity. I can't figure out why there's a reason for the Activity to kill itself and then restart again.

Try by setting launch mode to the activity which is being started.
android:launchMode="singleTop"
This delivers the intent to the same activity instance if this activity is currently the top most activity in that task and onNewIntent() method of the activity will be invoked instead of onCreate(). And manage the functionality by passing intent extras. If this activity is not the top most activity in its task or if there is no activity running at all, then new instance of activity will be created and onCreate() followed by onResume() will be invoked.
Other launch modes like "singleTask"/"singleInstance" also can be used based on the need.
Hope this helps.

I had the same issue - something was calling onDestroy upon Bluetooth connection state changed (of the barcode scanner). I have fallowed author`s other post (as mentioned) and it was solved: https://stackoverflow.com/a/52165268/12762397
Posting this to speed up solution for someone else in the future.
It is necessary to add
<activity
...
android:configChanges="keyboard|keyboardHidden"/>
Works like a charm!

Related

Android studio - When connected to a bluetooth device, the onDestroy is called [duplicate]

Goal
If an already connected bluetooth device disconnects, and an Activity is already running, close the Activity
Problem
When the bluetooth device connection state changes through BluetoothAdapterProperties: CONNECTION_STATE_CHANGE, it seems like a new Activity is created or the current one restarts.
There is nothing in the code that listens and/or should react to bluetooth connection state changes.
The problem manifests itself in the use of BroadcastReceivers which in turn starts the Activity using intents. For some reason the Activity keep running through its lifecycle, spawning up new windows, even if the only change in bluetooth connectivity is BluetoothAdapterProperties: CONNECTION_STATE_CHANGE
I've tested this solely on a Nexus 6P with Android N. I have no idea yet what kind of implications this implementation means for any other devices yet. But I at least need to get this working on one device.
UPDATE
I have done a fair bit of experimentation and found that if I don't register the BroadcastReceiver in AndroidManifest, the problem with onDestroy being called disappears. But, I want to be able to react to Bluetooth connecting devices, so that I can launch my activity and then process input. If the activity gets destroyed every time a new device connects/disconnects, this won't work at all. What's the reasoning for having the BroadcastReceiver finishing an activity if it's already running and can I control that behaviour?
UPDATE 2
I can also conclude that disabling the statically declared BroadcastReceiver using this method https://stackoverflow.com/a/6529365/975641 doesn't improve things. As soon as the Manifest-BroadcastReceiver catches the ACL_CONNECTED intent from Android, and start my custom activity, it will ruthlessly call onDestroy on it when the connection state changes (which is usually just before an ACL_DISCONNECTED). It does not matter if I have ACL_DISCONNECTED declared in the Manifest or not. As long as I have my receiver listening for ACL_CONNECTED intents and I launch my Activity based on that, onDestroy will be called when the connection state changes. So frustrating.
Manifest
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".BtActivity"
android:launchMode="singleTop" />
<receiver android:name=".BtConnectionBroadcastReceiver" android:priority="100000">
<intent-filter>
<action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED" />
<action android:name="android.intent.action.MEDIA_BUTTON" />
<action android:name="android.media.VOLUME_CHANGED_ACTION" />
</intent-filter>
</receiver>
</application>
BtConnectionBroadcastReceiver
public class BtConnectionBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "BT";
public static final String BROADCAST_ACTION_CONNECTED = "CONNECTED";
public static final String BROADCAST_ACTION_DISCONNECTED = "DISCONNECTED";
SharedPreferences mSharedPreferences;
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
// Get the BluetoothDevice object from the Intent
Log.d(TAG, "DEVICE CONNECTED");
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d("DEVICE NAME", device.getName());
Log.d("DEVICE ADDRESS", device.getAddress());
Intent i = new Intent(context, BtActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(i);
} else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
Log.d(TAG, "DEVICE DISCONNECTED");
intent = new Intent();
intent.setAction(BtConnectionBroadcastReceiver.BROADCAST_ACTION_DISCONNECTED);
context.sendBroadcast(intent);
}
}
BtActivity
public class BtActivity extends AppCompatActivity {
private static final String TAG = "BT";
Window mWindow;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bt);
Log.d(TAG, "onCreate");
IntentFilter filter = new IntentFilter(BtConnectionBroadcastReceiver.INTENT_FILTER);
filter.addAction(BtConnectionBroadcastReceiver.BROADCAST_ACTION_CONNECTED);
filter.addAction(BtConnectionBroadcastReceiver.BROADCAST_ACTION_DISCONNECTED);
//registerReceiver(mReceiver, filter);
mWindow = getWindow();
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
//params.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF;
params.screenBrightness = 0.2f;
mWindow.setAttributes(params);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
mWindow.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE);
}
#Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "BROADCAST RECEIVED IN ACTIVITY");
String mac;
if(intent.getAction().equals(BtConnectionBroadcastReceiver.BROADCAST_DEVICE_CONNECTED)) {
Log.d(TAG, "CONNECT BROADCAST RECEIVED");
mac = intent.getStringExtra("mac");
checkConnectedDevice(mac, true); // This adds a device to an internal list
Log.d(TAG, "Activity nr of devices:" +mNrOfDevices);
}
if(intent.getAction().equals(BtConnectionBroadcastReceiver.BROADCAST_DEVICE_DISCONNECTED)) {
Log.d(TAG, "DISCONNECT BROADCAST RECEIVED");
mac = intent.getStringExtra("mac");
checkConnectedDevice(mac, false); // This removes a device from an internal list
Log.d(TAG, "Activity nr of devices:" +mNrOfDevices);
if(mNrOfDevices < 1) {
Log.d(TAG, "No more connected devices");
finish();
}
}
abortBroadcast();
}
};
}
When I run this code, I get the following chain:
Start MainActivity (not included, it only contains an activity with the default main layout, so that the applications receiver is registered)
Switch on a bluetooth device (This has been paired earlier, so android knows about it)
Wait until it connects and get this:
DEVICE CONNECTED
onCreate
onResume
Switch off the bluetooth device and I then get this:
DEVICE DISCONNECTED
onDestroy
onCreate
onResume
I can't grasp why the activity is getting destroyed restarting at this point. The activity is already running, the BroadcastReceiver only sends a broadcast to an already running activity. I can't figure out why there's a reason for the Activity to kill itself and then restart again. This leaves me in a state of the Activity still running, but it is not the original Activity that was started.
I do however see something in the logcats that seem to have something to do with this, and it's in this sequencing;
06-02 15:45:09.156 26431 26431 D BT : DEVICE DISCONNECTED
06-02 15:45:09.213 19547 19547 D BluetoothAdapterService: handleMessage() - MESSAGE_PROFILE_CONNECTION_STATE_CHANGED
06-02 15:45:09.213 26431 26431 D BT : onDestroy
06-02 15:45:09.214 19547 19547 D BluetoothAdapterProperties: CONNECTION_STATE_CHANGE: FF:FF:20:00:C1:47: 2 -> 0
06-02 15:45:09.216 3502 3805 D CachedBluetoothDevice: onProfileStateChanged: profile HID newProfileState 0
06-02 15:45:09.237 414 414 W SurfaceFlinger: couldn't log to binary event log: overflow.
06-02 15:45:09.239 26431 26431 D BT : onCreate
06-02 15:45:09.243 26431 26431 D BT : onResume
In the AndroidManifest.xml add the following for the Activity, it is worked for me.
android:configChanges="keyboard|keyboardHidden"
Having read this https://developer.android.com/guide/components/broadcasts.html#effects_on_process_state I can probably safely conclude that the reason for why onDestroy gets called is because the receiver affects the process in which it is run, effectively meaning when the receiver has run its onReceive method, it will destroy itself and take the Activity with it.
I would of course have wished it was working differently, but I believe this is what effectively is going on and need to take another approach.
I know this answer is very late, but I was facing this issue with my activity tag. In Manifest file I have added below line for configChange.
android:configChanges="keyboard|orientation|screenSize|keyboardHidden|navigation|screenLayout"
Now my application does not kill itself.

Prevent onDestroy when Bluetooth connection state changes

Goal
If an already connected bluetooth device disconnects, and an Activity is already running, close the Activity
Problem
When the bluetooth device connection state changes through BluetoothAdapterProperties: CONNECTION_STATE_CHANGE, it seems like a new Activity is created or the current one restarts.
There is nothing in the code that listens and/or should react to bluetooth connection state changes.
The problem manifests itself in the use of BroadcastReceivers which in turn starts the Activity using intents. For some reason the Activity keep running through its lifecycle, spawning up new windows, even if the only change in bluetooth connectivity is BluetoothAdapterProperties: CONNECTION_STATE_CHANGE
I've tested this solely on a Nexus 6P with Android N. I have no idea yet what kind of implications this implementation means for any other devices yet. But I at least need to get this working on one device.
UPDATE
I have done a fair bit of experimentation and found that if I don't register the BroadcastReceiver in AndroidManifest, the problem with onDestroy being called disappears. But, I want to be able to react to Bluetooth connecting devices, so that I can launch my activity and then process input. If the activity gets destroyed every time a new device connects/disconnects, this won't work at all. What's the reasoning for having the BroadcastReceiver finishing an activity if it's already running and can I control that behaviour?
UPDATE 2
I can also conclude that disabling the statically declared BroadcastReceiver using this method https://stackoverflow.com/a/6529365/975641 doesn't improve things. As soon as the Manifest-BroadcastReceiver catches the ACL_CONNECTED intent from Android, and start my custom activity, it will ruthlessly call onDestroy on it when the connection state changes (which is usually just before an ACL_DISCONNECTED). It does not matter if I have ACL_DISCONNECTED declared in the Manifest or not. As long as I have my receiver listening for ACL_CONNECTED intents and I launch my Activity based on that, onDestroy will be called when the connection state changes. So frustrating.
Manifest
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".BtActivity"
android:launchMode="singleTop" />
<receiver android:name=".BtConnectionBroadcastReceiver" android:priority="100000">
<intent-filter>
<action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED" />
<action android:name="android.intent.action.MEDIA_BUTTON" />
<action android:name="android.media.VOLUME_CHANGED_ACTION" />
</intent-filter>
</receiver>
</application>
BtConnectionBroadcastReceiver
public class BtConnectionBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "BT";
public static final String BROADCAST_ACTION_CONNECTED = "CONNECTED";
public static final String BROADCAST_ACTION_DISCONNECTED = "DISCONNECTED";
SharedPreferences mSharedPreferences;
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
// Get the BluetoothDevice object from the Intent
Log.d(TAG, "DEVICE CONNECTED");
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d("DEVICE NAME", device.getName());
Log.d("DEVICE ADDRESS", device.getAddress());
Intent i = new Intent(context, BtActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(i);
} else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
Log.d(TAG, "DEVICE DISCONNECTED");
intent = new Intent();
intent.setAction(BtConnectionBroadcastReceiver.BROADCAST_ACTION_DISCONNECTED);
context.sendBroadcast(intent);
}
}
BtActivity
public class BtActivity extends AppCompatActivity {
private static final String TAG = "BT";
Window mWindow;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bt);
Log.d(TAG, "onCreate");
IntentFilter filter = new IntentFilter(BtConnectionBroadcastReceiver.INTENT_FILTER);
filter.addAction(BtConnectionBroadcastReceiver.BROADCAST_ACTION_CONNECTED);
filter.addAction(BtConnectionBroadcastReceiver.BROADCAST_ACTION_DISCONNECTED);
//registerReceiver(mReceiver, filter);
mWindow = getWindow();
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
//params.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF;
params.screenBrightness = 0.2f;
mWindow.setAttributes(params);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
mWindow.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE);
}
#Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "BROADCAST RECEIVED IN ACTIVITY");
String mac;
if(intent.getAction().equals(BtConnectionBroadcastReceiver.BROADCAST_DEVICE_CONNECTED)) {
Log.d(TAG, "CONNECT BROADCAST RECEIVED");
mac = intent.getStringExtra("mac");
checkConnectedDevice(mac, true); // This adds a device to an internal list
Log.d(TAG, "Activity nr of devices:" +mNrOfDevices);
}
if(intent.getAction().equals(BtConnectionBroadcastReceiver.BROADCAST_DEVICE_DISCONNECTED)) {
Log.d(TAG, "DISCONNECT BROADCAST RECEIVED");
mac = intent.getStringExtra("mac");
checkConnectedDevice(mac, false); // This removes a device from an internal list
Log.d(TAG, "Activity nr of devices:" +mNrOfDevices);
if(mNrOfDevices < 1) {
Log.d(TAG, "No more connected devices");
finish();
}
}
abortBroadcast();
}
};
}
When I run this code, I get the following chain:
Start MainActivity (not included, it only contains an activity with the default main layout, so that the applications receiver is registered)
Switch on a bluetooth device (This has been paired earlier, so android knows about it)
Wait until it connects and get this:
DEVICE CONNECTED
onCreate
onResume
Switch off the bluetooth device and I then get this:
DEVICE DISCONNECTED
onDestroy
onCreate
onResume
I can't grasp why the activity is getting destroyed restarting at this point. The activity is already running, the BroadcastReceiver only sends a broadcast to an already running activity. I can't figure out why there's a reason for the Activity to kill itself and then restart again. This leaves me in a state of the Activity still running, but it is not the original Activity that was started.
I do however see something in the logcats that seem to have something to do with this, and it's in this sequencing;
06-02 15:45:09.156 26431 26431 D BT : DEVICE DISCONNECTED
06-02 15:45:09.213 19547 19547 D BluetoothAdapterService: handleMessage() - MESSAGE_PROFILE_CONNECTION_STATE_CHANGED
06-02 15:45:09.213 26431 26431 D BT : onDestroy
06-02 15:45:09.214 19547 19547 D BluetoothAdapterProperties: CONNECTION_STATE_CHANGE: FF:FF:20:00:C1:47: 2 -> 0
06-02 15:45:09.216 3502 3805 D CachedBluetoothDevice: onProfileStateChanged: profile HID newProfileState 0
06-02 15:45:09.237 414 414 W SurfaceFlinger: couldn't log to binary event log: overflow.
06-02 15:45:09.239 26431 26431 D BT : onCreate
06-02 15:45:09.243 26431 26431 D BT : onResume
In the AndroidManifest.xml add the following for the Activity, it is worked for me.
android:configChanges="keyboard|keyboardHidden"
Having read this https://developer.android.com/guide/components/broadcasts.html#effects_on_process_state I can probably safely conclude that the reason for why onDestroy gets called is because the receiver affects the process in which it is run, effectively meaning when the receiver has run its onReceive method, it will destroy itself and take the Activity with it.
I would of course have wished it was working differently, but I believe this is what effectively is going on and need to take another approach.
I know this answer is very late, but I was facing this issue with my activity tag. In Manifest file I have added below line for configChange.
android:configChanges="keyboard|orientation|screenSize|keyboardHidden|navigation|screenLayout"
Now my application does not kill itself.

Android Custom Launcher startActivity() blocks BOOT_COMPLETED intent

I am currently working on a custom ROM (based on CyanogenMod 11.0), which aims to implement a custom "Kiosk Mode". To do this, I have three components in one application (with system privileges): The service, which handles modifications to the status/navigationbar and disables the power key. The receiver, which only starts the service after the BOOT_COMPLETED signal is received. The HomeIntentWrapper works as the launcher, and only starts one custom activity.
The problem I am currently facing is that the startActivity(...) command in the HomeIntentWrapper somehow blocks the system from booting any further, and the BOOT_COMPLETED intent is never sent.
I verifed this with the adb shell dumpsys activity command, which tells me:
mStartedUsers:
User #0: mState=BOOTING
It also does not show the BOOT_COMPLETED broadcast ever sent.
Now, if the user presses the Home-Button, the BOOT_COMPLETED intent is sent, and the mState switches to RUNNING.
If I do not start an activity in the HomeIntentWrapper, the intent is sent. What am I doing wrong here?
AndroidManifest.xml:
<manifest coreApp="true">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application android:allowBackup="true"
android:persistent="true" >
<service android:name="Service"
android:process=":service" >
</intent-filter>
</service>
<receiver android:name="Receiver"
android:process=":receiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<activity android:name="HomeIntentWrapper"
android:process=":launcher" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
Receiver:
public class Receiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context, Service.class));
}
}
HomeIntentWrapper:
public class HomeIntentWrapper extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startApp();
}
#Override
protected void onResume() {
super.onResume();
startApp();
}
private void startApp() {
SharedPreferences sharedPrefs = getSharedPreferences(getString(R.string.settings_file), Context.MODE_MULTI_PROCESS);
String customAppIntentString = sharedPrefs.getString(getString(R.string.settings_custom_intent), "");
if(customAppIntentString.equals("") == false) {
try {
Intent intent = Intent.getIntent(customAppIntentString);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} catch(java.net.URISyntaxException e) {
// Intentionally
}
}
}
}
Root cause: finishBooting() is not called because Home Activity is not on top of stack.
http://androidxref.com/4.4.4_r1/xref/frameworks/base/services/java/com/android/server/am/ActivityStackSupervisor.java
Line: 1811
Line: 1883-1886
Line: 1934-1940
Solution:
Do not call start Activity Until Boot_Completed is received.

In Android how do you register to receive headset plug broadcasts?

I am working in Android 2.1, and I want to detect when the headset is plugged in/taken out. I'm pretty new to android.
I think the way to do it is using a Broadcast receiver. I sublcassed this, and I also put the following in my AndroidManifest.xml. But do you have to register the receiver somehwere else, like in the activity? I'm aware there are lots of threads on this, but I don't really understand what they're talking about. Also, what's the difference between registering in AndroidManifest.xml versus registering dynamically in your activity?
<receiver android:enabled="true" android:name="AudioJackReceiver" >
<intent-filter>
<action android:name="android.intent.action.HEADSET_PLUG" >
</action>
</intent-filter>
</receiver>
And this was the implementation of the class (plus imports)
public class AudioJackReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.w("DEBUG", "headset state received");
}
}
I was just trying to see if it works, but nothing shows up when I unplug/plug in the headset while running the application.
EDIT: the documentation doesn't say this, but is it possible that this one won't work if registered in the manifest? I was able to get it to respond when I registered the receiver in one of my applications (or do you have to do that anyway?)
Just complementing Greg`s answer, here is the code that you need divided in two parts
Register the Service in the first Activity (here its called MainActivity.java).
Switch over the result of the ACTION_HEADSET_PLUG action in the BroadCastReceiver.
Here it goes:
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private MusicIntentReceiver myReceiver;
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myReceiver = new MusicIntentReceiver();
}
#Override public void onResume() {
IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
registerReceiver(myReceiver, filter);
super.onResume();
}
private class MusicIntentReceiver extends BroadcastReceiver {
#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.d(TAG, "Headset is unplugged");
break;
case 1:
Log.d(TAG, "Headset is plugged");
break;
default:
Log.d(TAG, "I have no idea what the headset state is");
}
}
}
}
Here are two sites that may help explain it in more detail:
http://www.grokkingandroid.com/android-tutorial-broadcastreceiver/
http://www.vogella.com/articles/AndroidBroadcastReceiver/article.html
You have to define your intent; otherwise it won't access the system function. The broadcast receiver; will alert your application of changes that you'd like to listen for.
Every receiver needs to be subclassed; it must include a onReceive(). To implement the onReceive() you'll need to create a method that will include two items: Context & Intent.
More then likely a service would be ideal; but you'll create a service and define your context through it. In the context; you'll define your intent.
An example:
context.startService
(new Intent(context, YourService.class));
Very basic example. However; your particular goal is to utilize a system-wide broadcast. You want your application to be notified of Intent.ACTION_HEADSET_PLUG.
How to subscribe through manifest:
<receiver
android:name="AudioJackReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.HEADSET_PLUG" />
</intent-filter>
</receiver>
Or you can simply define through your application; but. Your particular request; will require user permissions if you intend to detect Bluetooth MODIFY_AUDIO_SETTINGS.
You need to enable the broadcast receiver and set the exported attribute to true:
<receiver
android:name="AudioJackReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.HEADSET_PLUG" />
</intent-filter>
</receiver>

Not receiving a broadcast from my service to my Activity

I've looked at the numerous posts on sending messages from services to activities, but I just can't get this to work. My receiver is never being called. Does it matter if the Activity is not in the foreground?
Here is my scenario - I am running a web server as a service. When the web server receives a URL with link to a playlist, I need to send a message to my Activity that is acting as a media player. The media player will read the playlist and start playing the songs, videos, etc in order.
I don't want the service to start new instances of the media player activity, as sometimes it may be sending commands like fast forward or pause.
From my web server service:
private void sendMessage() {
Log.d("juice NonoHTTPD sender", "Sending message from Hub Web Server to Hub Player");
Intent intent = new Intent();
intent.putExtra(HubPlayer.ACTIVITY_PARAM_HOST, sAppHost);
intent.putExtra(HubPlayer.ACTIVITY_PARAM_PORT, sAppPort);
intent.putExtra(HubPlayer.ACTIVITY_PARAM_TYPE, sAppType);
intent.putExtra(HubPlayer.ACTIVITY_PARAM_URL, sAppPath);
intent.setAction("com.jigawattlabs.hubplayer.play");
appContext.sendBroadcast(intent);
}
From my activity:
public class HubPlayer extends Activity implements
OnBufferingUpdateListener, OnCompletionListener,
OnPreparedListener, OnVideoSizeChangedListener, SurfaceHolder.Callback
{
private IntentFilter intentFilter = new IntentFilter("com.jigawattlabs.hubplayer.play");
MyBroadcastReceiver mReceiver = new MyBroadcastReceiver();
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.mediaplayer_2);
registerReceiver(mReceiver , intentFilter);
}
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
extras = intent.getExtras();
sHost = extras.getString(ACTIVITY_PARAM_HOST);
sPort = extras.getString(ACTIVITY_PARAM_PORT);
sType = extras.getString(ACTIVITY_PARAM_TYPE);
sXMLURL = extras.getString(ACTIVITY_PARAM_URL);
DebugMsg("received broadcast in MyBroadcastReceiver.");
processInputRequest();
}
}
}
Even though it doesn't seem necessary to put this in the manifest since I'm registering the BroadcastReceiver within my code, I also added an intent filter:
<activity android:label="Media Hub Player" android:launchMode="singleTask"
android:screenOrientation="unspecified"
android:name=".HubPlayer" >
<intent-filter>
<action android:name="com.jigawattlabs.hubplayer.play" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Thanks in advance.
OK, I have a workaround for this, but I was hoping for something better. Here is what I did
If the service needs to send a message to an activity, it checks to see if the activity is running
If the activity is running, it does the sendBroadcast as above
If the activity is not running, I do a StartActivity and pass it the same info as I would with sendBroadcast
From the activity, I pick up the intent either from the registered BroadcastReceiver or from the intent passed on onResume (if StartActivity is called).
Here is the code to check if an activity is running
public boolean isHubRunning()
{
boolean isServiceFound = false;
ActivityManager activityManager = (ActivityManager)appContext.getSystemService (Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> services = activityManager.getRunningTasks(Integer.MAX_VALUE);
isServiceFound = false;
for (int i = 0; i < services.size(); i++)
{
//DebugMsg(services.get(i).topActivity.toString());
if (services.get(i).topActivity.toString().contains("juicemediahub.HubPlayer"))
{
isServiceFound = true;
}
}
DebugMsg("Hub Player Running = " + String.valueOf(isServiceFound));
return isServiceFound;
}
If you want to always get the intent, you should us a receiever. define it in your manifest and it will always capture your broadcast, activity running or not. something like:
<receiver android:name=".StartOnUser">
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT" />
<category android:name="android.intent.category.HOME" />
</intent-filter>
</receiver>

Categories

Resources