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.

Hooray!