close
The Wayback Machine - https://web.archive.org/web/20171110050706/https://developer.android.com/samples/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScannerFragment.html
Skip to content

Most visited

Recently visited

navigation
BluetoothAdvertisements / src / com.example.android.bluetoothadvertisements /

ScannerFragment.java

1
/*
2
 * Copyright (C) 2015 The Android Open Source Project
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
 
17
package com.example.android.bluetoothadvertisements;
18
 
19
import android.bluetooth.BluetoothAdapter;
20
import android.bluetooth.le.BluetoothLeScanner;
21
import android.bluetooth.le.ScanCallback;
22
import android.bluetooth.le.ScanFilter;
23
import android.bluetooth.le.ScanResult;
24
import android.bluetooth.le.ScanSettings;
25
import android.os.Bundle;
26
import android.os.Handler;
27
import android.support.v4.app.ListFragment;
28
import android.util.Log;
29
import android.view.LayoutInflater;
30
import android.view.Menu;
31
import android.view.MenuInflater;
32
import android.view.MenuItem;
33
import android.view.View;
34
import android.view.ViewGroup;
35
import android.widget.Toast;
36
 
37
import java.util.ArrayList;
38
import java.util.List;
39
import java.util.concurrent.TimeUnit;
40
 
41
 
42
/**
43
 * Scans for Bluetooth Low Energy Advertisements matching a filter and displays them to the user.
44
 */
45
public class ScannerFragment extends ListFragment {
46
 
47
    private static final String TAG = ScannerFragment.class.getSimpleName();
48
 
49
    /**
50
     * Stops scanning after 5 seconds.
51
     */
52
    private static final long SCAN_PERIOD = 5000;
53
 
54
    private BluetoothAdapter mBluetoothAdapter;
55
 
56
    private BluetoothLeScanner mBluetoothLeScanner;
57
 
58
    private ScanCallback mScanCallback;
59
 
60
    private ScanResultAdapter mAdapter;
61
 
62
    private Handler mHandler;
63
 
64
    /**
65
     * Must be called after object creation by MainActivity.
66
     *
67
     * @param btAdapter the local BluetoothAdapter
68
     */
69
    public void setBluetoothAdapter(BluetoothAdapter btAdapter) {
70
        this.mBluetoothAdapter = btAdapter;
71
        mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
72
    }
73
 
74
    @Override
75
    public void onCreate(Bundle savedInstanceState) {
76
        super.onCreate(savedInstanceState);
77
        setHasOptionsMenu(true);
78
        setRetainInstance(true);
79
 
80
        // Use getActivity().getApplicationContext() instead of just getActivity() because this
81
        // object lives in a fragment and needs to be kept separate from the Activity lifecycle.
82
        //
83
        // We could get a LayoutInflater from the ApplicationContext but it messes with the
84
        // default theme, so generate it from getActivity() and pass it in separately.
85
        mAdapter = new ScanResultAdapter(getActivity().getApplicationContext(),
86
                LayoutInflater.from(getActivity()));
87
        mHandler = new Handler();
88
 
89
    }
90
 
91
    @Override
92
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
93
                             Bundle savedInstanceState) {
94
 
95
        final View view = super.onCreateView(inflater, container, savedInstanceState);
96
 
97
        setListAdapter(mAdapter);
98
 
99
        return view;
100
    }
101
 
102
    @Override
103
    public void onViewCreated(View view, Bundle savedInstanceState) {
104
        super.onViewCreated(view, savedInstanceState);
105
 
106
        getListView().setDivider(null);
107
        getListView().setDividerHeight(0);
108
 
109
        setEmptyText(getString(R.string.empty_list));
110
 
111
        // Trigger refresh on app's 1st load
112
        startScanning();
113
 
114
    }
115
 
116
    @Override
117
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
118
        super.onCreateOptionsMenu(menu, inflater);
119
        inflater.inflate(R.menu.scanner_menu, menu);
120
    }
121
 
122
    @Override
123
    public boolean onOptionsItemSelected(MenuItem item) {
124
 
125
        switch (item.getItemId()) {
126
            case R.id.refresh:
127
                startScanning();
128
                return true;
129
            default:
130
                return super.onOptionsItemSelected(item);
131
        }
132
    }
133
 
134
    /**
135
     * Start scanning for BLE Advertisements (& set it up to stop after a set period of time).
136
     */
137
    public void startScanning() {
138
        if (mScanCallback == null) {
139
            Log.d(TAG, "Starting Scanning");
140
 
141
            // Will stop the scanning after a set time.
142
            mHandler.postDelayed(new Runnable() {
143
                @Override
144
                public void run() {
145
                    stopScanning();
146
                }
147
            }, SCAN_PERIOD);
148
 
149
            // Kick off a new scan.
150
            mScanCallback = new SampleScanCallback();
151
            mBluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), mScanCallback);
152
 
153
            String toastText = getString(R.string.scan_start_toast) + " "
154
                    + TimeUnit.SECONDS.convert(SCAN_PERIOD, TimeUnit.MILLISECONDS) + " "
155
                    + getString(R.string.seconds);
156
            Toast.makeText(getActivity(), toastText, Toast.LENGTH_LONG).show();
157
        } else {
158
            Toast.makeText(getActivity(), R.string.already_scanning, Toast.LENGTH_SHORT);
159
        }
160
    }
161
 
162
    /**
163
     * Stop scanning for BLE Advertisements.
164
     */
165
    public void stopScanning() {
166
        Log.d(TAG, "Stopping Scanning");
167
 
168
        // Stop the scan, wipe the callback.
169
        mBluetoothLeScanner.stopScan(mScanCallback);
170
        mScanCallback = null;
171
 
172
        // Even if no new results, update 'last seen' times.
173
        mAdapter.notifyDataSetChanged();
174
    }
175
 
176
    /**
177
     * Return a List of {@link ScanFilter} objects to filter by Service UUID.
178
     */
179
    private List<ScanFilter> buildScanFilters() {
180
        List<ScanFilter> scanFilters = new ArrayList<>();
181
 
182
        ScanFilter.Builder builder = new ScanFilter.Builder();
183
        // Comment out the below line to see all BLE devices around you
184
        builder.setServiceUuid(Constants.Service_UUID);
185
        scanFilters.add(builder.build());
186
 
187
        return scanFilters;
188
    }
189
 
190
    /**
191
     * Return a {@link ScanSettings} object set to use low power (to preserve battery life).
192
     */
193
    private ScanSettings buildScanSettings() {
194
        ScanSettings.Builder builder = new ScanSettings.Builder();
195
        builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER);
196
        return builder.build();
197
    }
198
 
199
    /**
200
     * Custom ScanCallback object - adds to adapter on success, displays error on failure.
201
     */
202
    private class SampleScanCallback extends ScanCallback {
203
 
204
        @Override
205
        public void onBatchScanResults(List<ScanResult> results) {
206
            super.onBatchScanResults(results);
207
 
208
            for (ScanResult result : results) {
209
                mAdapter.add(result);
210
            }
211
            mAdapter.notifyDataSetChanged();
212
        }
213
 
214
        @Override
215
        public void onScanResult(int callbackType, ScanResult result) {
216
            super.onScanResult(callbackType, result);
217
 
218
            mAdapter.add(result);
219
            mAdapter.notifyDataSetChanged();
220
        }
221
 
222
        @Override
223
        public void onScanFailed(int errorCode) {
224
            super.onScanFailed(errorCode);
225
            Toast.makeText(getActivity(), "Scan failed with error: " + errorCode, Toast.LENGTH_LONG)
226
                    .show();
227
        }
228
    }
229
}
This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)