Can i make a background service that always runs a speech recognition - java

i want to make an application that i can run it over voice like siri or google assistant, so in order to make it i have to implement the code in a background or foreground service which will run speech recognizer
so is this possible,because of the limitation of the working services on android Oreo and higher this make me stuck in the middle of nowhere
and i'm not so professional so i could figure this out myself
i have tried to make a foreground service with IntentService so that it could make the work on the background without freezing the UI
and the Speech recognizer didn't work
package com.example.intentservice;
import android.app.IntentService;
import android.app.Notification;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.SystemClock;
import android.speech.RecognitionListener;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.Nullable;
import java.util.List;
import java.util.Locale;
import static com.example.intentservice.App.CHANNEL_ID;
public class ExampleService extends IntentService {
public static final String TAG = "ExampleService";
private PowerManager.WakeLock wakeLock;
private TextToSpeech textToSpeech;
private SpeechRecognizer recognizer;
#Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate");
initRec();
startListening();
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ExampleService:wakelock");
wakeLock.acquire();
Log.e(TAG, "wakelock acquired");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification notification = new Notification.Builder(this, CHANNEL_ID)
.setContentTitle("noti")
.setContentText("running")
.build();
startForeground(1, notification);
}
}
private void startListening() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS,1);
recognizer.startListening(intent);
}
private void initRec() {
if (SpeechRecognizer.isRecognitionAvailable(this)) {
recognizer = SpeechRecognizer.createSpeechRecognizer(this);
recognizer.setRecognitionListener(new RecognitionListener() {
#Override
public void onReadyForSpeech(Bundle bundle) {
}
#Override
public void onBeginningOfSpeech() {
}
#Override
public void onRmsChanged(float v) {
}
#Override
public void onBufferReceived(byte[] bytes) {
}
#Override
public void onEndOfSpeech() {
}
#Override
public void onError(int i) {
}
#Override
public void onResults(Bundle bundle) {
List<String> res = bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
Toast.makeText(ExampleService.this, res.get(0), Toast.LENGTH_SHORT).show();
}
#Override
public void onPartialResults(Bundle bundle) {
}
#Override
public void onEvent(int i, Bundle bundle) {
}
});
}
}
public ExampleService() {
super("ExampleService");
// to create service again
setIntentRedelivery(true);
}
// this just to test the code
#Override
protected void onHandleIntent(#Nullable Intent intent) {
Log.e(TAG, "onHandleIntent");
String string = intent.getStringExtra("key");
for (int i = 0; i < 10; i++) {
Log.e(TAG, string + "-" + i);
// Toast.makeText(this, "i", Toast.LENGTH_SHORT).show();
SystemClock.sleep(1000);
}
}
#Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy");
wakeLock.release();
Log.e(TAG, "wakelock realised");
}
}

Related

How to make my virtual assistant app run always in the background? (android studio)

I created a virtual assistant app in Android Studio and it working fine until I exit the app window and the process stops. I want to make the app run in the background always so when it get's the wake word anytime it will respond. I tried using a Service but I couldn't make it work.
Can you help me please?
This is my code:
package com.eylon.jarvis;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.speech.RecognitionListener;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import java.util.ArrayList;
import java.util.Locale;
import ai.picovoice.porcupine.Porcupine;
import ai.picovoice.porcupine.PorcupineActivationException;
import ai.picovoice.porcupine.PorcupineActivationLimitException;
import ai.picovoice.porcupine.PorcupineActivationRefusedException;
import ai.picovoice.porcupine.PorcupineActivationThrottledException;
import ai.picovoice.porcupine.PorcupineException;
import ai.picovoice.porcupine.PorcupineInvalidArgumentException;
import ai.picovoice.porcupine.PorcupineManager;
import ai.picovoice.porcupine.PorcupineManagerCallback;
enum AppState {
STOPPED,
WAKEWORD,
STT
}
public class MainActivity extends AppCompatActivity {
private static final String ACCESS_KEY = "Oc8ZOSkVtJHWKhVW3iGMedHDSCSXn6P4vQtrQBl8hNLXwLmxLhs2AA==";
private PorcupineManager porcupineManager = null;
TextView textView;
ToggleButton button;
private SpeechRecognizer speechRecognizer;
private Intent speechRecognizerIntent;
private AppState currentState;
private void displayError(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
private final PorcupineManagerCallback porcupineManagerCallback = new PorcupineManagerCallback() {
#Override
public void invoke(int keywordIndex) {
runOnUiThread(() -> {
textView.setText("");
try {
// need to stop porcupine manager before speechRecognizer can start listening.
porcupineManager.stop();
} catch (PorcupineException e) {
displayError("Failed to stop Porcupine.");
return;
}
speechRecognizer.startListening(speechRecognizerIntent);
currentState = AppState.STT;
});
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text1);
button = findViewById(R.id.button1);
if (!SpeechRecognizer.isRecognitionAvailable(this)) {
displayError("Speech Recognition not available.");
}
// Creating the Intent of the Google speech to text and adding extra variables.
speechRecognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE, "en-US");
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "en-US");
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_ONLY_RETURN_LANGUAGE_PREFERENCE, "en-US");
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Start speaking");
try {
porcupineManager = new PorcupineManager.Builder()
.setAccessKey(ACCESS_KEY)
.setKeyword(Porcupine.BuiltInKeyword.JARVIS)
.setSensitivity(0.7f)
.build(getApplicationContext(), porcupineManagerCallback);
} catch (PorcupineInvalidArgumentException e) {
onPorcupineInitError(
String.format("%s\nEnsure your accessKey '%s' is a valid access key.", e.getMessage(), ACCESS_KEY)
);
} catch (PorcupineActivationException e) {
onPorcupineInitError("AccessKey activation error");
} catch (PorcupineActivationLimitException e) {
onPorcupineInitError("AccessKey reached its device limit");
} catch (PorcupineActivationRefusedException e) {
onPorcupineInitError("AccessKey refused");
} catch (PorcupineActivationThrottledException e) {
onPorcupineInitError("AccessKey has been throttled");
} catch (PorcupineException e) {
onPorcupineInitError("Failed to initialize Porcupine " + e.getMessage());
}
currentState = AppState.STOPPED;
}
private void onPorcupineInitError(final String errorMessage) {
runOnUiThread(() -> {
TextView errorText = findViewById(R.id.text1);
errorText.setText(errorMessage);
ToggleButton recordButton = findViewById(R.id.button1);
recordButton.setChecked(false);
recordButton.setEnabled(false);
});
}
#Override
protected void onStop() {
if (button.isChecked()) {
stopService();
button.toggle();
speechRecognizer.destroy();
}
super.onStop();
}
private boolean hasRecordPermission() {
return ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
== PackageManager.PERMISSION_GRANTED;
}
private void requestRecordPermission() {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO},
0);
}
#SuppressLint("SetTextI18n")
private void playback(int milliSeconds) {
speechRecognizer.stopListening();
currentState = AppState.WAKEWORD;
new Handler(Looper.getMainLooper()).postDelayed(() -> {
if (currentState == AppState.WAKEWORD) {
porcupineManager.start();
textView.setText("Listening for " + Porcupine.BuiltInKeyword.JARVIS + " ...");
}
}, milliSeconds);
}
private void stopService() {
if (porcupineManager != null) {
try {
porcupineManager.stop();
} catch (PorcupineException e) {
displayError("Failed to stop porcupine.");
}
}
textView.setText("");
speechRecognizer.stopListening();
speechRecognizer.destroy();
currentState = AppState.STOPPED;
}
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String[] permissions,
#NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length == 0 || grantResults[0] == PackageManager.PERMISSION_DENIED) {
displayError("Microphone permission is required for this app!");
requestRecordPermission();
} else {
speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this);
speechRecognizer.setRecognitionListener(new SpeechListener());
playback(0);
}
}
public void process(View view) {
if (button.isChecked()) {
if (hasRecordPermission()) {
speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this);
speechRecognizer.setRecognitionListener(new SpeechListener());
playback(0);
} else {
requestRecordPermission();
}
} else {
stopService();
}
}
private class SpeechListener implements RecognitionListener {
#Override
public void onReadyForSpeech(Bundle params) {
}
#Override
public void onBeginningOfSpeech() {
}
#Override
public void onRmsChanged(float rmsdB) {
}
#Override
public void onBufferReceived(byte[] buffer) {
}
#Override
public void onEndOfSpeech() {
}
#SuppressLint("SwitchIntDef")
#Override
public void onError(int error) {
switch (error) {
case SpeechRecognizer.ERROR_AUDIO:
displayError("Error recording audio.");
break;
case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
displayError("Insufficient permissions.");
break;
case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
case SpeechRecognizer.ERROR_NETWORK:
displayError("Network Error.");
break;
case SpeechRecognizer.ERROR_NO_MATCH:
if (button.isChecked()) {
displayError("No recognition result matched.");
playback(1000);
}
case SpeechRecognizer.ERROR_CLIENT:
return;
case SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
displayError("Recognition service is busy.");
break;
case SpeechRecognizer.ERROR_SERVER:
displayError("Server Error.");
break;
case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
displayError("No speech input.");
break;
default:
displayError("Something wrong occurred.");
}
stopService();
button.toggle();
}
#Override
public void onResults(Bundle results) {
ArrayList<String> data = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
audioResponseSelecting(data.get(0).toLowerCase(Locale.ROOT));
textView.setText(data.get(0));
playback(3000);
}
#Override
public void onPartialResults(Bundle partialResults) {
ArrayList<String> data = partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
audioResponseSelecting(data.get(0).toLowerCase(Locale.ROOT));
textView.setText(data.get(0));
}
#Override
public void onEvent(int eventType, Bundle params) {
}
}
// The response selecting function.
public void audioResponseSelecting(String transcript)
{
if (transcript.equals(("Good morning").toLowerCase(Locale.ROOT)))
{
executeResponse(R.raw.good_morning);
}
else if (transcript.equals(("Who is your creator").toLowerCase(Locale.ROOT)))
{
executeResponse(R.raw.creator);
}
}
// The audio file response execution function.
public void executeResponse(final int audio)
{
MediaPlayer response = MediaPlayer.create(MainActivity.this, audio);
response.start();
}
}
By design, all SpeechRecognizer's methods "must be invoked only from the main application thread."
Main application thread is also referred to as "UI thread".
This means that SpeechRecognizer cannot run in a service.

Task is not yet completed while using firebase

I am creating app that stores music from device to firebase. I am using Firebase storage, and database, all is working but but when I upload it it shows me progress bar with progress and after that app crashes I found that file is uploaded in storage but not came it database
Getting following error:
2020-07-08 13:57:47.766 32456-32456/xyz.hannanshaikh.firebasemusic E/AndroidRuntime: FATAL EXCEPTION: main
Process: xyz.hannanshaikh.firebasemusic, PID: 32456
java.lang.IllegalStateException: Task is not yet complete
at com.google.android.gms.common.internal.Preconditions.checkState(Unknown Source:29)
at com.google.android.gms.tasks.zzu.zzb(Unknown Source:121)
at com.google.android.gms.tasks.zzu.getResult(Unknown Source:12)
at xyz.hannanshaikh.firebasemusic.MainActivity$3.onSuccess(MainActivity.java:105)
at xyz.hannanshaikh.firebasemusic.MainActivity$3.onSuccess(MainActivity.java:98)
at com.google.firebase.storage.StorageTask.lambda$new$0(com.google.firebase:firebase-storage##19.1.1:123)
at com.google.firebase.storage.StorageTask$$Lambda$1.raise(Unknown Source:6)
at com.google.firebase.storage.TaskListenerImpl.lambda$onInternalStateChanged$2(com.google.firebase:firebase-storage##19.1.1:90)
at com.google.firebase.storage.TaskListenerImpl$$Lambda$3.run(Unknown Source:6)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:201)
at android.app.ActivityThread.main(ActivityThread.java:6823)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873)
Here is code:
package xyz.hannanshaikh.firebasemusic;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import android.Manifest;
import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.OpenableColumns;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.OnProgressListener;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.UploadTask;
import com.karumi.dexter.Dexter;
import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.PermissionDeniedResponse;
import com.karumi.dexter.listener.PermissionGrantedResponse;
import com.karumi.dexter.listener.PermissionRequest;
import com.karumi.dexter.listener.single.PermissionListener;
import java.util.Objects;
public class MainActivity extends AppCompatActivity {
private static final String LOG_TAG = "MainActivity";
private boolean checkPermission = false;
Uri uri;
String songName, songUrl;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
if (item.getItemId() == R.id.nav_upload) {
if (validatePermission()) {
pickSongs();
}
}
return super.onOptionsItemSelected(item);
}
private void pickSongs() {
Intent intent_upload = new Intent();
intent_upload.setType("audio/*");
intent_upload.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(intent_upload, 1);
Log.d(LOG_TAG,"Intent sent for result");
}
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1) {
if (resultCode == RESULT_OK) {
uri = data.getData();
Cursor mCursor = getApplicationContext().getContentResolver()
.query(uri, null, null, null);
int indexedName = Objects.requireNonNull(mCursor).getColumnIndex(OpenableColumns.DISPLAY_NAME);
mCursor.moveToFirst();
songName = mCursor.getString(indexedName);
mCursor.close();
uploadToFirebaseStorage();
}
}
super.onActivityResult(requestCode, resultCode, data);
}
private void uploadToFirebaseStorage() {
Log.d(LOG_TAG,"Method: uploadToFirebaseStorage");
StorageReference storageReference = FirebaseStorage.getInstance().getReference()
.child("Songs").child(Objects.requireNonNull(uri.getLastPathSegment()));
final ProgressDialog progressDialog = new ProgressDialog(this);
progressDialog.show();
storageReference.putFile(uri).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
Task<Uri> uriTask = taskSnapshot.getStorage().getDownloadUrl();
while (!uriTask.isSuccessful()) {
Uri urlSong = uriTask.getResult();
songUrl = Objects.requireNonNull(urlSong).toString();
uploadDetailsToDatabase();
progressDialog.dismiss();
}
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
progressDialog.dismiss();
}
}).addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
#Override
public void onProgress(#NonNull UploadTask.TaskSnapshot taskSnapshot) {
double progress = (100.0 * taskSnapshot.getBytesTransferred()) / taskSnapshot.getTotalByteCount();
int currentProgress = (int) progress;
progressDialog.setMessage("Uploaded: " + currentProgress + "%");
}
});
}
private void uploadDetailsToDatabase() {
Songs songObj = new Songs(songName, songUrl);
FirebaseDatabase.getInstance().getReference("Songs")
.push().setValue(songObj).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()){
Toast.makeText(MainActivity.this,"Song Uploaded",Toast.LENGTH_SHORT).show();
}
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(MainActivity.this, e.getMessage(),Toast.LENGTH_SHORT).show();
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.custom_menu, menu);
return super.onCreateOptionsMenu(menu);
}
//Validate permission with Dexter library
private boolean validatePermission() {
Dexter.withContext(MainActivity.this)
.withPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
.withListener(new PermissionListener() {
#Override
public void onPermissionGranted(PermissionGrantedResponse permissionGrantedResponse) {
checkPermission = true;
}
#Override
public void onPermissionDenied(PermissionDeniedResponse permissionDeniedResponse) {
checkPermission = false;
}
#Override
public void onPermissionRationaleShouldBeShown(PermissionRequest permissionRequest, PermissionToken permissionToken) {
permissionToken.continuePermissionRequest();
}
}).check();
return checkPermission;
}
}
I think the way you try to upload the song in the database (in Realtime Database I suppose) is wrong. In the method uploadDetailsToDatabase, write:
Declare the reference, in the begining of the class:
DatabaseReference songsDatabase;
Initialize the reference, in onCreate() method, with the path of the song table inside the database:
songsDatabase = FirebaseDatabase.getInstance().getReference("songs");
Add the song object in the realtime database:
Songs songObj = new Songs(songName, songUrl);
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
songsDatabase.child(songName).setValue(songObj).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()){
Toast.makeText(MainActivity.this,"Song Uploaded",Toast.LENGTH_SHORT).show();
}
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(MainActivity.this, e.getMessage(),Toast.LENGTH_SHORT).show();
}
});
In that way, you create a data(table) in the Realtime Databse, with the name of the song, as the name of this table (songName). Inside this table, you have 2 fields: The song name, and the url of the song.
You may find usefull this answer:
how to resolve java.lang.IllegalStateException: Task is not yet complete Error when uploading image to Firebase storage?
You should avoid using while loop inside a callback listener, instead you can check on the other listener method onComplete() if the upload has been successful or not.

Trying to keep MQTT client connection even when app is closed

I am currently using MQTT protocol in my android app and I am able to receive messages when my app is open or when it is running in the background however when i close my app completely the connection is lost and I am not longer able to receive messages. I need to be able to still recieve messages as I want the message to trigger an activity in my app to launch. Here are the files that I am using to implement MQTT. MQTTHelper class: EDIT. Ive have tried to implement a service but it still doesnt seem to be working.Does Anyone know what I am doing wrong?
package com.example.carcrashdetection.helpers;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.DisconnectedBufferOptions;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
public class MqttHelper extends Service {
public MqttAndroidClient mqttAndroidClient;
BroadcastReceiver m_ScreenOffReceiver;
final String serverUri = "tcp://hairdresser.cloudmqtt.com:15767";
final String clientId = "CarCrashDetection";
final String subscriptionTopic = "Topic/+";
final String username = "username";
final String password = "password";
#Override
public void onCreate() {
super.onCreate();
registerReceiver();
new Thread(new Runnable() {
#Override
public void run() {
MqttHelper.this.connect();
}
}).start();
}
public MqttHelper(Context context){
mqttAndroidClient = new MqttAndroidClient(context, serverUri, clientId);
mqttAndroidClient.setCallback(new MqttCallback() {
public void connectComplete(boolean b, String s) {
}
#Override
public void connectionLost(Throwable throwable) {
}
#Override
public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
Log.w("Mqtt", mqttMessage.toString());
}
#Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
});
connect();
}
public void setCallback(MqttCallback callback) {
mqttAndroidClient.setCallback(callback);
}
private void connect(){
MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setAutomaticReconnect(true);
mqttConnectOptions.setCleanSession(false);
mqttConnectOptions.setUserName(username);
mqttConnectOptions.setPassword(password.toCharArray());
try {
mqttAndroidClient.connect(mqttConnectOptions, null, new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
DisconnectedBufferOptions disconnectedBufferOptions = new DisconnectedBufferOptions();
disconnectedBufferOptions.setBufferEnabled(true);
disconnectedBufferOptions.setBufferSize(100);
disconnectedBufferOptions.setPersistBuffer(false);
disconnectedBufferOptions.setDeleteOldestMessages(false);
mqttAndroidClient.setBufferOpts(disconnectedBufferOptions);
subscribeToTopic();
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.w("Mqtt", "Failed to connect to: " + serverUri + exception.toString());
}
});
} catch (MqttException ex){
ex.printStackTrace();
}
}
private void subscribeToTopic() {
try {
mqttAndroidClient.subscribe(subscriptionTopic, 0, null, new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
Log.w("Mqtt","Subscribed!");
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.w("Mqtt", "Subscribed fail!");
}
});
} catch (MqttException ex) {
System.err.println("Exception whilst subscribing");
ex.printStackTrace();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//do something
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void registerReceiver(){
m_ScreenOffReceiver = new BroadcastReceiver(){
#Override
public void onReceive(final Context context, Intent intent){
//Log.d(TAG,"onReceive of Wifi_State_Change called");
if(intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION))
{
int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);
if(wifiState != WifiManager.WIFI_STATE_ENABLED)
return;
final WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
String ssid = wifiInfo.getSSID();
Toast.makeText(context, "active wifi:" + ssid, Toast.LENGTH_SHORT).show();
//You can connect to the your mqtt broker again:
connect();
}
}, 10000);
}
}
};
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
registerReceiver(m_ScreenOffReceiver, intentFilter);
}
#Override
public void onDestroy() {
if(mqttAndroidClient!=null) {
/*unregisterResources is needed,otherwise receive this error:
has leaked ServiceConnection org.eclipse.paho.android.service.MqttAndroidClient*/
try {
mqttAndroidClient.unregisterResources();
mqttAndroidClient.close();
mqttAndroidClient.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
unregisterReceiver(m_ScreenOffReceiver);
m_ScreenOffReceiver = null;
super.onDestroy();
}
}
MainActivity
private void startMqtt(){
mqttHelper = new MqttHelper(getApplicationContext());
mqttHelper.setCallback(new MqttCallbackExtended(){
#Override
public void connectComplete(boolean b, String s) {
Toast.makeText(MainActivity.this, "connected ", Toast.LENGTH_SHORT).show();
}
public void connectionLost(Throwable throwable){
Toast.makeText(MainActivity.this,"Disconnected", Toast.LENGTH_SHORT).show();
}
public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception{
Log.e("message ", String.valueOf(mqttMessage));
Toast.makeText(MainActivity.this, "Crash Occurred", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(MainActivity.this, alert.class);
startActivity(intent);
}
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken){
}
});
}
I've been searching for a while now but cant seem to find anything that works. I thought it would be an easy fix but its hasn't been for me. Any help would be much appreciated.
Your options will be to implement a server that will send your device push notifications and then your app can fetch the message once it is open again.
Alternatively you can implement a background service: https://developer.android.com/guide/components/services

Sharing one service between two activities without destroying it?

I'm working on an application to communicate with a raspberry pi. The app communicates with the server via a socket connection.
I'm currently able to building up the communication and holding it in a service. But my goal is to change from activity A to activity B. The problem is, that my application destroys the service when im switching the activities. Is there a way to avoid this?
Activity A starts the service and when im starting Activity B i bind it to the activity to call the methods in the service class. But it isn't the same instance.
How could i solve this?
Code
ConnectActivity
package com.example.domen.twitchdronev3;
import android.content.Intent;
import android.os.Parcelable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class ConnectActivity extends AppCompatActivity {
private int port = 3000;
private String ip = "localhost";
private Intent serviceIntent, activityIntent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_connect);
Toast.makeText(getApplicationContext(), "Starte App...", Toast.LENGTH_LONG).show();
activityIntent = new Intent(this, ControllActivity.class);
serviceIntent = new Intent(this, ClientService.class);
serviceIntent.putExtra("ip", ip);
serviceIntent.putExtra("port", port);
Button btnActivity = (Button) findViewById(R.id.changeActivity);
btnActivity.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startService(serviceIntent);
activityIntent.putExtra("Intent", (Parcelable) serviceIntent);
startActivity(activityIntent);
}
});
}
// destroys the Activity
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(getApplicationContext(), "Destroy (ConnectActivity)", Toast.LENGTH_LONG).show();
}
// Activity stopping
#Override
protected void onStop() {
super.onStop();
}
}
ControllActivity
package com.example.domen.twitchdronev3;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class ControllActivity extends AppCompatActivity {
public static final String TAG = "ControllActivity";
private ClientService clientservice;
private Intent serviceIntent;
private boolean isBound;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_controll);
Bundle extras = getIntent().getExtras();
serviceIntent = extras.getParcelable("Intent");
Button btnDisconnect = (Button) findViewById(R.id.btnDisconnect);
btnDisconnect.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
doBindToService();
}
});
}
// destroys the Activity
#Override
public void onDestroy(){
super.onDestroy();
Toast.makeText(getApplicationContext(), "Destroy (ControllActivity)", Toast.LENGTH_LONG).show();
doUnbindToService();
stopService(serviceIntent);
}
// Functions to BIND and UNBIND the SERVICE
// bind to Service
public void doBindToService(){
if(!isBound) {
Toast.makeText(this, "(doBindToService) Binding...", Toast.LENGTH_LONG).show();
isBound = bindService(serviceIntent, myConnection, Context.BIND_AUTO_CREATE);
}
}
// Unbind to Service
public void doUnbindToService(){
Toast.makeText(this, "(doUnbindToService) Unbinding...", Toast.LENGTH_LONG).show();
unbindService(myConnection);
isBound = false;
}
private ServiceConnection myConnection = new ServiceConnection(){
public static final String TAG = "ConnectActivity";
#Override
public void onServiceConnected(ComponentName className, IBinder service){
Log.i(TAG, "BOUND SERVICE CONNECTED");
clientservice = ((ClientService.ClientBinder) service).getService();
isBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name){
Log.i(TAG, "BOUND SERVICE DISCONNECTED");
clientservice = null;
isBound = false;
}
};
}
ClientService
package com.example.domen.twitchdronev3;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
/**
* Created by ViTrex on 12.02.2018.
*/
public class ClientService extends Service {
private static final String TAG = "ClientService";
private final IBinder mBinder = new ClientBinder();
private PrintWriter out;
private Socket socket;
private String ip = "";
private int port = 0;
private Thread backgroundThread;
// the class used for the client binder
public class ClientBinder extends Binder {
ClientService getService() {
// returns the instance of ClientService
// so the client can access the public methods
return ClientService.this;
}
}
//SETTER
public void setIP(String ip) {
this.ip = ip;
}
public void setPort(int port) {
this.port = port;
}
//GETTER
public String getIP() {
return this.ip;
}
public int getPort() {
return this.port;
}
#Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate...");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "onStartCommand", Toast.LENGTH_LONG).show();
if(intent != null){
setIP(intent.getStringExtra("ip"));
setPort(intent.getIntExtra("port", 3000));
}
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind called...");
Toast.makeText(this, getIP() + " " + getPort(), Toast.LENGTH_LONG).show();
backgroundThread = new Thread(new Runnable() {
#Override
public void run() {
buildConnection();
}
});
backgroundThread.start();
return mBinder;
}
private void buildConnection() {
Log.i(TAG, "Try to build up a connection ...");
synchronized (this) {
try {
this.socket = new Socket(getIP(), getPort());
this.out = new PrintWriter(this.socket.getOutputStream(), true);
Log.i(TAG, "Connected");
} catch (IOException ioe) {
Log.i(TAG, ioe.getMessage());
this.socket = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Close Service");
Thread dummy = backgroundThread;
backgroundThread = null;
if (dummy != null)
dummy.interrupt();
if (this.socket != null) {
try {
this.out.close();
this.socket.close();
Log.i(TAG, "Close Socket");
} catch (IOException ioe) {
Log.i(TAG, "ERROR: Close Socket");
}
}
}
}
You say:
Activity A starts the service and when I'm starting Activity B I bind
it to the activity to call the methods in the service class. But it
isn't the same instance.
How do you know it is not the same instance (presumably you mean of the service instance)?
I think your code is o.k. to run on older devices (It runs o.k. on my device. pre-API26, I think permissions changed after then so maybe more code needed. See NOTE at the end of this answer).
Don't you want START_STICKY ?
I would add this code (read the Intent data that was setup by Activity A) to your ClientService.java:
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
String a_ip = intent.getStringExtra("ip");
int a_port = intent.getIntExtra("port",-1);
Log.i(TAG, "onStartCommand called...with " + "ip:" + a_ip + "and port:"+ a_port);
setIP(a_ip);
setPort(a_port);
return START_NOT_STICKY;//don't you want START_STICKY ?
}
Note: If your app targets API level 26 or higher, the system imposes
restrictions on using or creating background services unless the app
itself is in the foreground. If an app needs to create a foreground
service, the app should call StartForegroundService(). That method
creates a background service, but the method signals to the system
that the service will promote itself to the foreground. Once the
service has been created, the service must call its startForeground()
method within five seconds. From here

foreground location service with doze mode not working as expected

I have created a sample foreground service to check if it's possible to fetch location from background after every 15 mins or 30 mins. With introduction to doze mode alarm manager does not work at exact time.
In foreground service I have tried using handler post delay but that also does not work as I am getting intervals of upto 3 hrs. In the below code I have used ScheduledThreadPoolExecutor and it performed even worse. I got delay of upto 12 hours. I have tested this on android 6.0 as of now.
Can anyone suggest a work around for this?
package com.hitec16.foregroundservicetest;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.location.Location;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.FusedLocationProviderApi;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import java.text.DateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class MyService extends Service
implements LocationListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private static final String TAG = "MyService";
private static final long INTERVAL = 1000 * 10;
private static final long FASTEST_INTERVAL = 1000 * 5;
private static final long WAIT_INTERVAL = 1000 * 30;
private ScheduledThreadPoolExecutor mExecutor;
private int mCounter = 0;
private FusedLocationProviderApi fusedLocationProviderApi = LocationServices.FusedLocationApi;
LocationRequest mLocationRequest;
GoogleApiClient mGoogleApiClient;
Location mCurrentLocation;
private String mLastUpdateTime;
public MyService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
// throw new UnsupportedOperationException("Not yet implemented");
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
Looper.prepare();
onStart_(intent, flags, startId);
Looper.loop();
}
});
thread.start();
// onStart_(intent,flags,startId);
return START_STICKY;
}
private void onStart_(Intent intent, int flags, int startId) {
if (intent.getAction().equals(Constants.STARTFOREGROUND_ACTION)) {
Logger.d("Received Start Foreground Intent ");
createLocationRequest();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
connectToGoogleApi();
showNotification();
Toast.makeText(getApplicationContext(), "Service Started!", Toast.LENGTH_SHORT).show();
Logger.d("work finished");
// fetchLocationAgain();
mExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(2);
mExecutor.scheduleWithFixedDelay(new Runnable() {
#Override
public void run() {
try {
startLocationUpdates();
} catch (Exception e) {
Log.d(getClass().getSimpleName(), "Exception caught: " + e.getMessage());
}
Log.d("Test", "test");
}
}, 30, 60 * 15, TimeUnit.SECONDS);
}
}
private void showNotification() {
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setAction(Constants.MAIN_ACTION);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Fused Location Service")
.setTicker("ticker text")
.setContentText("My Location")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.setContentIntent(pendingIntent)
.setOngoing(true)
.build();
startForeground(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE,
notification);
}
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(INTERVAL);
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
protected void stopLocationUpdates() {
LocationServices.FusedLocationApi.removeLocationUpdates(
mGoogleApiClient, MyService.this);
mCounter = 0;
Logger.d("Location update stopped .......................");
}
#Override
public void onConnected(#Nullable Bundle bundle) {
Logger.d("onConnected - isConnected ...............: " + mGoogleApiClient.isConnected());
try {
startLocationUpdates();
} catch (Exception e) {
Log.d(getClass().getSimpleName(), "Exception caught: " + e.getMessage());
}
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
Logger.d("Connection failed: " + connectionResult.toString());
}
#Override
public void onLocationChanged(Location location) {
if (mCounter < 3) {
Log.d(getClass().getSimpleName(), "returned....");
mCounter++;
return;
}
Logger.d("Firing onLocationChanged..............................................");
mCurrentLocation = location;
mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
Logger.d("Location latitude and longitude :" + location.getLatitude() + " ," + location.getLongitude()
+ " , Accuracy : " + location.getAccuracy() + ", location provider : " + location.getProvider() + "Time : " + mLastUpdateTime);
stopLocationUpdates();
}
protected void startLocationUpdates() throws Exception {
PendingResult<Status> pendingResult = LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this, Looper.getMainLooper());
Logger.d("Location update started ..............: ");
}
#Override
public void onDestroy() {
disconnectFromGoogleApi();
Log.d(getClass().getSimpleName(), "on destroy called");
mExecutor.shutdown();
super.onDestroy();
}
private void connectToGoogleApi() {
Logger.d("connectToGoogleApi fired ..............");
mGoogleApiClient.connect();
}
private void disconnectFromGoogleApi() {
Logger.d("disConnectFromGoogleApi fired ..............");
mGoogleApiClient.disconnect();
}
}

Categories

Resources