This is in relation to the question How to run a background task in android when the app is killed which I posted a few days ago.
After reading some blogs (e.g this) and code (of telephony) I have done some coding. My requirement is to show the user some sort of notification when a call is received, even when my app is not running (much like the Truecaller app in android). I am posting the code below.
callback_dispatcher.dart
const String HANDLE_INCOMING_CALL_BG = "handleIncomingCallBg";
const String INCOMING_CALL_BG_INITIALIZED = "incomingCallBgInitialized";
const String INCOMING_CALL_BG_INIT_SERVICE = "incomingCallBgInitService";
const String backgroundChannelId = "background_channel";
const String foregroundChannelId = "foreground_channel";
void callback_dispatcher() {
const MethodChannel _backgroundChannel = MethodChannel(backgroundChannelId);
WidgetsFlutterBinding.ensureInitialized();
_backgroundChannel.setMethodCallHandler((MethodCall call) =>
methodCallHandler(call));
_backgroundChannel.invokeMethod<void>(INCOMING_CALL_BG_INITIALIZED);
}
Future<dynamic> methodCallHandler(MethodCall call) async {
if(call.method == HANDLE_INCOMING_CALL_BG) {
final CallbackHandle _handle =
CallbackHandle.fromRawHandle(call.arguments['handle']);
final Function _handlerFunc = PluginUtilities.getCallbackFromHandle(_handle)!;
try {
await _handlerFunc(call.arguments['message']);
} catch(e) {
print('Unable to handle incoming call in background');
print(e);
}
}
return Future<void>.value();
}
void handleBgMsg(String msg) {
debugPrint("[handleBgMsg] incoming call number : " + msg);
// notification to user
}
background_incoming_call.dart
typedef BgMessageHandler(String msg);
class BackgroundIncomingCall {
MethodChannel? foregroundChannel;
Future<void> initialize(BgMessageHandler onBgMessage) async {
foregroundChannel = const MethodChannel(foregroundChannelId);
final CallbackHandle? bgSetupHandle =
PluginUtilities.getCallbackHandle(callback_dispatcher);
final CallbackHandle? bgMsgHandle =
PluginUtilities.getCallbackHandle(onBgMessage);
await foregroundChannel?.invokeMethod<bool>(
INCOMING_CALL_BG_INIT_SERVICE,
<String, dynamic> {
'bgSetupHandle': bgSetupHandle?.toRawHandle(),
'bgMsgHandle': bgMsgHandle?.toRawHandle()
}
);
}
}
In main.dart I am initializing BackgroundIncomingCall().initialize(handleBgMsg);.
On the Android part I have created a BroadcastReceiver as follows (the code mostly from the aforementioned Telephony package).
public class CallEventReceiver extends BroadcastReceiver implements
MethodChannel.MethodCallHandler {
private static final String TAG = "[TEST]";
private static final int NUMBER_LEN = 10;
public static String INCOMING_CALL_BG_INITIALIZED = "incomingCallBgInitialized";
public static String INCOMING_CALL_BG_INIT_SERVICE = "incomingCallBgInitService";
public static String SETUP_HANDLE = "bgSetupHandle";
public static String MESSAGE_HANDLE = "bgMsgHandle";
public static String BG_INCOMING_CALL_CHANNEL_ID = "background_channel";
public static String FG_INCOMING_CALL_CHANNEL_ID = "foreground_channel";
public static String HANDLE_INCOMING_CALL_BG = "handleIncomingCallBg";
public static String SHARED_PREF_NAME = "incomingCallSharedPrefBg";
public static String SHARED_PREF_BG_INCOMING_CALL_HANDLE = "incomingCallSharedPrefBgHandle";
public static String SHARED_PREFS_BACKGROUND_SETUP_HANDLE = "backgroundSetupHandle";
private Activity activity = null;
/* for background isolate*/
private Context appContext = null;
private AtomicBoolean isIsolateRunning = new AtomicBoolean(false);
private List<String> bgIncomingCallQueue = Collections.synchronizedList(new ArrayList<String>());
private MethodChannel bgIncomingCallChannel = null;;
private MethodChannel fgIncomingCallChannel = null;;
private FlutterLoader flutterLoader = null;
private FlutterEngine bgFlutterEngine = null;
private Long bgIncomingCallHandle = null;
/* END */
public CallEventReceiver() {}
public CallEventReceiver(Activity activity) {
super();
this.activity = activity;
}
#Override
public void onMethodCall(#NonNull MethodCall call, #NonNull MethodChannel.Result result) {
if(call.method.equals(INCOMING_CALL_BG_INITIALIZED)) {
if(appContext != null) {
onChannelInitialized(appContext);
} else {
throw new RuntimeException("Application context is not set");
}
} else if(call.method.equals(INCOMING_CALL_BG_INIT_SERVICE)) {
if(appContext != null) {
if(call.hasArgument(SETUP_HANDLE) && call.hasArgument(MESSAGE_HANDLE)) {
Long setupHandle = call.<Long>argument(SETUP_HANDLE);
Long msgHandle = call.<Long>argument(MESSAGE_HANDLE);
if (setupHandle == null || msgHandle == null) {
result.error("ILLEGAL_ARGS", "Setup handle or message handle is missing", null);
return;
}
setBgSetupHandle(appContext, setupHandle);
setBgMessageHandle(appContext, msgHandle);
}
} else {
throw new RuntimeException("Application context is not set");
}
} else {
result.notImplemented();
}
}
#Override
public void onReceive(Context context, Intent intent) {
this.appContext = context;
try {
Log.i(TAG, "[CallEventHandler] Receiver start");
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
if(state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
if(incomingNumber != null) {
Log.i(TAG, "[CallEventHandler] Incoming number : " + incomingNumber);
if(incomingNumber.length() > NUMBER_LEN) {
incomingNumber = incomingNumber.substring(incomingNumber.length() - NUMBER_LEN, incomingNumber.length());
Log.i(TAG, "[CallEventHandler] Incoming number after : " + incomingNumber);
processIncomingCall(context, incomingNumber);
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void setBgMessageHandle(Context ctx, Long handle) {
bgIncomingCallHandle = handle;
SharedPreferences pref = ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
pref.edit().putLong(SHARED_PREF_BG_INCOMING_CALL_HANDLE, handle).apply();
}
public void setBgSetupHandle(Context ctx, Long setupBgHandle) {
SharedPreferences pref = ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
pref.edit().putLong(SHARED_PREFS_BACKGROUND_SETUP_HANDLE, setupBgHandle).apply();
}
private void processIncomingCall(Context ctx, String incomingNumber) {
boolean isForeground = isAppForeground(ctx);
Log.i(TAG, "[processIncomingCall] is app in foreground : " + isForeground);
if (isForeground) {
processIncomingCallInForeground(incomingNumber);
} else {
processIncomingCallInBackground(ctx, incomingNumber);
}
}
private void processIncomingCallInBackground(Context ctx, String incomingNumber) {
if(!isIsolateRunning.get()) {
init(ctx);
SharedPreferences sharedPref = ctx.getSharedPreferences(SHARED_PREF_NAME,
Context.MODE_PRIVATE);
Long bgCallbackHandle = sharedPref.getLong(SHARED_PREFS_BACKGROUND_SETUP_HANDLE, 0);
startBackgroundIsolate(ctx, bgCallbackHandle);
bgIncomingCallQueue.add(incomingNumber);
} else {
executeDartCallbackInBgIsolate(ctx, incomingNumber);
}
}
private void processIncomingCallInForeground(String incomingNumber) {
Log.i(TAG, "[processIncomingCallInFg] incoming number : " + incomingNumber);
if(activity != null) {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
if(eventSink != null) {
eventSink.success(incomingNumber);
}
}
});
}
}
private void onChannelInitialized(Context ctx) {
isIsolateRunning.set(true);
synchronized(bgIncomingCallQueue) {
Iterator<String> it = bgIncomingCallQueue.iterator();
while(it.hasNext()) {
executeDartCallbackInBgIsolate(ctx, it.next());
}
bgIncomingCallQueue.clear();
}
}
private void init(Context ctx) {
FlutterInjector flutterInjector = FlutterInjector.instance();
flutterLoader = flutterInjector.flutterLoader();
flutterLoader.startInitialization(ctx);
}
private void executeDartCallbackInBgIsolate(Context ctx, String incomingNumber) {
if(bgIncomingCallChannel == null) {
throw new RuntimeException("background channel is not initialized");
}
if(bgIncomingCallHandle == null) {
bgIncomingCallHandle = getBgMessageHandle(ctx);
}
HashMap<String, Object> args = new HashMap<String, Object>();
args.put("handle", bgIncomingCallHandle);
args.put("message", incomingNumber);
bgIncomingCallChannel.invokeMethod(HANDLE_INCOMING_CALL_BG, args);
}
private Long getBgMessageHandle(Context ctx) {
return ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
.getLong(SHARED_PREF_BG_INCOMING_CALL_HANDLE, 0);
}
private void startBackgroundIsolate(Context ctx, Long callbackHandle) {
String appBundlePath = flutterLoader.findAppBundlePath();
FlutterCallbackInformation cbInfo = FlutterCallbackInformation.
lookupCallbackInformation(callbackHandle);
DartExecutor.DartCallback dartEntryPoint =
new DartExecutor.DartCallback(ctx.getAssets(), appBundlePath, cbInfo);
bgFlutterEngine = new FlutterEngine(ctx, flutterLoader, new FlutterJNI());
bgFlutterEngine.getDartExecutor().executeDartCallback(dartEntryPoint);
bgIncomingCallChannel = new MethodChannel(bgFlutterEngine.getDartExecutor(),
BG_INCOMING_CALL_CHANNEL_ID);
bgIncomingCallChannel.setMethodCallHandler(this);
}
private boolean isAppForeground(Context ctx) {
KeyguardManager keyGuardManager = (KeyguardManager) ctx.
getSystemService(Context.KEYGUARD_SERVICE);
if(keyGuardManager.isKeyguardLocked()) {
return false;
}
int pid = Process.myPid();
ActivityManager activityManager = (ActivityManager) ctx.
getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> list =
activityManager.getRunningAppProcesses();
for(ActivityManager.RunningAppProcessInfo proc : list) {
if(pid == proc.pid) {
return proc.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
}
}
return false;
}
}
In my AndroidManifest.xml I am registering the broadcast receiver
<receiver android:name="org.cdot.diu.handler.CallEventReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
</receiver>
Now If I run my app and minimize it, the handleBgMsg is called successfully. Since no print or debug message is shown in terminal when I kill the app, I am sending an SMS to me using Telephony package, which is sent successfully when the app is minimized. But it does not work if I kill the application, no SMS is sent. I am not sure what I am doing wrong here. My guess is that the Broadcast Receiver is killed when the app is killed. However, in the telephony package there is nothing to indicate that special care is done to keep the receiver alive when the app is killed.
Related
I'm trying to connect to random Bluetooth device that match a certain characteristic. I just want to print the incoming messages from the bluetooth device.
I've been trying for the past few days to try and connect to some random bluetooth device that matches a characteristic so that I'll be able to just print the messages from the bluetooth device.
public class main extends AppCompatActivity {
private List Service_UUIDs;
private List Charac_UUIDS;
private List Descriptor_UUIDs;
public static final UUID descriptor = UUID.fromString("863930fc-947c-421b-
9170-9969ea6ad610");
private BluetoothAdapter bluetoothAdapter = null;
private BluetoothLeScanner bluetoothLeScanner;
private ScanCallback scanCallback;
private TextView messageText;
private Handler handler;
private static final int SCAN_TIME = 15000;
private int connectionState = STATE_DISCONNECTED;
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTED = 2;
private boolean isConnected;
private boolean isScanning;
private BluetoothLeScanner myScanner;
private BluetoothGatt bluetoothGatt;
private String connected = "";
public List<BluetoothGattService> myServices;
private ScanCallback myScanCallBack;
private BluetoothDevice myDevice;
private HashMap<String, BluetoothDevice> myScanResults;
public final static String ACTION_GATT_SERVICES_DISCOVERED =
"com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
public final static String ACTION_DATA_AVAILABLE =
"com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
public static final int REQUEST_ENABLE_BT = 10;
private void unpairDevice(BluetoothDevice device) {
try {
Method m = device.getClass().getMethod("removeBond", (Class[])
null);
m.invoke(device, (Object[]) null);
Log.d("unpairDevice", Integer.toString(device.getBondState()));
}
catch (Exception e) { Log.e("unpairDevice", e.getMessage()); }
}
private void makeToast(String message) {
//creates pop-ups in UI of the message
Context context = getApplicationContext();
CharSequence text = message;
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BluetoothManager bluetoothManager = (BluetoothManager)
getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
if(!bluetoothManager.getAdapter().isEnabled()){
Intent btEnable = new
Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(btEnable, REQUEST_ENABLE_BT);
}
messageText = findViewById(R.id.atm_msg);
findViewById(R.id.scan_button).setOnClickListener(new handleScan());
}
class handleScan implements View.OnClickListener{
public void onClick(View view){
startScan();
}
}
private void startScan() {
List<ScanFilter> filters = new ArrayList<>();
ScanFilter scanFilter = new ScanFilter.Builder()
.setServiceUuid(new ParcelUuid(descriptor))
.build();
filters.add(scanFilter);
ScanSettings settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.build();
myScanResults = new HashMap<>();
myScanCallBack = new BtleScanCallback(myScanResults);
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
bluetoothLeScanner.startScan(myScanCallBack);
isScanning = true;
new Handler().postDelayed(this::stopScan, SCAN_TIME);
}
private void stopScan(){
if(isScanning && bluetoothAdapter != null
&& bluetoothAdapter.isEnabled()
&& bluetoothLeScanner != null){
bluetoothLeScanner.stopScan(myScanCallBack);
scanComplete();
}
myScanCallBack = null;
isScanning = false;
handler = null;
}
private void scanComplete(){
if (myScanResults.isEmpty()){
return;
}
for(BluetoothDevice device: myScanResults.values()){
connectDevice(device);
}
}
protected class BtleScanCallback extends ScanCallback {
private HashMap<String, BluetoothDevice> myScanResults;
BtleScanCallback(HashMap<String, BluetoothDevice> scanResults) {
myScanResults = scanResults;
}
#Override
public void onScanResult(int callbackType, ScanResult result) {
addScanResult(result);
}
#Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult result : results) {
addScanResult(result);
}
}
#Override
public void onScanFailed(int errorCode) {
makeToast("BLE Scan Failed with code " + errorCode);
}
private void addScanResult(ScanResult result) {
myDevice = result.getDevice();
String deviceAddress = myDevice.getAddress();
myScanResults.put(deviceAddress, myDevice);
}
}
private void connectDevice(BluetoothDevice device) {
bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback);
}
private final BluetoothGattCallback bluetoothGattCallback = new
BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int
newState) {
super.onConnectionStateChange(gatt, status, newState);
if (status == gatt.GATT_FAILURE){
Log.d("BLE_Assistance", "-----> status: " + gatt.GATT_FAILURE);
disconnectGattServer();
return;
}
if (status == 133){
bluetoothAdapter.disable();
bluetoothAdapter.enable();
Log.d("OCSC: ", "133 GATT_ERROR OCCURRED");
}
if (newState == gatt.STATE_CONNECTED){
Log.d("BLE_Assistance", "-----> state: " +
gatt.STATE_CONNECTED);
connectionState = gatt.STATE_CONNECTED;
isConnected = true;
return;
}
else if (status != gatt.GATT_SUCCESS){
Log.d("BLE_Assistance", "-----> status: could not connect");
disconnectGattServer();
return;
}
else if (newState == gatt.STATE_DISCONNECTED){
Log.d("BLE_Assistance", "status: " + gatt.STATE_DISCONNECTED);
disconnectGattServer();
}
}
public void disconnectGattServer(){
isConnected = false;
connected = "";
if (bluetoothLeScanner != null && scanCallback != null){
bluetoothLeScanner.flushPendingScanResults(scanCallback);
}
if (!myScanResults.isEmpty()){
myScanResults.clear();
}
try {
if (myServices.isEmpty()){
Log.d("Services", "Empty");
}
else{
myServices.clear();
}
} catch (NullPointerException nullPointerException){
Log.d("Services", "" + nullPointerException);
}
bluetoothAdapter.cancelDiscovery();
if(bluetoothGatt != null){
bluetoothGatt.abortReliableWrite();
bluetoothGatt.disconnect();
bluetoothGatt.close();
bluetoothGatt = null;
Log.d("disconnect server", ": Disconnected GATT Server");
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
byte[] msg_bytes = characteristic.getValue();
try {
String message = new String(msg_bytes, 0, msg_bytes.length,
"utf-8");
Log.d("onCharacteristicRead", message);
new Handler(Looper.getMainLooper()).postDelayed(() -> {
makeToast("onCharacteristicRead: " + message);
messageText.append("\nonCharacteristicRead: " + message);
}, 1000);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status){
super.onServicesDiscovered(gatt,status);
Service_UUIDs = new ArrayList();
Charac_UUIDS = new ArrayList();
Descriptor_UUIDs = new ArrayList();
if(status != gatt.GATT_SUCCESS){
return;
}else{
myServices = gatt.getServices();
for (BluetoothGattService service : myServices) {
for (BluetoothGattCharacteristic charac : service.getCharacteristics()) {
if (charac.getUuid().toString().contains("863930fc")) {
Service_UUIDs.add(charac.getService().getUuid().toString());
Charac_UUIDS.add(charac.getUuid().toString());
Descriptor_UUIDs.add(descriptor);
BluetoothGattDescriptor desc = charac.getDescriptor(descriptor);
gatt.setCharacteristicNotification(charac, true);
desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(desc);
}
}
}
Log.d("BLE_Assistance", "------ UUID Lists: " + Service_UUIDs + Charac_UUIDS + Descriptor_UUIDs);
}
}
};
}
I am not completely sure, I am also struggling with this kind of project right now and I am quite new to programming on Android. But I spotted a small mistake in your startScan and stopScan codes
bluetoothLeScanner.startScan(myScanCallBack);
Should be
bluetoothLeScanner.startScan(List<ScanFilter> filters, ScanSettings settings, ScanCallback callback)
with your scan filters and settings.
When you only send 'myScanCallback' to your 'bluetoothLeScanner.startScan', you're sending only default parameters with no filters.
https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner.html#startScan(java.util.List%3Candroid.bluetooth.le.ScanFilter%3E,%20android.bluetooth.le.ScanSettings,%20android.bluetooth.le.ScanCallback)
This means that you're searching for every device, and not specifically the one you want.
For testing while powerdraw isn't an issue, you can also set your ScanSettings to LOW_LATENCY (LOW_POWER is more intuitive to use with BLE but not a requirement. You can switch it later when you want more battery life while scanning).
I hope it helps you
I get out of memory error if the function doesWifiExist is false if it is true every thing works normally.Can someone tell me what m i doing wrong
The idea is to get a list of wifi networks nearby and check if the inserted network exists in the list.
Here is the wifi network scanning code and the the function that checks if the wifi network exists.
MainActivity:
private WiFiConnection _wifiConnection = null;
static final int MY_PERMISSIONS_REQUEST = 1042;
private static final String PERMISSIONS_TAG = "PERMISSION";
...
#Override
protected void onStart() {
super.onStart();
_wifiConnection = new WiFiConnection(this);
startScan();
}
#Override
protected void onStop() {
super.onStop();
_wifiConnection.stopScan();
unregisterReceiver(_wifiScanReceiver);
}
void startScan() {
checkPermission(this);
registerReceiver(_wifiScanReceiver,
new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
Thread t = new Thread(_wifiConnection.getWifiScanner());
t.start();
}
private final BroadcastReceiver _wifiScanReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context c, Intent intent) {
if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
if (_wifiConnection != null && _wifiConnection.isWifiEnabled()) {
_wifiConnection.updateScanData();
}
}
}
};
public static boolean checkPermission(Activity activity) {
boolean permission = true;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
List<String> requiringList = new ArrayList<>();
permission = activity.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
Log.d(PERMISSIONS_TAG, "ACCESS_COARSE_LOCATION: " + permission);
if (!permission) {
requiringList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
}
if (requiringList.size() > 0) {
String[] stringArray = requiringList.toArray(new String[0]);
activity.requestPermissions(stringArray, MY_PERMISSIONS_REQUEST);
}
}
return permission;
}
private boolean doesWifiExist(String s){
String[] array = s.split(" ");
String ssid = array[0];
boolean flag = false;
//Check if wifi exists in the area
for(int i = 0 ; i < _wifiConnection.getListSSIDs().size(); i++){
if(_wifiConnection.getListSSIDs().get(i).equals(ssid)){
flag = true;
break;
}
}
return flag;
}
WiFiConnection class:
public class WiFiConnection
{
private static final int SCAN_INTERVAL = 5000;
final private List<String> _listSSIDs = new ArrayList<>();
private WifiManager _wifiManager;
private final WiFiScanner _startScan = new WiFiScanner();
private List<ScanResult> scanResults;
WiFiConnection(Activity activity) {
_wifiManager = (WifiManager) activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
}
//Puts wifi networks in a list
public List<String> getListSSIDs() {
for(int i = 0; i < scanResults.size(); i++)
{
_listSSIDs.add(scanResults.get(i).SSID);
}
return _listSSIDs;
}
WiFiScanner getWifiScanner() { return _startScan; }
void stopScan() { _startScan.stop(); }
boolean isWifiEnabled() { return _wifiManager.isWifiEnabled(); }
//Gets the wifi networks
void updateScanData() {
if ((_wifiManager != null && _wifiManager.isWifiEnabled())) {
scanResults = _wifiManager.getScanResults();
}
}
//Scans at an interval
private class WiFiScanner implements Runnable
{
private boolean _stop = false;
public void stop() {_stop = true;}
#Override
public void run() {
while (!_stop) {
_listSSIDs.clear();
_wifiManager.startScan();
try {
Thread.sleep(SCAN_INTERVAL);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
The code where doesWifiExist is used.
//Check if wifi exists in the area
if(doesWifiExist(barcode.displayValue)){
//Connects to wifi
WifiConnect(barcode.displayValue);
//Saves item in db
insertItem();
if(networkSecurity.equals("None")){
networkPass = empty;
}
//Adds item to recyclerview
historyItems.add(0, new HistoryItem(wifiName + " " + networkSSID, wifiPass + " " + networkPass));
adapter.notifyItemInserted(0);
} else
Snackbar.make(findViewById(R.id.main_activity), R.string.snackInvalidQrCode, Snackbar.LENGTH_SHORT).show();
This method is called many times, and it everytime adds all scanResults at the end of field _listSSIDS.
public List<String> getListSSIDs() {
for(int i = 0; i < scanResults.size(); i++)
{
_listSSIDs.add(scanResults.get(i).SSID);
}
return _listSSIDs;
}
Use a local variable with a new List or better Set there.
Instead replace
for(int i = 0 ; i < _wifiConnection.getListSSIDs().size(); i++){
if(_wifiConnection.getListSSIDs().get(i).equals(ssid)){
flag = true;
break;
}
}
with
flag = _wifiConnection.hasSSID(String ssid);
public boolean hasSSID(String ssid) {
for (int i = 0; i < scanResults.size(); i++) {
if (ssid.equals(scanResults.get(i).SSID)) {
return true;
}
}
return false;
}
I have java.util.concurrent.RejectedExecutionException in this file. As I can see there is no more processes running after onStop called. Not sure where the error comes from. And I'm sure the executor isn't getting more tasks it can handle too.
Please help me to figure out where the error comes from.
public static final String TAG = BroadcastService.class.getSimpleName();
private static final int TIMER_DELAY_SECONDS = 3;
private volatile JmDNS mService = null;
private WifiManager.MulticastLock mMulticastLock = null;
private ScheduledExecutorService mExecutorService = null;
private ScheduledFuture mPublisherFuture = null;
private ScheduledFuture mApiPublisherFuture = null;
private NetworkUtils mNetworkUtils = null;
private Runnable mDelayedKiller = null;
public static Intent getStartIntent(Context context) {
final Intent serviceIntent = new Intent(context, BroadcastService.class);
serviceIntent.setAction(BroadcastService.INTENT_ACTION_BROADCAST_START);
return serviceIntent;
}
public static Intent getStopIntent(Context context) {
final Intent serviceIntent = new Intent(context, BroadcastService.class);
serviceIntent.setAction(BroadcastService.INTENT_ACTION_BROADCAST_STOP);
return serviceIntent;
}
#Override
public void onCreate() {
super.onCreate();
mNetworkUtils = NetworkUtils.getInstance(getApplicationContext());
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
if (intent == null) {
return START_STICKY;
}
if (intent.getAction() != null) {
switch (intent.getAction()) {
case INTENT_ACTION_BROADCAST_START:
startBroadcast();
break;
case INTENT_ACTION_BROADCAST_STOP:
stopBroadcast();
break;
}
}
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(final Intent intent) {
return null;
}
/**
* Starts broadcast on a background thread
*/
public void startBroadcast() {
if (mDelayedKiller != null) {
NetworkThread.getCommonInstance().removeTask(mDelayedKiller);
mDelayedKiller = null;
}
if (mExecutorService == null || mExecutorService.isShutdown()) {
mExecutorService = Executors.newScheduledThreadPool(2);
}
if (mPublisherFuture != null) {
mPublisherFuture.cancel(true);
}
final BonjourPublisher bonjourPublisher = new BonjourPublisher();
mPublisherFuture = mExecutorService.schedule(bonjourPublisher, 2, TimeUnit.SECONDS);
if (mApiPublisherFuture != null) {
mApiPublisherFuture.cancel(true);
}
final ApiPublisher apiPublisher = new ApiPublisher();
mApiPublisherFuture = mExecutorService.scheduleWithFixedDelay(apiPublisher, 0, 30, TimeUnit.SECONDS);
//inform listeners
EventBus.getDefault().post(new EventServiceBroadcasting(true));
}
public synchronized void stopBroadcast() {
if (mPublisherFuture == null && mApiPublisherFuture == null) {
return;
}
if (mPublisherFuture != null) {
mPublisherFuture.cancel(true);
if (mMulticastLock != null) {
mMulticastLock.release();
mMulticastLock = null;
}
}
if (mApiPublisherFuture != null) {
mApiPublisherFuture.cancel(true);
}
mDelayedKiller = new Runnable() {
#Override
public void run() {
mExecutorService.shutdownNow();
killService();
stopSelf();
}
};
NetworkThread.getCommonInstance().postDelayed(mDelayedKiller, 1000 * 20); //kill the service after 20 seconds
//inform listeners
EventBus.getDefault().post(new EventServiceBroadcasting(false));
}
#Override
public void onDestroy() {
super.onDestroy();
killService();
}
private synchronized void killService() {
if (mService != null) {
try {
mService.unregisterAllServices();
mService.close();
mService = null;
} catch (IOException e) {
e.printStackTrace();
}
} else {
}
}
public static class DiscoverableAssistant {
private DiscoverableAssistant() {
}
public static boolean isDiscoverable(Context context) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean(PREF_DEVICE_DISCOVERABLE, true); //true by default
}
public static void setDiscoverable(Context context, boolean discoverable) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit().putBoolean(PREF_DEVICE_DISCOVERABLE, discoverable).apply();
}
}
private class BonjourPublisher implements Runnable {
#Override
public void run() {
final String serviceName = mNetworkUtils.getDeviceName(BroadcastService.this);
final String serviceType = getString(R.string.multi_dns_network_name);
final Map<String, String> properties = new HashMap<>();
properties.put(DeviceViewActivity.DEVICE_PROPERTY_DEVICE_TYPE, "Android");
properties.put(DeviceViewActivity.DEVICE_PROPERTY_FILE_SERVER_PORT,
String.valueOf(mNetworkUtils.getAssignedPort()));
if (DiscoverableAssistant.isDiscoverable(BroadcastService.this)) {
properties.put(DeviceViewActivity.DEVICE_PROPERTY_DISCOVERABLE, "true");
} else {
properties.put(DeviceViewActivity.DEVICE_PROPERTY_DISCOVERABLE, "false");
}
//acquire wifi multicast lock
if (mMulticastLock == null) {
final WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
mMulticastLock.setReferenceCounted(true);
mMulticastLock.acquire();
}
try {
if (mService == null) {
mService = JmDNS.create(mNetworkUtils.getMyInet4Address(),
NetworkUtils.getHostName(mNetworkUtils.getDeviceName(BroadcastService.this)));
}
final ServiceInfo info = ServiceInfo.create(serviceType, serviceName, mNetworkUtils.getAssignedPort(), 0, 0, true, properties);
while (mService != null) {
mService.registerService(info);
Thread.sleep(TIMER_DELAY_SECONDS * 1000);
mService.unregisterAllServices();
Thread.sleep(1000);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
} catch (Exception e) {
}
}
}
private class ApiPublisher implements Runnable {
private APo api = null;
private SimplifiedDeviceInfo mDeviceInfo = null;
public ApiPublisher() {
api = Utils.getRetrofitInstance(BroadcastService.this, null)
.create(api.class);
}
#Override
public void run() {
try {
if (mDeviceInfo == null) {
mDeviceInfo = new SimplifiedDeviceInfo(mNetworkUtils.getDeviceName(BroadcastService.this),
mNetworkUtils.getMyInet4Address().getHostAddress(), mNetworkUtils.getAssignedPort(),
NetworkUtils.getDeviceType(), BroadcastService.DiscoverableAssistant.isDiscoverable(BroadcastService.this));
}
Call<JsonElement> call = api.broadcastDevice(mDeviceInfo);
call.execute();
} catch (Exception e) {
}
}
}
The a RejectedExecutionException is thrown when you attempt to submit a task to an executor, and it is refuses it. In this case, there is a clue in the exception message:
java.util.concurrent.ScheduledThreadPoolExecutor#42209b70[
Shutting down, pool size = 2, active threads = 2,
queued tasks = 0, completed tasks = 248]
This is telling me that you are attempting to submit a task to an Executor that is being shut down.
Now I can't pretend that I understand what your code is actually doing, but I can see that it is using postThread to schedule a Runnable that shuts down the executor. My guess is that the app has done that ... and then it is somehow trying to submit another task.
In reading your code I spotted a couple of places where you catch and then squash Exception. That is a really bad idea. I wouldn't be surprised if that is why you are having trouble debugging your code.
Still with the same project, this is a continuation from Run pocketSphinx and Google TTS together. I already do the revision according to the guide from Nikolay Shymyrev and do a lot of helping. But the final feature that I want implement still remains. The Google TTS run just fine now, but the recognizer have some problem.
the recognizer won't start if the Google TTS some words that quite long like
Speaker.speak("Drive mode now will be enabled. I will read your new messages for you now.");
and then my onPartialResult if condition cannot fulfilled like
if (text.equals("exit")) {
speaker.speak("yeah get in");
recognizer.cancel();
Intent i = new Intent(getApplicationContext(),PocketSphinxActivity.class);
startActivity(i);
I think the recognizer always listen since it runs in background, and then it listen the google TTS sentence that caused it won't recognize my speech afterwards. Because when I use handsfree with mic, and the sentence for speaker.Speak is just "Drive mode enabled", it recognize well my word next and execute the if condition above when I say "exit". But when the sentence is quite long like "Drive mode now will be enabled, I will read bla bla bla" it won't listen to my "exit" word.
What I want to do now is add timeout to the recognizer to timeout several momment so that it wont recognize any unnecessary sound. I want to put
startRecognition("search", timeout)
but my Eclipse won't me let do that. It gives me error. I'm Using PocketSphinx for Android 5 pre alpha.
Here's again, my code that build just to test and make sure it recognize just "exit" words
SMSReaderMain.java
public class SMSReaderMain extends Activity implements RecognitionListener {
private final int CHECK_CODE = 0x1;
private final int LONG_DURATION = 5000;
private final int SHORT_DURATION = 1200;
private Speaker speaker;
private TextView smsText;
private TextView smsSender;
private BroadcastReceiver smsReceiver;
public static final String TURNON_SR = "drive mode";
public static final String TURNOFF_SR = "ok";
public static final String DESTROY_SR = "exit";
public SpeechRecognizer recognizer;
public HashMap<String, Integer> captions;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_sms);
captions = new HashMap<String, Integer>();
new AsyncTask<Void, Void, Exception>() {
#Override
protected Exception doInBackground(Void... params) {
try {
Assets assets = new Assets(SMSReaderMain.this);
File assetDir = assets.syncAssets();
setupRecognizer(assetDir);
} catch (IOException e) {
return e;
}
return null;
}
#Override
protected void onPostExecute(Exception result) {
if (result != null) {
((TextView) findViewById(R.id.caption_text))
.setText("Failed to init recognizer " + result);
} else {
switchSearch(TURNOFF_SR);
}
}
}.execute();
//toggle = (ToggleButton)findViewById(R.id.speechToggle);
smsText = (TextView)findViewById(R.id.sms_text);
smsSender = (TextView)findViewById(R.id.sms_sender);
checkTTS();
}
private void startDriveMode(){
speaker.allow(true);
//speaker.speak(getString(R.string.start_speaking));
speaker.speak("Drive mode enabled");
}
private void checkTTS(){
Intent check = new Intent();
check.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
startActivityForResult(check, CHECK_CODE);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == CHECK_CODE){
if(resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS)
{
speaker = new Speaker(this);
}else {
Intent install = new Intent();
install.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(install);
}
}
startDriveMode();
initializeSMSReceiver();
registerSMSReceiver();
}
private void initializeSMSReceiver(){
smsReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if(bundle!=null){
Object[] pdus = (Object[])bundle.get("pdus");
for(int i=0;i<pdus.length;i++){
byte[] pdu = (byte[])pdus[i];
SmsMessage message = SmsMessage.createFromPdu(pdu);
String text = message.getDisplayMessageBody();
String sender = getContactName(message.getOriginatingAddress());
speaker.pause(LONG_DURATION);
speaker.speak("You have a new message from" + sender + "!");
speaker.pause(SHORT_DURATION);
speaker.speak(text);
smsSender.setText("Message from " + sender);
smsText.setText(text);
}
}
}
};
}
private void registerSMSReceiver() {
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(smsReceiver, intentFilter);
}
private String getContactName(String phone){
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phone));
String projection[] = new String[]{ContactsContract.Data.DISPLAY_NAME};
Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
if(cursor.moveToFirst()){
return cursor.getString(0);
}else {
return "unknown number";
}
}
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(smsReceiver);
speaker.destroy();
}
public void onPartialResult(Hypothesis hypothesis) {
//System.out.println("masuk coiii");
String text = hypothesis.getHypstr();
try {
if (text.equals("exit")) {
speaker.speak("yeah get in");
recognizer.cancel();
Intent i = new Intent(getApplicationContext(),PocketSphinxActivity.class);
startActivity(i);
}
//Intent i= null;
/**if (text.equals(TURNON_SR)) {
recognizer.cancel();
popPicture();
startDriveMode();
}
if (text.equals(TURNOFF_SR)) {
//speaker = new Speaker(this);
speaker.speak(getString(R.string.stop_speaking));
speaker.allow(false);
//popPicture2();
}
if (text.equals(DESTROY_SR)) {
recognizer.cancel();
i = new Intent(getApplicationContext(),PocketSphinxActivity.class);
startActivity(i);
onDestroy();
//popPicture2();
} **/
} catch (Exception e) {
e.printStackTrace();
}
}
public void popPicture() {
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.toast_image,(ViewGroup)
findViewById(R.id.toast_layout_id));
Toast toast = new Toast(getApplicationContext());
toast.setGravity(Gravity.CENTER_HORIZONTAL, 0, 0);
toast.setDuration(Toast.LENGTH_SHORT);
toast.setView(layout);
toast.show();
}
public void onResult(Hypothesis hypothesis) {
((TextView) findViewById(R.id.result_text)).setText("");
if (hypothesis != null) {
String text = hypothesis.getHypstr();
makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
}
}
public void switchSearch(String searchName) {
recognizer.stop();
recognizer.startListening(searchName);
//taro timout disini biar mic ga denger suara hp sendiri
((TextView) findViewById(R.id.caption_text)).setText(searchName);
}
public void setupRecognizer(File assetsDir) {
File modelsDir = new File(assetsDir, "models");
recognizer = defaultSetup()
.setAcousticModel(new File(modelsDir, "hmm/en-us-semi"))
.setDictionary(new File(modelsDir, "dict/cmu07a.dic"))
.setRawLogDir(assetsDir).setKeywordThreshold(1e-10f)
.getRecognizer();
recognizer.addListener(this);
// Create grammar-based searches.
// recognizer.addKeyphraseSearch(TURNOFF_SR, TURNON_SR);
//recognizer.addGrammarSearch(TURNON_SR, new File(modelsDir, "grammar/sms.gram"));
//recognizer.addGrammarSearch(TURNOFF_SR, new File(modelsDir, "grammar/sms.gram"));
//recognizer.addGrammarSearch(DESTROY_SR, new File(modelsDir, "grammar/sms.gram"));
File menuGrammar = new File(modelsDir, "grammar/sms.gram");
recognizer.addGrammarSearch(TURNOFF_SR, menuGrammar);
//recognizer.addGrammarSearch(TURNON_SR, menuGrammar);
//recognizer.addGrammarSearch(DESTROY_SR, menuGrammar);
}
#Override
public void onBeginningOfSpeech() {
// TODO Auto-generated method stub
}
#Override
public void onEndOfSpeech() {
// TODO Auto-generated method stub
}
}
Speaker.java
public class Speaker implements OnInitListener {
private TextToSpeech tts;
private boolean ready = false;
private boolean prematureSpeak = false;
private String ps;
private boolean allowed = false;
public Speaker(Context context){
tts = new TextToSpeech(context, this);
}
public boolean isAllowed(){
return allowed;
}
//public void allow(boolean allowed){
public void allow(boolean allowed){
this.allowed = allowed;
}
#Override
public void onInit(int status) {
if(status == TextToSpeech.SUCCESS){
// Change this to match your
// locale
tts.setLanguage(Locale.US);
ready = true;
if (prematureSpeak)
{
speak(ps);
prematureSpeak = false;
}
}else{
ready = false;
}
}
public void speak(String text){
// Speak only if the TTS is ready
// and the user has allowed speech
if(ready && allowed) {
HashMap<String, String> hash = new HashMap<String,String>();
hash.put(TextToSpeech.Engine.KEY_PARAM_STREAM,
String.valueOf(AudioManager.STREAM_NOTIFICATION));
tts.speak(text, TextToSpeech.QUEUE_ADD, hash);
}
else if(!ready) {
prematureSpeak = true;
ps = text;
}
}
public void pause(int duration){
tts.playSilence(duration, TextToSpeech.QUEUE_ADD, null);
}
// Free up resources
public void destroy(){
tts.shutdown();
}
public boolean isSpeaking()
{
return tts.isSpeaking();
}
}
Your code has several issues:
1) I told you to use keyword spotting mode, you are still using grammar mode
2) You need to cancel recognizer before you start voice feedback, instead of first speak then cancel
if (text.equals("exit")) {
speaker.speak("yeah get in");
recognizer.cancel();
....
you need to first cancel then speak:
if (text.equals("exit")) {
recognizer.cancel();
speaker.speak("yeah get in");
....
3) Once speaker is over you need to restart the recognizer, but there is no need to run activity again, see for details How to know when TTS is finished?
With those changes in onUtteranceEnded you start recognizer again:
public void onUtteranceCompleted(String utteranceId) {
recognizer.startSearch("search name");
}
Do not restart recognizer in onPartialResult, wait till TTS will finish.
GCM is always returning SENDER_INVALID with following code..
All I could find is project id to be valid, which is valid and i have also tried changing the project but that is not helping.
I also tried going though entire stackoverflow and google groups, also changing google account and tried creating Application with old and as well new Google API console.
public class GCM {
private static final String TAG = "GCM";
public static final String EXTRA_MESSAGE = "message";
public static final String PROPERTY_REG_ID = "registration_id";
private static final String PROPERTY_APP_VERSION = "appVersion";
private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
Context mContext=null;
Activity mActivity=null;
String SENDER_ID = "44843754432";
GoogleCloudMessaging gcm;
String regid;
public GCM(Context ctx, Activity act){
mContext=ctx;
mActivity=act;
gcm = GoogleCloudMessaging.getInstance(ctx);
}
public String getRegistrationId(Context context) {
final SharedPreferences prefs = context.getSharedPreferences("DealsGeo_GCM", Context.MODE_PRIVATE);
String registrationId = prefs.getString(PROPERTY_REG_ID, PROPERTY_REG_ID);
if (registrationId.isEmpty()) {
Log.i(TAG, "Registration not found.");
return "";
}
int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
int currentVersion = getAppVersion(context);
if (registeredVersion != currentVersion) {
Log.i(TAG, "App version changed.");
return "";
}
return registrationId;
}
public boolean checkPlayServices() {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mContext);
if (resultCode != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
GooglePlayServicesUtil.getErrorDialog(resultCode, mActivity, PLAY_SERVICES_RESOLUTION_REQUEST).show();
} else {
Log.i(TAG, "This device is not supported.");
return false;
}
return false;
}
return true;
}
private static int getAppVersion(Context context) {
try {
PackageInfo packageInfo = context.getPackageManager()
.getPackageInfo(context.getPackageName(), 0);
return packageInfo.versionCode;
} catch (NameNotFoundException e) {
throw new RuntimeException("Could not get package name: " + e);
}
}
public void registerInBackground() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... arg0) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(mContext);
}
Log.i(TAG,"Registering "+SENDER_ID);
regid = gcm.register(SENDER_ID);
storeRegistrationId(mContext, regid);
} catch (IOException ex) {
Log.i(TAG,"Error: "+ex.getMessage());
}
return msg;
}
}.execute(null, null, null);
}
private void storeRegistrationId(Context context, String regId) {
final SharedPreferences prefs = context.getSharedPreferences("DealsGeo_GCM", Context.MODE_PRIVATE);
int appVersion = getAppVersion(mContext);
Log.i(TAG, "Saving regId on app version " + appVersion);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PROPERTY_REG_ID, regId);
editor.putInt(PROPERTY_APP_VERSION, appVersion);
editor.commit();
}
}
You have a URL like https://code.google.com/apis/console/?noredirect#project:581054524740:access in your project google console, so you must put the sender code : 581054524740
Perhaps you forgot a digit when copying the Google API project ID.
Your project ID - 44843754432 - has 11 digits, while all the project IDs I've seen had 12 digits.