I've made an Android app that main feature is to play audio stream from my server. But for some reason music stops playing after about 20 minutes and the app is throwing ProtocolException (logs from app on Android Studio logcat image).
Android Studio logcat
This error is even more weird because it occurs only on some devices. Error occurs on multiple Xiaomi devices (all with Android 10) and Samsung Galaxy S9 (also with Android 10) but on Samsung Galaxy S10 (Android 10) and Huawai tablet that error doesn't occur and music is being played as long as user does not stop it.
This is my PlayerService class code that is responsible for running MediaPlayer as a service:
public class PlayerService extends Service {
private static final String CHANNEL_ID = "PlayerServiceChannel";
private static final int SERVICE_ID = 1;
private MediaPlayer player;
private Notification notification;
private JsonObjectHandler jsonObjectHandler;
private int streamIndex = 9999;
private Messenger messenger;
private PendingIntent pendingIntent;
private NotificationManager notificationManager;
private PendingIntent closeApp;
private WifiManager.WifiLock wifiLock;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
JSONObject jsonObject = new JSONObject(Objects.requireNonNull(intent.getStringExtra("JsonInfo")));
jsonObjectHandler = new JsonObjectHandler(jsonObject);
} catch (JSONException e) {
jsonObjectHandler = null;
}
return START_REDELIVER_INTENT;
}
#Override
public void onCreate() {
super.onCreate();
wifiLock = ((WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "playerServiceWiFiLock");
HandlerThread thread = new HandlerThread("ServiceStartArgument", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
Looper mServiceLooper = thread.getLooper();
ServiceHandler mServiceHandler = new ServiceHandler(mServiceLooper);
messenger = new Messenger(mServiceHandler);
PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
case TelephonyManager.CALL_STATE_OFFHOOK:
stopForeground(true);
stopSelf();
break;
case TelephonyManager.CALL_STATE_IDLE:
break;
}
super.onCallStateChanged(state, incomingNumber);
}
};
TelephonyManager mgr = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
if (mgr != null) {
mgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
createNotificationChannel();
Intent notificationIntent = new Intent(this, LoginActivity.class);
pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
stopSelf();
unregisterReceiver(this);
System.exit(0);
}
};
IntentFilter intentFilter = new IntentFilter("android.intent.CLOSE_APP");
registerReceiver(broadcastReceiver, intentFilter);
Intent intentClose = new Intent("android.intent.CLOSE_APP");
closeApp = PendingIntent.getBroadcast(this, 0, intentClose, 0);
updateNotification(streamIndex);
startForeground(SERVICE_ID, notification);
}
private NotificationCompat.Builder createNotification(int streamIndex) {
String defaultValue;
if (jsonObjectHandler == null || streamIndex == 9999) {
defaultValue = "";
} else {
defaultValue =
jsonObjectHandler.getPlaylistButtons().get(streamIndex).getButtonName();
}
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ikonastream48)
.setContentIntent(pendingIntent)
.setContentTitle(getString(R.string.app_name))
.setContentText(defaultValue)
.setDefaults(0)
.setSound(null)
.addAction(R.drawable.ic_close_black, getString(R.string.close), closeApp);
}
private void updateNotification(int streamIndex) {
notification = createNotification(streamIndex).build();
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
assert notificationManager != null;
notificationManager.notify(SERVICE_ID, notification);
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID, "Example Service Channel", NotificationManager.IMPORTANCE_DEFAULT);
serviceChannel.setSound(null, null);
notificationManager = getSystemService(NotificationManager.class);
if (notificationManager != null) {
notificationManager.createNotificationChannel(serviceChannel);
}
}
}
private void stopServicePlayer() {
if (player != null) {
player.stop();
player.reset();
player.release();
player = null;
}
stopForeground(true);
}
#Override
public void onDestroy() {
super.onDestroy();
stopServicePlayer();
if (wifiLock.isHeld()) wifiLock.release();
}
private void sendMessageBroadcast(Intent intent) {
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
if (streamIndex != 9999) {
Intent intent1 = new Intent("streamIndex");
intent1.putExtra("INDEX", streamIndex);
sendMessageBroadcast(intent1);
}
return messenger.getBinder();
}
private final class ServiceHandler extends Handler {
ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0) {
player = new MediaPlayer();
player.setOnErrorListener((mp, what, extra) -> false);
player.setAudioAttributes(new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build());
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
streamIndex = msg.arg1;
updateNotification(streamIndex);
try {
player.setOnPreparedListener(mediaPlayer -> {
mediaPlayer.start();
Intent intent = new Intent("streamIndex");
intent.putExtra("INDEX", streamIndex);
sendMessageBroadcast(intent);
});
player.setDataSource(jsonObjectHandler
.getPlaylistButtons()
.get(streamIndex)
.getButtonStreamAddress());
} catch (IOException e) {
e.printStackTrace();
}
player.prepareAsync();
if (!wifiLock.isHeld()) wifiLock.acquire();
} else if (msg.what == 1) {
Intent intent2 = new Intent("streamIndex");
intent2.putExtra("INDEX", streamIndex);
sendMessageBroadcast(intent2);
}
}
}
}
Thank you for all responses in advance!
P.S. The code was written few years ago (at the beginning of my programming journey), so I'm aware that it looks bad and need to be rewritten in better way.
Disable caches and retry :
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "audio/mp3");
headers.put("Accept-Ranges", "bytes");
headers.put("Status", "206");
headers.put("Cache-control", "no-cache");
Uri uri = Uri.parse(yourContentUrl);
player.setDataSource(getApplicationContext(), uri, headers);
The problem was on the server side. When the stream was switched from CBR to the VBR in the Lame encoder, the problem stop occurring.
Related
In our android webview app, we start a foreground service (RingtonePlayingservice.class) from our firebase.class on notification action. The foreground service in turn starts playing the ringtone vide mediaplayer. The ringtone runs to like 10 seconds and stops, so we implemented setloop parameter, but still the mediaplayer just stops without looping at all. We are at loss as where we are going wrong. The codes are as given below
Firebase.Class (summarized)
public class Firebase extends FirebaseMessagingService {
public static Ringtone ringtone;
Intent i = new Intent(this, RingtonePlayingService.class);
this.startForegroundService(i);
}
Ringtoneplayingservice class
public class RingtonePlayingService extends Service {
private static final String TAG = RingtonePlayingService.class.getSimpleName();
private static final String URI_BASE = RingtonePlayingService.class.getName() + ".";
public static final String ACTION_DISMISS = URI_BASE + "ACTION_DISMISS";
public static final String ACTION_START = URI_BASE + "ACTION_START";
private MediaPlayer mp;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
if (intent == null) {
Log.d(TAG, "The intent is null.");
return START_REDELIVER_INTENT;
}
String action = intent.getAction();
if (ACTION_DISMISS.equals(action)) {
int notificationId = intent.getIntExtra("notificationId", 0);
dismissRingtone();
NotificationManager notificationManager =
(NotificationManager) getSystemService(getApplicationContext().NOTIFICATION_SERVICE);
notificationManager.cancel(notificationId);
}
else {
mp = MediaPlayer.create(this, Settings.System.DEFAULT_RINGTONE_URI);
mp.setlooping (true);
mp.start();
}
return START_STICKY;
}
public void dismissRingtone() {
// stop the alarm rigntone
Intent i = new Intent(this, RingtonePlayingService.class);
this.stopService(i);
}
#Override
public void onDestroy() {
super.onDestroy();
if(mp.isPlaying())
{
mp.reset();
}
else {
}
}}
Any help would be deeply appreciated
I made an Android app that should play a sound when event is received, it works when app is in focus, but when the app is closed/collapsed sound doesnt play, only standard notification.
How to start a sound/music that is placed inside the app when app with the foreground service?
Main activity:
public class MainActivity extends AppCompatActivity {
private Intent alarmServiceIntent;
private ServiceConnection sConn;
private boolean bound;
private boolean alarm = false;
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Init();
}
private void Init() {
alarmServiceIntent = new Intent(this, AlarmService.class);
sConn = new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder binder) {
bound = true;
}
public void onServiceDisconnected(ComponentName name) {
bound = false;
}
};
ContextCompat.startForegroundService(this, alarmServiceIntent);
}
private void playAlarm() {
bindService(alarmServiceIntent, sConn, BIND_AUTO_CREATE);
}
public void stopAlarm(View v) {
if (sConn != null ) {
unbindService(sConn);
}
if(alarmServiceIntent != null) {
stopService(alarmServiceIntent);
}
}
//here is the method to receive event and call playAlarm
}
Alarm service:
public class AlarmService extends Service{
private final String CHANNEL_ID = "ID";
private final String CHANNEL_NAME = "NAME";
private IBinder mBinder = new MyBinder();
private MediaPlayer player;
#Nullable
#Override
public IBinder onBind(Intent intent) {
play();
return mBinder;
}
#Override
public boolean onUnbind(Intent intent) {
onStop();
return super.onUnbind(intent);
}
#Override
public void onCreate() {
player = MediaPlayer.create(this,R.raw.alarm);
player.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
pendingIntent = PendingIntent.getActivity
(this, 0, notificationIntent, PendingIntent.FLAG_MUTABLE);
}
else
{
pendingIntent = PendingIntent.getActivity
(this, 0, notificationIntent, PendingIntent.FLAG_ONE_SHOT);
}
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("TITLE")
.setSmallIcon(R.mipmap.icon)
.setContentIntent(pendingIntent)
.build();
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel( CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(channel);
new NotificationCompat.Builder(this, CHANNEL_ID);
}
startForeground(619, notification);
return START_REDELIVER_INTENT;
}
public void play() {
if(player == null) {
player = MediaPlayer.create(this,R.raw.alarm);
player.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
}
Log.v("ALARM", "play: 12345");
player.setAudioAttributes(
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_ALARM)
.build()
);
player.setVolume(2,2);
AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
// audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
player.setLooping(true);
player.start();
}
class MyBinder extends Binder {
AlarmService getService(){
return AlarmService.this;
}
}
}
You should override onDestroy method and call stop there.
#Override
public void onDestroy() {
player.stop();
}
I have been trying to implement a way that the application detect wake word like "Hey google" or "Jarvis". I did some research and found out porcupine helps towards solving the wake word problem but now the problem is I can't seem to trigger startRecognition() to listen again for the user input and then carry forward with it. I still tried to trigger startRecognition() but then it was asking me to do speechRecognizer.Destroy() which I tried doing with the porcupine onDestroy method but then it just stopped working. Sorry if I confused anyone, I will attach my code I will really appreciate everyone's help as I have been trying to solve this problem for a while now.
Another question is what does the following line of code do?
PendingIntent contentIntent = PendingIntent.getActivity(
this,
0,
new Intent(this, MainActivity.class), // this line ?
0);
The code currently :(
public class PorcupineService extends Service {
private static final int REQUEST_RECORD_AUDIO_PERMISSION_CODE = 1;
private SpeechRecognizer speechRecognizer;
TextToSpeech textToSpeech;
String userResponse;
Float speechRate = 2f;
private static final String CHANNEL_ID = "PorcupineServiceChannel";
private PorcupineManager porcupineManager;
private int numUtterances;
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(
CHANNEL_ID,
"Porcupine",
NotificationManager.IMPORTANCE_HIGH);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(notificationChannel);
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
createNotificationChannel();
PendingIntent pendingIntent = PendingIntent.getActivity(
this,
0,
new Intent(this, MainActivity.class),
0);
numUtterances = 0;
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Wake word")
.setContentText("Service running")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentIntent(pendingIntent)
.build();
startForeground(1234, notification);
try {
porcupineManager = new PorcupineManager.Builder()
.setKeyword(Porcupine.BuiltInKeyword.JARVIS)
.setSensitivity(0.7f).build(
getApplicationContext(),
(keywordIndex) -> {
Log.i("YOU SAID IT!", "yesss");
textSpeechInitialize();
startRecognition();
listening();
numUtterances++;
PendingIntent contentIntent = PendingIntent.getActivity(
this,
0,
new Intent(this, MainActivity.class),
0);
final String contentText = numUtterances == 1 ? " time!" : " times!";
Notification n = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Wake word")
.setContentText("Detected " + numUtterances + contentText)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentIntent(contentIntent)
.build();
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
assert notificationManager != null;
notificationManager.notify(1234, n);
});
porcupineManager.start();
} catch (PorcupineException e) {
Log.e("PORCUPINE", e.toString());
}
return super.onStartCommand(intent, flags, startId);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
try {
porcupineManager.stop();
porcupineManager.delete();
speechRecognizer.destroy();
} catch (PorcupineException e) {
Log.e("PORCUPINE", e.toString());
}
super.onDestroy();
}
public void listening(){
speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this);
speechRecognizer.setRecognitionListener(new 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() {}
#Override
public void onError(int error) {
String errorMessage = getErrorText(error);
Log.i(">>> INFO", "Failed " + errorMessage);
}
#Override
public void onResults(Bundle results) {
ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
userResponse = matches.get(0);
userResponse = userResponse.toLowerCase();
toSpeak(userResponse);
}
#Override
public void onPartialResults(Bundle partialResults) {}
#Override
public void onEvent(int eventType, Bundle params) {}
});
}
public void textSpeechInitialize(){
textToSpeech = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS){
textToSpeech.setLanguage(Locale.getDefault());
textToSpeech.setSpeechRate(speechRate);
String greet = greetings();
toSpeak(greet);
startRecognition();
} else {
Toast.makeText(getApplicationContext(), "Feature not supported", Toast.LENGTH_SHORT).show();
}
}
});
}
public String getErrorText(int errorCode) {
String message;
switch (errorCode) {
...
}
return message;
}
public static String greetings(){
String s = "";
Calendar c = Calendar.getInstance();
int time = c.get(Calendar.HOUR_OF_DAY);
if (time >= 0 && time < 12){
s = "Good Morning sir! how can I help you today?";
} else if (time >= 12 && time < 16){
s = "Good Afternoon sir";
} else if (time >= 16 && time < 22){
s = "Good Evening sir";
}
else if (time >= 22 && time < 24){
s = "Hello sir, you need to take some rest... its getting late!";
}
return s;
}
private void startRecognition() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, getPackageName());
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "en");
speechRecognizer.startListening(intent);
}
private void toSpeak(String toSpeak){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Log.i(">>>Voice Info", String.valueOf(textToSpeech.getVoice()));
}
try {
textToSpeech.speak(toSpeak, TextToSpeech.QUEUE_FLUSH, null);
} catch (Exception e){
e.printStackTrace();
}
}
}
Since startRecognition() is not getting triggered, check permission to record audio in MainActivity. Your app's AndroidManifest.xml should have the following line:
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
or, you might have to explicitly ask for user permission. You can refer to this article on Medium:
https://medium.com/picovoice/no-way-google-build-your-own-wake-word-service-on-android-339a0189ff4c
Intents are objects of android.content.Intent type. It's basically a passive data structure holding an abstract description of an action to be performed(like starting activities or services). Here, it starts MainActivity class, which gets triggered immediately after the user logs in to mobile SDK apps.
new Intent(this, MainActivity.class)
For more details, you can check out the following docs:
https://developer.android.com/reference/android/app/PendingIntent#getActivity(android.content.Context,%20int,%20android.content.Intent,%20int)
https://developer.android.com/reference/android/content/Intent
https://www.vogella.com/tutorials/AndroidIntent/article.html
I am creating an Android app that notify the users when they received a SMS from selected numbers using Foreground Service. The program only notify the user when the Service is running. My Main Activity has 3 buttons: Start Service, Stop Service and Setting which lead to another Activity that let the user change their information such as password and selected number. Currently the application can read and write data to JSON fine and the data get pass to other activities through Intent, also the Broadcast Receiver for detecting SMS also work when a message in received, and since I want it to work with Foreground Service, I register it in onStartCommand and unregister it in onDestroy in the Service and not register it in Manifest. My problem is on how to pass the user data to the Broadcast Receiver, since it is register to listen to android.provider.Telephony.SMS_RECEIVED and when I try to pass Intent to it through sentBroadcast() in my Service, it does not receive any value of the user. I tried to settle in making the user public static and it worked, but not sure if this the right way to do it. Here is my current code:
MainActivity.java
Button btnStartService, btnStopService;
TextView lblStatus;
JSONHandler jsonHandler;//for handling JSON file and data
public static User user;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnStartService = findViewById(R.id.buttonStartService);
btnStopService = findViewById(R.id.buttonStopService);
jsonHandler = new JSONHandler(this);
boolean isFilePresent = isFilePresent(this, "user.json");
if(isFilePresent) {
try {
user = jsonHandler.readFromFile();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
} else {
user = new User();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestSmsPermission();
requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE,READ_EXTERNAL_STORAGE}, 1);
}
else {
startService();
}
}
public boolean isFilePresent(Context context, String fileName) {
String path = context.getFilesDir().getAbsolutePath() + "/" + fileName;
File file = new File(path);
return file.exists();
}
public void setBtnStartService(View view)
{
startService();
}
public void setBtnStopService(View view)
{
stopService();
}
public void startService() {
Intent serviceIntent = new Intent(this, ForegroundService.class);
serviceIntent.putExtra("inputExtra", "Message Service is running");
serviceIntent.putExtra("user", user);
ContextCompat.startForegroundService(this, serviceIntent);
Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
}
public void stopService() {
Intent serviceIntent = new Intent(this, ForegroundService.class);
stopService(serviceIntent);
Toast.makeText(this, "Service Stopped", Toast.LENGTH_LONG).show();
}
public void setBtnSetting(View view) {
Intent intent = new Intent(this, AuthenticateActivity.class);
intent.putExtra("user", user);
intent.putExtra("action", "setting");
startActivity(intent);
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
startService();
}
}
private void requestSmsPermission() {
String permission = Manifest.permission.RECEIVE_SMS;
int grant = ContextCompat.checkSelfPermission(this, permission);
if ( grant != PackageManager.PERMISSION_GRANTED) {
String[] permission_list = new String[1];
permission_list[0] = permission;
ActivityCompat.requestPermissions(this, permission_list, 1);
}
}
ForegroundService.java
public class ForegroundService extends Service {
public static final String CHANNEL_ID = "ForegroundServiceChannel";
SMSReceiver smsListener;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
User user = (User) intent.getSerializableExtra("user");
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
//do heavy work on a background thread
if(smsListener == null)
{
smsListener = new SMSReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
Intent i = new Intent(this, SMSReceiver.class);
i.putExtra("user", user);
sendBroadcast(i);
registerReceiver(smsListener, intentFilter);
}
//stopSelf();
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
if(null!=smsListener)
{
unregisterReceiver(smsListener);
smsListener = null;
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
}
SMSReceiver.java
public class SMSReceiver extends BroadcastReceiver {
private String msgBody;
private String text = "";
private SharedPreferences preferences;
private String sender = "";
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
// User user = (User) intent.getExtras().getSerializable("user"); not working
User user = MainActivity.user;
ArrayList<String> banks = user.getBankList();
if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) {
Toast.makeText(context, "message received", Toast.LENGTH_SHORT).show();
Bundle bundle = intent.getExtras();
try {
if (bundle != null) {
final Object[] pdus = (Object[]) bundle.get("pdus");
SmsMessage smsMessage;
for (int i = 0; i < pdus.length; i++) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
smsMessage = SmsMessage.createFromPdu((byte[]) pdus[i], bundle.getString("format"));
else smsMessage = SmsMessage.createFromPdu((byte[]) pdus[i]);
msgBody = smsMessage.getMessageBody();
sender = smsMessage.getOriginatingAddress();
if(banks.contains(sender)) {
if (msgBody.contains(user.getPattern())) {
String[] tokens = msgBody.split(" ");
for (int j = 0; j < tokens.length; j++) {
if (tokens[j].contains("API")) {
text = tokens[j];
break;
}
}
}
}
}
//MainActivity.message = msgBody;
if(!text.isEmpty()) {
Toast.makeText(context, "message is: " + text, Toast.LENGTH_SHORT).show();
}
}
} catch (Exception e) {
Log.d("Exception caught", e.getMessage());
}
}
else {
Log.i("cs.fsu", "smsReceiver : NULL");
}
}
}
Is this the right way to maintain user data throughout the application lifecycle? By making public static for every class accessible? Or is there away to pass it through Intent? Please help me out
SO, I have a MainActivity that has a button which starts a service, MyService. I want the service to show the notification with a custom title,text,icon,etc. The service is a foreground service. not a bound service. everything works fine but the only thing is about my notification is keep showing "tap for more information" and clicking on it leads me to the settings page of my application. despite having the pendingIntent on my MainActivity.
What I want is to have a Notification with custom Title,Text,icon and an action to stop the service.
this is my MainActivity.java class
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.start_tracking).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startCommand();
}
});
}
void startCommand() {
startService(new Intent(this, MyService.class));
}
}
Myservice.java file
public class MyService extends Service {
boolean mServiceIsStarted = false;
void moveToStartedState() {
Intent myIntentbuilder = new IntentBuilder(this).setmCommandId(Command.START).build();
Log.d(TAG, "moveToStartedState: Running on Android O - startForegroundService(intent)");
ContextCompat.startForegroundService(this, myIntentbuilder);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: ");
boolean containsCommand = IntentBuilder.containsCommand(intent);
routeIntentToCommand(intent);
return START_NOT_STICKY;
}
void routeIntentToCommand(Intent intent) {
if (intent != null) {
if (IntentBuilder.containsCommand(intent)) {
processCommand(IntentBuilder.getCommand(intent));
} else
commandStart();
}
}
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: ");
}
void showNotification() {
Log.d(TAG, "showNotification: ");
HandleNotification.O.createNotification(this);
}
private void processCommand(int command) {
try {
switch (command) {
case Command.START:
commandStart();
break;
case Command.STOP:
commandStop();
break;
}
} catch (Exception e) {
e(TAG, "processCommand: exception", e);
}
}
void commandStop() {
stopSelf();
stopForeground(true);
}
void commandStart() {
if (!mServiceIsStarted) {
mServiceIsStarted = true;
moveToStartedState();
return;
}
//
HandleNotification.O.createNotification(this);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
IntentBuilder.java class
#IntDef({Command.INVALID, Command.STOP, Command.START})
#Retention(RetentionPolicy.SOURCE)
#interface Command {
int INVALID = -1;
int STOP = 0;
int START = 1;
}
public class IntentBuilder {
private static final String KEY_MESSAGE = "msg";
private static final String KEY_COMMAND = "cmd";
private Context mContext;
private String mMessage;
private #Command
int mCommandId = Command.INVALID;
public static IntentBuilder getInstance(Context mContext) {
return new IntentBuilder(mContext);
}
public IntentBuilder(Context mContext) {
this.mContext = mContext;
}
public void setmContext(Context mContext) {
this.mContext = mContext;
}
public IntentBuilder setmMessage(String mMessage) {
this.mMessage = mMessage;
return this;
}
public IntentBuilder setmCommandId(int mCommandId) {
this.mCommandId = mCommandId;
return this;
}
private static final String TAG = "IntentBuilder";
public Intent build() {
Log.e(TAG, "build: context cannot be null" + mContext);
Intent intent = new Intent(mContext, MyService.class);
if (mMessage != null)
intent.putExtra(KEY_MESSAGE, mMessage);
if (mCommandId != Command.INVALID)
intent.putExtra(KEY_COMMAND, mCommandId);
return intent;
}
public static boolean containsCommand(Intent intent) {
return intent.getExtras() != null && intent.getExtras().containsKey(KEY_COMMAND);
}
public static boolean containsMessage(Intent intent) {
return intent.getExtras() != null && intent.getExtras().containsKey(KEY_MESSAGE);
}
public static #Command
int getCommand(Intent intent) {
final #Command int commandId = intent.getExtras().getInt(KEY_COMMAND);
return commandId;
}
public static String getMessage(Intent intent) {
return intent.getExtras().getString(KEY_MESSAGE);
}
}
HandleNotification.java file
public class HandleNotification {
public static class O {
public static int getRandomNumber() {
return new Random().nextInt(100000);
}
static PendingIntent getLaunchActivityPI(Service context) {
Intent intent = new Intent(context, MainActivity.class);
return PendingIntent.getActivity(context, getRandomNumber(), intent, 0);
}
static PendingIntent getStopServicePI(Service context) {
PendingIntent pendingIntent;
{
Intent intent = new IntentBuilder(context).setmCommandId(Command.STOP).build();
pendingIntent = PendingIntent.getService(context, getRandomNumber(), intent, 0);
}
return pendingIntent;
}
public static final Integer ONGOING_NOTIFICATION_ID = getRandomNumber();
public static final String CHANNEL_ID = String.valueOf(getRandomNumber());
private static final String TAG = "O";
public static void createNotification(Service context) {
Log.d(TAG, "createNotification: ");
String channelId = createChannel(context);
Notification notification = buildNotification(channelId, context);
context.startForeground(ONGOING_NOTIFICATION_ID, notification);
}
static Notification buildNotification(String channelId, Service context) {
PendingIntent piMainActivity = getLaunchActivityPI(context);
PendingIntent piStopService = getStopServicePI(context);
Icon poweroff = Icon.createWithResource(context, android.R.drawable.star_big_on);
Notification.Action stopAction = new Notification.Action
.Builder(poweroff, "STOP", piStopService).build();
return new Notification.Builder(context, channelId)
.addAction(stopAction)
.setContentTitle("Tracking...")
.setContentText("I'm tracking your location now... ")
.setContentIntent(piMainActivity)
.build();
}
#NonNull
private static String createChannel(Context service) {
NotificationManager notificationManager = (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE);
CharSequence channelName = "Start Location Updates";
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID, channelName, importance);
notificationManager.createNotificationChannel(notificationChannel);
return CHANNEL_ID;
}
}
}
Use NotificationCompat.Builder. You can follow how to create one here
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle(textTitle)
.setContentText(textContent)
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
You can add your custom small or big icon, title, content, and other properties.
An example from a project that I did long ago
private void LockNotification() {
NotificationCompat.Builder builder = new
NotificationCompat.Builder(getApplicationContext());
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("key","launch_about");
PendingIntent pendingIntent =
PendingIntent.getActivity(getApplicationContext(), 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// Set the title, text, and icon
builder.setContentTitle(getString(R.string.app_name))
.setContentText("App Lock Enabled")
.setSmallIcon(R.drawable.ic_applock)
.setContentIntent(pendingIntent)
.setOngoing(true);
// Get an instance of the Notification Manager
NotificationManager notifyManager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
// Build the notification and post it
notifyManager.notify(0, builder.build());
}