1

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:

  1. Start MainActivity (not included, it only contains an activity with the default main layout, so that the applications receiver is registered)
  2. Switch on a bluetooth device (This has been paired earlier, so android knows about it)
  3. 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.

shellström
  • 1,417
  • 13
  • 29
  • 1
    When you start the Activity try to add Intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) to you intent. As the documentation says: If set, the activity will not be launched if it is already running at the top of the history stack. Hope this is what you are looking for! – Endre Börcsök Jun 02 '17 at 14:24
  • This question needed to be split up to be on point. I have added another question regarding bluetooth connection states with broadcastreceivers here: https://stackoverflow.com/questions/44367910/prevent-ondestroy-when-bluetooth-connection-state-changes – shellström Jun 05 '17 at 11:28

2 Answers2

1

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.

GRK
  • 204
  • 1
  • 6
  • This helped with not spawning new Activities, however, it does not help with preventing onDestroy being called when the bluetooth connection state changes. I'll accept the answer and post another one, because I've realised this question is composed of two. I'll edit this question as well, so it's on point. – shellström Jun 05 '17 at 10:48
0

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!

Jasurbek
  • 2,946
  • 3
  • 20
  • 37