Quick Start: Zero to Print
This guide gets you from nothing to printing a label in the shortest time possible. We'll cover the most important use case: scan a barcode, look up product information, and print a label.
By the end of this guide, you'll have working code that:
- Connects to a Pathfinder Edge device
- Scans a product barcode
- Looks up product information
- Prints a price label
Prerequisites
Before you begin, ensure you have:
- ✅ Android Studio installed
- ✅ An Android device (7.1+) with Bluetooth enabled
- ✅ A Pathfinder Edge device powered on
- ✅ The SDK files:
addevicemanager.aarandedge.aar
Step 1: Project Setup (2 minutes)
Add the SDK Libraries
- Copy
addevicemanager.aarandedge.aarinto your project'sapp/libs/folder - Open
app/build.gradleand add:
dependencies {
// AD Device Manager SDK
implementation(files("libs/addevicemanager.aar"))
// Required dependencies for AD Device Manager SDK
implementation(files("libs/edge.aar"))
implementation 'org.slf4j:slf4j-api:2.0.17'
// ... your other dependencies
}
- Click Sync Now in Android Studio
Add Bluetooth Permissions
Open app/src/main/AndroidManifest.xml and add inside the <manifest> tag:
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Step 2: Create the Layout (1 minute)
Create or update app/src/main/res/layout/activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton
android:id="@+id/bleRadioButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="BLE" />
<RadioButton
android:id="@+id/serialRadioButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Serial" />
</RadioGroup>
<TextView
android:id="@+id/statusText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="Ready to connect"
android:textSize="18sp" />
<Button
android:id="@+id/searchButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Scan for Devices" />
<Button
android:id="@+id/serialConnectButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Connect via Serial"
android:visibility="gone" />
<Button
android:id="@+id/disconnectButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Disconnect"
android:visibility="gone" />
<Button
android:id="@+id/scanButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:text="Scan Barcode" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false">
<EditText
android:id="@+id/inputField"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="Text to Print" />
<Button
android:id="@+id/printButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:enabled="false"
android:text="Print" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="4dp"
android:text="Available Devices:"
android:textStyle="bold" />
<ListView
android:id="@+id/deviceList"
android:layout_width="match_parent"
android:layout_height="200dp" />
</LinearLayout>
</ScrollView>
Step 3: Create the Activity (5 minutes)
Create a new Activity or modify your MainActivity.java. Here's the complete, working code:
package com.averydennison.pfquickstart;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import com.averydennison.addevicemanager.DeviceManager;
import com.averydennison.addevicemanager.adapters.DeviceAdapter;
import com.averydennison.addevicemanager.adapters.PrinterAdapter;
import com.averydennison.addevicemanager.adapters.ScannerAdapter;
import com.averydennison.addevicemanager.callbacks.DiscoveryCallback;
import com.averydennison.addevicemanager.callbacks.ScanCallback;
import com.averydennison.addevicemanager.connection.DeviceConnection;
import com.averydennison.addevicemanager.connection.SerialConnection;
import com.averydennison.addevicemanager.models.ScannedData;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
// SDK Components
private DeviceManager deviceManager;
private DeviceAdapter deviceAdapter;
private PrinterAdapter printerAdapter;
private ScannerAdapter scannerAdapter;
// UI Components
private RadioButton bleRadioButton, serialRadioButton;
private TextView statusText;
private Button bleSearchButton, serialConnectButton, disconnectButton, scanButton, printButton;
private EditText inputField;
private ListView deviceListView;
// Discovered devices
private final List<DeviceConnection> discoveredDevices = new ArrayList<>();
private ArrayAdapter<String> deviceListAdapter;
// Permission launcher
private final ActivityResultLauncher<String[]> permissionLauncher =
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(),
result -> {
if (!result.containsValue(false)) startDeviceDiscovery();
else showToast("Bluetooth permissions required");
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize UI
statusText = findViewById(R.id.statusText);
bleSearchButton = findViewById(R.id.searchButton);
serialConnectButton = findViewById(R.id.serialConnectButton);
disconnectButton = findViewById(R.id.disconnectButton);
scanButton = findViewById(R.id.scanButton);
printButton = findViewById(R.id.printButton);
inputField = findViewById(R.id.inputField);
deviceListView = findViewById(R.id.deviceList);
bleRadioButton = findViewById(R.id.bleRadioButton);
serialRadioButton = findViewById(R.id.serialRadioButton);
bleRadioButton.setOnCheckedChangeListener((buttonView, isChecked) -> {
bleSearchButton.setVisibility(isChecked ? Button.VISIBLE : Button.GONE);
deviceListView.setVisibility(isChecked ? ListView.VISIBLE : ListView.GONE);
serialConnectButton.setVisibility(!isChecked ? Button.VISIBLE : Button.GONE);
});
// Check if device supports BLE, if not allow serial only (and hide radio buttons)
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
serialRadioButton.setChecked(true);
bleRadioButton.setVisibility(RadioButton.GONE);
serialRadioButton.setVisibility(RadioButton.GONE);
showToast("BLE not supported, defaulting to Serial connection");
}
// Initialize device list adapter
deviceListAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new ArrayList<>());
deviceListView.setAdapter(deviceListAdapter);
// Tap a device in the list to connect
deviceListView.setOnItemClickListener((parent, view, position, id) -> {
DeviceConnection device = discoveredDevices.get(position);
deviceManager.stopBluetoothDiscovery();
connectToDevice(device);
});
// Initialize SDK
deviceManager = new DeviceManager(this);
// Button handlers
bleSearchButton.setOnClickListener(v -> checkPermissionsAndSearch());
serialConnectButton.setOnClickListener(v -> connectViaSerial());
disconnectButton.setOnClickListener(v -> disconnect());
scanButton.setOnClickListener(v -> triggerScan());
printButton.setOnClickListener(v -> onClickPrint());
}
@Override
protected void onDestroy() {
super.onDestroy();
if (deviceAdapter == null) return;
deviceAdapter.disconnect();
}
// ============================================
// STEP 1: Request Permissions & Discover, Or connect via Serial
// ============================================
private void checkPermissionsAndSearch() {
List<String> permissions = new ArrayList<>();
permissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
permissions.add(Manifest.permission.BLUETOOTH_SCAN);
permissions.add(Manifest.permission.BLUETOOTH_CONNECT);
}
List<String> needed = new ArrayList<>();
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(this, permission)
!= PackageManager.PERMISSION_GRANTED) {
needed.add(permission);
}
}
if (needed.isEmpty()) {
startDeviceDiscovery();
} else {
permissionLauncher.launch(needed.toArray(new String[0]));
}
}
private void startDeviceDiscovery() {
// Clear previous results
discoveredDevices.clear();
deviceListAdapter.clear();
updateStatus("Scanning for devices...");
deviceManager.startBluetoothDiscovery(new DiscoveryCallback() {
@Override
public void onDeviceFound(DeviceConnection device) {
Log.i(TAG, "Found device: " + device.getName());
runOnUiThread(() -> {
discoveredDevices.add(device);
deviceListAdapter.add(device.getName());
deviceListAdapter.notifyDataSetChanged();
});
}
@Override
public void onDiscoveryFinished() {
if (discoveredDevices.isEmpty()) {
updateStatus("No devices found");
} else {
updateStatus("Found " + discoveredDevices.size() + " device(s). Tap to connect.");
}
}
@Override
public void onDiscoveryFailed(Exception error) {
updateStatus("Discovery failed: " + error.getMessage());
}
});
}
private void connectViaSerial() {
DeviceConnection deviceConnection = new SerialConnection(DeviceConnection.PrinterType.EDGE,
"PF Edge Printer");
connectToDevice(deviceConnection);
}
// ============================================
// STEP 2: Connect and Create Print Template
// ============================================
private void connectToDevice(DeviceConnection device) {
updateStatus("Connecting to " + device.getName() + "...");
deviceAdapter = deviceManager.getAdapterForDevice(device);
deviceAdapter.connect()
.thenRunAsync(this::createPrintTemplate)
.thenAccept(avoid -> {
updateUiState("Connected to " + device.getName(), true);
setupSdkComponents();
})
.exceptionally(error -> {
updateStatus("Connection failed: " + error.getMessage());
return null;
});
}
private void createPrintTemplate() {
String template = "SIZE 2,1\n" +
"CLS\n" +
"DIRECTION 0\n" +
"TEXT 160,20,\"Roboto-Bold.TTF\",0,0,10,\"{PRICE}\"\n" +
"BARCODE 200,60,\"128\",50,2,0,2,2,2,\"{SERIAL}\"\n";
deviceAdapter.createResource("ProductLabel.ngt", template.getBytes(), true, null)
.exceptionally(error -> {
showToast("Failed to create template: " + error.getMessage());
return null;
});
}
private void setupSdkComponents() {
try {
printerAdapter = deviceAdapter.getPrinterAdapter();
scannerAdapter = deviceAdapter.getScannerAdapter();
scannerAdapter.setScanCallback(new ScanCallback() {
@Override
public void onScanSuccess(ScannedData scannedData) {
runOnUiThread(() -> handleScannedBarcode(scannedData.data));
}
@Override
public void onScanFailure(String errorMessage) {
showToast("Scan failed: " + errorMessage);
}
});
Log.i(TAG, "Adapters initialized successfully");
} catch (Exception e) {
Log.e(TAG, "Failed to initialize adapters", e);
}
}
private void disconnect() {
if (deviceAdapter != null) {
deviceAdapter.disconnect();
deviceAdapter = null;
printerAdapter = null;
scannerAdapter = null;
}
discoveredDevices.clear();
deviceListAdapter.clear();
updateUiState("Disconnected. Ready to connect.", false);
}
// ============================================
// STEP 3: Scan Barcode
// ============================================
private void triggerScan() {
if (scannerAdapter == null) {
showToast("Scanner not available");
return;
}
updateStatus("Scanning...");
scannerAdapter.triggerScan()
.exceptionally(error -> {
runOnUiThread(() -> showToast("Scan failed: " + error.getMessage()));
return null;
});
}
private void handleScannedBarcode(String barcode) {
Log.i(TAG, "Scanned barcode: " + barcode);
updateStatus("Scanned: " + barcode);
showToast("Scanned: " + barcode);
inputField.setText(barcode);
}
// ============================================
// STEP 4: Print
// ============================================
private void onClickPrint() {
String input = inputField.getText().toString().trim();
if (input.isEmpty()) {
showToast("Please enter a barcode");
return;
}
String productPrice = "$1.99";
String data = "SERIAL=" + input + ";PRICE=" + productPrice;
printerAdapter.print("ProductLabel.ngt", data, 1)
.thenAccept(unused -> {
updateStatus("Printed: " + input);
showToast("Label printed!");
})
.exceptionally(error -> {
updateStatus("Print failed");
showToast("Print failed: " + error.getMessage());
return null;
});
}
// ============================================
// Helper Methods
// ============================================
private void updateStatus(String message) {
Log.i(TAG, message);
runOnUiThread(() -> statusText.setText(message));
}
private void updateUiState(String statusMessage, boolean connected) {
runOnUiThread(() -> {
boolean bleMode = ((RadioButton) findViewById(R.id.bleRadioButton)).isChecked();
bleSearchButton.setVisibility(bleMode && !connected ? Button.VISIBLE : Button.GONE);
serialConnectButton.setVisibility(connected || bleMode ? Button.GONE : Button.VISIBLE);
disconnectButton.setVisibility(connected ? Button.VISIBLE : Button.GONE);
bleRadioButton.setEnabled(!connected);
serialRadioButton.setEnabled(!connected);
scanButton.setEnabled(connected);
inputField.setEnabled(connected);
printButton.setEnabled(connected);
updateStatus(statusMessage);
});
}
private void showToast(String message) {
runOnUiThread(() -> Toast.makeText(this, message, Toast.LENGTH_SHORT).show());
}
}
Step 4: Run and Test
- Power on your Pathfinder Edge device
- Run the app on your Android device
- Choose connection mode:
- BLE (Companion Device): Select "BLE" radio button, then tap Scan for Devices
- Serial (Integrated Device): Select "Serial" radio button, then tap Connect via Serial
- Grant Bluetooth permissions when prompted (for BLE mode)
- For BLE mode: Click the device to connect
- Once connected, tap Scan Barcode
- The scanned barcode will appear in the input field
- Tap Print to print a label with the scanned barcode
- Watch the label print! 🎉
What Just Happened?
Here's the workflow:
┌─────────────────────────────────────────────────────────────────┐
│ 1. User selects connection mode (BLE or Serial) │
│ │
│ 2. User taps "Scan for Devices" (BLE) or "Connect" (Serial) │
│ └── BLE Mode: │
│ └── App requests Bluetooth permissions │
│ └── App discovers Pathfinder Edge devices │
│ └── App connects to selected device │
│ └── Serial Mode: │
│ └── App connects directly via Serial connection │
│ │
│ 3. User taps "Scan" (or presses device button) │
│ └── Scanner reads barcode │
│ └── ScanCallback.onScanSuccess() fires with barcode │
│ │
│ 4. User taps "Print" button │
│ └── Builds template data string: "PRICE=...;SERIAL=...;" │
│ └── Calls printerAdapter.print() │
│ └── Label prints! ✓ │
└─────────────────────────────────────────────────────────────────┘
Troubleshooting
| Problem | Solution |
|---|---|
| "No devices found" | Ensure Pathfinder Edge is powered on and Bluetooth is enabled |
| Permission dialog doesn't appear | Check that permissions are in AndroidManifest.xml |
| Scan doesn't trigger | Ensure device has scanning capability |
Next Steps
You've got the basics working! Now dive deeper:
| Guide | Learn About |
|---|---|
| Getting Started | Complete SDK setup and configuration |
| Resource Management | Uploading templates and fonts |
| Printing | Advanced printing techniques |
| Scanning | Symbology configuration |
| Callbacks | Real-time status updates |
| Error Handling | Handling failures gracefully |