Creating a live broadcasting app in Java using Android Studio

This guide is based on Android Studio 3.1.3.

This guide focuses on the bare minimum required to start broadcasting. We're using Android Studio, which you can find on developer.android.com/studio. Make sure to familiarize yourself with the example apps in the SDK bundles for a more complete overview.

If you want to clone a sample project similar to this guide, see the Broadcaster example for Android project on our GitHub page.

Create a new Application

  • Open Android Studio
  • Choose Start a new Android Studio project
  • Enter a suitable application name and your company domain.
  • Select the Phone and Tablet form factor.
  • Set the Minimum SDK to at least API 16: Android 4.1, which is the oldest API supported by the Broadcaster.
  • Choose the Empty Activity template.
  • Enter a suitable Activity Name, make sure Backwards Compatibility (AppCompat) is checked and let Android Studio finish generating the project.

Add the broadcast SDK

  • Log in to the Bambuser site and download the latest SDK bundle for Android from the Developer page.
    • Open the downloaded zip file and extract it.
  • In Android Studio, in the project tree on the left-hand-side, right click on your app module and choose Open Module Settings.
  • Click the green + in the upper left corner of the Project Structure screen.
  • Choose Import .JAR/.AAR Package.
  • Navigate to the SDK files you extracted and import the .aar file.
  • You may now need to close and reopen the Project Structure screen, before the library module shows up in the list. Some recent versions of Android Studio fail to add new modules to the settings.gradle file. If this happens, add include ':libbambuser' manually at the end of the file and sync the project, then reopen the Project Structure screen.
  • Keep the app module selected in the Project Structure screen and bring up the Dependencies pane.
  • Click the green + in the upper right corner and choose Module dependency.
  • Choose the imported library module, click OK
  • Click OK to return from the Project Structure screen.

Add required Android app permissions and features

The Bambuser broadcasting library for Android requires at least the following permissions for basic functionality: CAMERA, RECORD_AUDIO and INTERNET. We also recommend adding the ACCESS_NETWORK_STATE and WAKE_LOCK permissions at this point.

Additionally, to achieve suitable filtering of your app in the Google Play store, you should declare <uses-feature /> tags relevant for apps that rely on the camera.

  • Open the manifests/AndroidManifest.xml file in the Project tree on the left.

  • Add the following tags for permissions and features, before the <application /> tag:

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-feature android:name="android.hardware.camera.any" android:required="true" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
<uses-feature android:name="android.hardware.camera.front" android:required="false" />

Since Android 6.0, the above is not enough. Certain permissions must be approved by the end-user at runtime. In the generated CameraActivity.java, check for and request any missing permissions:

import android.Manifest;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
// ...
public class CameraActivity extends AppCompatActivity {
    @Override
    public void onResume() {
        super.onResume();
        if (!hasPermission(Manifest.permission.CAMERA)
            && !hasPermission(Manifest.permission.RECORD_AUDIO))
            ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.CAMERA,
                Manifest.permission.RECORD_AUDIO}, 1);
        else if (!hasPermission(Manifest.permission.RECORD_AUDIO))
            ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.RECORD_AUDIO}, 1);
        else if (!hasPermission(Manifest.permission.CAMERA))
            ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.CAMERA}, 1);
    }

    private boolean hasPermission(String permission) {
        return ActivityCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED;
    }
    // ...
}

This is enough for our minimal example. In practice, you should also at least implement onRequestPermissionsResult() in case the user rejects any of the requested permissions.

Add the viewfinder

In activity_camera.xml replace the auto-generated TextView with a SurfaceViewWithAutoAR.

<com.bambuser.broadcaster.SurfaceViewWithAutoAR android:id="@+id/PreviewSurfaceView"
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:layout_centerInParent="true" />

In the generated CameraActivity.java import and add a reference to the SurfaceView from the layout.

import android.view.SurfaceView;
// ...
public class CameraActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
        mPreviewSurface = (SurfaceView) findViewById(R.id.PreviewSurfaceView);
    }
    SurfaceView mPreviewSurface;
}

Authentication

To be able to broadcast, your app needs to identify itself to Bambuser. Head over to the Developer page on the Bambuser site again and get the Sandbox applicationId, which we'll use when constructing the Broadcaster.

Remember to replace the Sandbox id with a Production applicationId before you release your app!

Bootstrap the SDK

Prepare CameraActivity.java for integration of the Broadcaster by importing the related classes and adding an implementation of the Broadcaster.Observer interface:

import com.bambuser.broadcaster.BroadcastStatus;
import com.bambuser.broadcaster.Broadcaster;
import com.bambuser.broadcaster.CameraError;
import com.bambuser.broadcaster.ConnectionError;
// ...
public class CameraActivity extends AppCompatActivity {
    // ...
    private Broadcaster.Observer mBroadcasterObserver = new Broadcaster.Observer() {
        @Override
        public void onConnectionStatusChange(BroadcastStatus broadcastStatus) {
        }
        @Override
        public void onStreamHealthUpdate(int i) {
        }
        @Override
        public void onConnectionError(ConnectionError connectionError, String s) {
        }
        @Override
        public void onCameraError(CameraError cameraError) {
        }
        @Override
        public void onChatMessage(String s) {
        }
        @Override
        public void onResolutionsScanned() {
        }
        @Override
        public void onCameraPreviewStateChanged() {
        }
        @Override
        public void onBroadcastInfoAvailable(String s, String s1) {
        }
        @Override
        public void onBroadcastIdAvailable(String s) {
        }
    };
    // ...
}

Add the applicationId, and in the onCreate(Bundle) method create an instance of the Broadcaster class. Override and forward the Activity lifecycle events necessary for init and release of the camera.

// ...
public class CameraActivity extends AppCompatActivity {
    private static final String APPLICATION_ID = "GFZalqkR5iyZcIgaolQmA";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
        mBroadcaster = new Broadcaster(this, APPLICATION_ID, mBroadcasterObserver);
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        mBroadcaster.onActivityDestroy();
    }
    @Override
    public void onPause() {
        super.onPause();
        mBroadcaster.onActivityPause();
    }
    @Override
    public void onResume() {
        // ...
        mBroadcaster.setCameraSurface(mPreviewSurface);
        mBroadcaster.onActivityResume();
    }
    // ...
    Broadcaster mBroadcaster;
}

Screen rotation and keep-awake

To rotate the camera preview and live video according to the rotation of the device, forward the display rotation to the Broadcaster in onCreate(Bundle) and onResume().

mBroadcaster.setRotation(getWindowManager().getDefaultDisplay().getRotation());

To prevent the screen from going to sleep, set FLAG_KEEP_SCREEN_ON when a broadcast starts and clear it when the broadcast ends.

Import the WindowManager class where the flag is found:

import android.view.WindowManager;

Then add an implementation to the empty onConnectionStatusChange(BroadcastStatus broadcastStatus) callback in the Broadcaster.Observer you created earlier:

@Override
public void onConnectionStatusChange(BroadcastStatus broadcastStatus) {
    if (broadcastStatus == BroadcastStatus.STARTING)
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    if (broadcastStatus == BroadcastStatus.IDLE)
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}

Add a broadcast button

Add a simple Button in the activity_camera.xml layout:

<Button android:id="@+id/BroadcastButton"
    android:text="Broadcast"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

Import relevant classes for a Button and make it respond to clicks in CameraActivity.java:

import android.view.View;
import android.widget.Button;
// ...
public class CameraActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
        mBroadcastButton = (Button)findViewById(R.id.BroadcastButton);
        mBroadcastButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mBroadcaster.canStartBroadcasting())
                    mBroadcaster.startBroadcast();
                else
                    mBroadcaster.stopBroadcast();
            }
        });
    }
	// ...
	Button mBroadcastButton;
}

Update the button label depending on broadcast state:

@Override
public void onConnectionStatusChange(BroadcastStatus broadcastStatus) {
    // ...
    mBroadcastButton.setText(broadcastStatus == BroadcastStatus.IDLE ? "Broadcast" : "Disconnect");
}

Log events from the app

Get primitive status updates from the app by logging some of the events in the Broadcaster.Observer interface:

import android.util.Log;
// ...
public class CameraActivity extends AppCompatActivity {
    // ...
    private Broadcaster.Observer mBroadcasterObserver = new Broadcaster.Observer() {
        @Override
        public void onConnectionStatusChange(BroadcastStatus broadcastStatus) {
            Log.i("Mybroadcastingapp", "Received status change: " + broadcastStatus);
            // ...
        }
        @Override
        public void onConnectionError(ConnectionError connectionError, String s) {
            Log.w("Mybroadcastingapp", "Received connection error: " + connectionError + ", " + s);
        }
        // ...
    };
}

Running the app

Connect your mobile device to your PC and follow the Android Developers guide for running your app.

If everything is set up correctly, you should be able to start your first broadcast from your app by tapping the button we added earlier.

When broadcasting successfully with a valid applicationId, a filtered device log in Android Studio would contain the following output from the example code above:

Check the Content page when logged in on the Bambuser site to view the broadcast when you go live in Sandbox mode.

What's next

  • Be sure to experience a few of your live broadcasts via the Sandbox player on the Bambuser website.

  • Consider whether you also need a player in your app and / or on your website.

  • Study the REST APIs and Webhooks, which can be used to make your backend broadcast-aware.