Related
I have almost a week figuring out how should I have one instance of exoplayer in background.
I want to use this instance in a playerview and in notification as well. Problem I am facing now, the player plays well, after a time it pauses (to release resource I guess), but when i click play it plays again, when i click other oudio, I get two audio playing at same time.
Here are my codes.
------------------------ BACKGROUND SERVICE --------------------------------------
public class BackgroundPlayingService extends Service {
public static PlayerNotificationManager playerNotificationManager;
public static ExoPlayer exoPlayer;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (this.exoPlayer == null){
createPlayer();
}
/////////////////THIS WAS ADDED LATER
return START_STICKY;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
super.onDestroy();
exoPlayer.pause();
exoPlayer = null;
playerNotificationManager.setPlayer(null);
}
private void createPlayer(){
exoPlayer = new ExoPlayer.Builder(this).build();
MediaItem mediaItem;
try {
mediaItem = MediaItem.fromUri(Uri.parse(DataUtility.playingUrl));
}catch (Exception e){
mediaItem = MediaItem.fromUri(Uri.parse("https://server13.mp3quran.net/husr/062.mp3"));
}
exoPlayer.setMediaItem(mediaItem);
exoPlayer.prepare();
exoPlayer.addListener(new Player.Listener() {
#Override
public void onPlaybackStateChanged(int playbackState) {
Player.Listener.super.onPlaybackStateChanged(playbackState);
try {
if (ThePlayingActivity.dialog.isShowing()){
ThePlayingActivity.dialog.dismiss();}
}catch (Exception ignore){
}
if (playbackState==Player.STATE_READY){
try {
ThePlayingActivity.downloadBtn.setVisibility(View.VISIBLE);
showNotification();
}catch (Exception ignore){
}
}
if (playbackState == Player.STATE_BUFFERING) {
try {
ThePlayingActivity.myProgress.setVisibility(View.VISIBLE);
}catch (Exception e){
}
} else {
try {
ThePlayingActivity.myProgress.setVisibility(View.GONE);
}catch (Exception e){
}
}
}
#Override
public void onIsLoadingChanged(boolean isLoading) {
Player.Listener.super.onIsLoadingChanged(isLoading);
}
#Override
public void onPlayerError(PlaybackException error) {
Player.Listener.super.onPlayerError(error);
try {
if (ThePlayingActivity.dialog.isShowing()){
ThePlayingActivity.dialog.dismiss();
}
playerNotificationManager.setPlayer(null);
}catch (Exception ignore){
}
}
});
exoPlayer.play();
try {
ThePlayingActivity.myPlayerView.setPlayer(exoPlayer);
}catch (Exception e){
Log.d("background", "createPlayer: set player to view"+e.getLocalizedMessage());
}
}
public static void setNewPlayeData(){
MediaItem mediaItem = MediaItem.fromUri(Uri.parse(DataUtility.playingUrl));
exoPlayer.setMediaItem(mediaItem);
exoPlayer.prepare();
ThePlayingActivity.myPlayerView.setPlayer(exoPlayer);
try {
ThePlayingActivity.myPlayerView.setPlayer(exoPlayer);
}catch (Exception e){
Log.d("background", "createPlayer: set player to view"+e.getLocalizedMessage());
}
}
public void showNotification() {
playerNotificationManager = new PlayerNotificationManager.Builder(this, 151,
this.getResources().getString(R.string.app_name))
.setChannelNameResourceId(R.string.app_name)
.setChannelImportance(IMPORTANCE_HIGH)
.setMediaDescriptionAdapter(new PlayerNotificationManager.MediaDescriptionAdapter() {
#Override
public CharSequence getCurrentContentTitle(Player player) {
return player.getCurrentMediaItem().mediaMetadata.displayTitle;
}
#Nullable
#Override
public PendingIntent createCurrentContentIntent(Player player) {
return null;
}
#Nullable
#Override
public CharSequence getCurrentContentText(Player player) {
return Objects.requireNonNull(player.getCurrentMediaItem()).mediaMetadata.artist;
}
#Nullable
#Override
public Bitmap getCurrentLargeIcon(Player player, PlayerNotificationManager.BitmapCallback callback) {
return null;
}
}).setNotificationListener(new PlayerNotificationManager.NotificationListener() {
#Override
public void onNotificationCancelled(int notificationId, boolean dismissedByUser) {
}
#Override
public void onNotificationPosted(int notificationId, Notification notification, boolean ongoing) {
PlayerNotificationManager.NotificationListener.super.onNotificationPosted(notificationId, notification, ongoing);
}
})
.build();
playerNotificationManager.setUseStopAction(false);
try {
playerNotificationManager.setPlayer(exoPlayer);
}catch (Exception e){
}
}
}
-----------------------HERE IS THE ACTIVITY WITH PLAYER ----------------------------------
public class ThePlayingActivity extends AppCompatActivity {
private static final int PERMISION_STORAGE_CODE = 100;
private ProgressBar downloadingProgress;
public static ProgressBar myProgress;
public static Dialog dialog;
private ConstraintLayout layoutForSnack;
long downloadId;
private PowerManager powerManager;
private PowerManager.WakeLock wakeLock;
String myurl;
public static PlayerControlView myPlayerView;
private TextView currentPlaying, downloadingTv;
public static Button downloadBtn;
private String thePlayingSura;
private final BroadcastReceiver onDownloadComplete = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID,-1);
if (downloadId == id){
downloadingProgress.setVisibility(View.GONE);
downloadingTv.setVisibility(View.GONE);
downloadBtn.setText("DOWNLOADED");
downloadBtn.setBackgroundColor(Color.TRANSPARENT);
downloadBtn.setTextColor(Color.BLACK);
downloadBtn.setClickable(false);
Toast.makeText(context, "DOWNLOAD COMPLETED", Toast.LENGTH_SHORT).show();
}
}
};
#SuppressLint("SetTextI18n")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_the_playing);
myPlayerView = findViewById(R.id.playerView);
Intent dataIntent = getIntent();
myurl = dataIntent.getStringExtra("theUrl");
if (BackgroundPlayingService.exoPlayer == null){
startService();}else{
BackgroundPlayingService.setNewPlayeData();
myPlayerView.setPlayer(BackgroundPlayingService.exoPlayer);
}
keepActivityAlive();
registerReceiver(onDownloadComplete,new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
downloadingProgress = findViewById(R.id.downloadProgress);
downloadingTv = findViewById(R.id.downloadingWaitTv);
currentPlaying = findViewById(R.id.currentPlaying);
downloadBtn = findViewById(R.id.downloadbtn);
downloadBtn.setVisibility(View.GONE);
myProgress = findViewById(R.id.progressBar2);
myProgress.setVisibility(View.GONE);
layoutForSnack = findViewById(R.id.constraintLayout);
downloadingProgress.setVisibility(View.GONE);
downloadingTv.setVisibility(View.GONE);
thePlayingSura = dataIntent.getStringExtra("sendSuraName");
currentPlaying.setText(thePlayingSura+" - " + DataUtility.theKariName);
/////////////end of ids
showLoadingDialog();
/////////////////download
downloadBtn.setOnClickListener(view -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED){
//todo ask permission
String permissions[] = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
requestPermissions(permissions,PERMISION_STORAGE_CODE);
}else{
downloadStaffs();
}
}else {
//TODO DOWNLOAD
downloadStaffs();
}
});
/////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
}
#Override
public void onBackPressed() {
super.onBackPressed();
//todo fire service (may be)
}
private void showLoadingDialog() {
dialog = new Dialog(ThePlayingActivity.this);
dialog.setContentView(R.layout.dialog_loading_quran);
dialog.setCancelable(false);
dialog.show();
}
private void showSnackBar() {
Snackbar snackbar = Snackbar.make(layoutForSnack, "THERE WAS ON ERROR, CHECK YOUR INTERNET CONNECTION", Snackbar.LENGTH_INDEFINITE);
snackbar.setAction("RETRY", view -> {
});
snackbar.setActionTextColor(Color.YELLOW);
snackbar.show();
}
#Override
protected void onStop() {
super.onStop();
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case PERMISION_STORAGE_CODE:{
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
// we have permission
//todo Download
downloadStaffs();
}else{
Toast.makeText(ThePlayingActivity.this , "PERMISSION DENIED... CAN NOT DOWNLOAD", Toast.LENGTH_SHORT).show();
}
}
}
}
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(onDownloadComplete);
try {
if (wakeLock.isHeld()){
wakeLock.release();}
}catch (Exception ignore){
}
}
private void downloadStaffs(){
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(DataUtility.playingUrl));
request.setDescription("Download File");
request.setTitle(thePlayingSura);
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, thePlayingSura+" - "+ DataUtility.theKariName+".mp3");
final DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
final long downloadId = manager.enqueue(request);
this.downloadId = downloadId;
final ProgressBar mProgressBar = (ProgressBar) findViewById(R.id.downloadProgress);
downloadingTv.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.VISIBLE);
new Thread(new Runnable() {
#SuppressLint("Range")
#Override
public void run() {
boolean downloading = true;
while (downloading) {
DownloadManager.Query q = new DownloadManager.Query();
q.setFilterById(downloadId);
Cursor cursor = manager.query(q);
cursor.moveToFirst();
#SuppressLint("Range") int bytes_downloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
#SuppressLint("Range") int bytes_total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) {
downloading = false;
}
final double dl_progress = (int) ((bytes_downloaded * 100l) / bytes_total);
runOnUiThread(new Runnable() {
#Override
public void run() {
mProgressBar.setProgress((int) dl_progress);
}
});
Log.d("MESSAGES", statusMessage(cursor));
cursor.close();
}
}
}).start();
}
#SuppressLint("Range")
private String statusMessage(Cursor c) {
String msg = "???";
switch (c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS))) {
case DownloadManager.STATUS_FAILED:
msg = "Download failed!";
break;
case DownloadManager.STATUS_PAUSED:
msg = "Download paused!";
break;
case DownloadManager.STATUS_PENDING:
msg = "Download pending!";
break;
case DownloadManager.STATUS_RUNNING:
msg = "Download in progress!";
break;
case DownloadManager.STATUS_SUCCESSFUL:
msg = "Download complete!";
break;
default:
msg = "Download is nowhere in sight";
break;
}
return (msg);
}
}
private void keepActivityAlive(){
powerManager = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"QuranAudio:WakeLock");
}
private void startService(){
Intent i = new Intent(this, BackgroundPlayingService.class);
startService(i);
}
}
I figured it out.
I had to keep most of my business inside in onStartCommand
public class BackgroundPlayingService extends Service {
public static final String TAG = "bck_service";
public static ExoPlayer player;
public static PlayerNotificationManager playerNotificationManager;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String url = DataUtility.playingUrl;
if (url==null){
url = "https://server8.mp3quran.net/afs/Rewayat-AlDorai-A-n-Al-Kisa-ai/025.mp3";
}
if (player == null){
player = createPlayerIfNull();
MediaItem mediaItem = MediaItem.fromUri(Uri.parse(url));
player.setMediaItem(mediaItem);
player.prepare();
player.addListener(new Player.Listener() {
#Override
public void onEvents(Player player, Player.Events events) {
}
#Override
public void onPlaybackStateChanged(int playbackState) {
Player.Listener.super.onPlaybackStateChanged(playbackState);
try {
if (ThePlayingActivity.dialog.isShowing()){
ThePlayingActivity.dialog.dismiss();}
}catch (Exception ignore){
}
if (playbackState==Player.STATE_READY){
try {
ThePlayingActivity.downloadBtn.setVisibility(View.VISIBLE);
showNotification();
}catch (Exception ignore){
}
}
if (playbackState == Player.STATE_BUFFERING) {
try {
ThePlayingActivity.myProgress.setVisibility(View.VISIBLE);
}catch (Exception e){
}
} else {
try {
ThePlayingActivity.myProgress.setVisibility(View.GONE);
}catch (Exception e){
}
}
}
#Override
public void onIsLoadingChanged(boolean isLoading) {
Player.Listener.super.onIsLoadingChanged(isLoading);
}
#Override
public void onPlayerError(PlaybackException error) {
Player.Listener.super.onPlayerError(error);
try {
if (ThePlayingActivity.dialog.isShowing()){
ThePlayingActivity.dialog.dismiss();}
}catch (Exception ignore){
}
}
});
player.play();
try {
ThePlayingActivity.myPlayerView.setPlayer(player);
}catch (Exception ignore){}
}
//notification
createNotificationChannel();
Intent i = new Intent(this,BackgroundPlayingService.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,i,0);
Notification notification = new NotificationCompat.Builder(this,"myChannelId")
.setContentTitle("QURAN IS PLAYING")
.setContentText("This means that Qur-an app is open. You can close it from app exit menu")
.setSmallIcon(androidx.core.R.drawable.notification_icon_background)
.setContentIntent(pendingIntent)
.build();
startForeground(1,notification);
return START_STICKY;
}
public ExoPlayer createPlayerIfNull() {
if (player == null) {
player = new ExoPlayer.Builder(BackgroundPlayingService.this).build();
ThePlayingActivity.myProgress.setVisibility(View.VISIBLE);
try {
ThePlayingActivity.dialog.show();
}catch (Exception e){
Log.d(TAG, "createPlayerIfNull: ");
}
}
return player;
}
private void createNotificationChannel(){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
NotificationChannel channel = new NotificationChannel("myChannelId","qur-anAudiChannel", NotificationManager.IMPORTANCE_LOW);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
}
#Override
public void onDestroy() {
super.onDestroy();
player.stop();
player = null;
stopForeground(true);
}
public void showNotification() {
playerNotificationManager = new PlayerNotificationManager.Builder(this, 151,
this.getResources().getString(R.string.app_name))
.setChannelNameResourceId(R.string.app_name)
.setChannelImportance(IMPORTANCE_HIGH)
.setMediaDescriptionAdapter(new PlayerNotificationManager.MediaDescriptionAdapter() {
#Override
public CharSequence getCurrentContentTitle(Player player) {
return player.getCurrentMediaItem().mediaMetadata.displayTitle;
}
#Nullable
#Override
public PendingIntent createCurrentContentIntent(Player player) {
return null;
}
#Nullable
#Override
public CharSequence getCurrentContentText(Player player) {
return Objects.requireNonNull(player.getCurrentMediaItem()).mediaMetadata.artist;
}
#Nullable
#Override
public Bitmap getCurrentLargeIcon(Player player, PlayerNotificationManager.BitmapCallback callback) {
return null;
}
}).setNotificationListener(new PlayerNotificationManager.NotificationListener() {
#Override
public void onNotificationCancelled(int notificationId, boolean dismissedByUser) {
}
#Override
public void onNotificationPosted(int notificationId, Notification notification, boolean ongoing) {
PlayerNotificationManager.NotificationListener.super.onNotificationPosted(notificationId, notification, ongoing);
}
})
.build();
playerNotificationManager.setUseStopAction(false);
try {
playerNotificationManager.setPlayer(player);
}catch (Exception e){
}}
}
-------------------- player view activity ------------------------------------
public class ThePlayingActivity extends AppCompatActivity {
private static final int PERMISION_STORAGE_CODE = 100;
private ProgressBar downloadingProgress;
public static ProgressBar myProgress;
public static Dialog dialog;
private ConstraintLayout layoutForSnack;
long downloadId;
private PowerManager powerManager;
private PowerManager.WakeLock wakeLock;
String myurl;
public static PlayerControlView myPlayerView;
private TextView currentPlaying, downloadingTv;
public static Button downloadBtn;
private String thePlayingSura;
private BroadcastReceiver onDownloadComplete = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (downloadId == id) {
downloadingProgress.setVisibility(View.GONE);
downloadingTv.setVisibility(View.GONE);
downloadBtn.setText("DOWNLOADED");
downloadBtn.setBackgroundColor(Color.TRANSPARENT);
downloadBtn.setTextColor(Color.BLACK);
downloadBtn.setClickable(false);
Toast.makeText(context, "DOWNLOAD COMPLETED", Toast.LENGTH_SHORT).show();
}
}
};
#SuppressLint("StaticFieldLeak")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_the_playing);
myPlayerView = findViewById(R.id.playerView);
Intent dataIntent = getIntent();
myurl = dataIntent.getStringExtra("theUrl");
Intent backgroundPlayIntent = new Intent(ThePlayingActivity.this,BackgroundPlayingService.class);
try {
stopService(backgroundPlayIntent);
}catch (Exception e){}
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
startForegroundService(backgroundPlayIntent);
}else{
startService(backgroundPlayIntent);
}
keepActivityAlive();
registerReceiver(onDownloadComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
downloadingProgress = findViewById(R.id.downloadProgress);
downloadingTv = findViewById(R.id.downloadingWaitTv);
currentPlaying = findViewById(R.id.currentPlaying);
downloadBtn = findViewById(R.id.downloadbtn);
downloadBtn.setVisibility(View.GONE);
myProgress = findViewById(R.id.progressBar2);
myProgress.setVisibility(View.VISIBLE);
layoutForSnack = findViewById(R.id.constraintLayout);
downloadingProgress.setVisibility(View.GONE);
downloadingTv.setVisibility(View.GONE);
thePlayingSura = dataIntent.getStringExtra("sendSuraName");
currentPlaying.setText(thePlayingSura + " - " + DataUtility.theKariName);
/////////////end of ids
showLoadingDialog();
/////////////////download
downloadBtn.setOnClickListener(view -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
String permissions[] = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
requestPermissions(permissions, PERMISION_STORAGE_CODE);
} else {
downloadStaffs();
}
} else {
downloadStaffs();
}
});
}
private void showLoadingDialog() {
dialog = new Dialog(ThePlayingActivity.this);
dialog.setContentView(R.layout.dialog_loading_quran);
dialog.setCancelable(false);
dialog.show();
}
public void showSnackBar() {
Snackbar snackbar = Snackbar.make(layoutForSnack, "THERE WAS ON ERROR, CHECK YOUR INTERNET CONNECTION", Snackbar.LENGTH_INDEFINITE);
snackbar.setAction("RETRY", view -> {
//todo retry player
});
snackbar.setActionTextColor(Color.YELLOW);
snackbar.show();
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case PERMISION_STORAGE_CODE: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// we have permission
downloadStaffs();
} else {
Toast.makeText(ThePlayingActivity.this, "PERMISSION DENIED... CAN NOT DOWNLOAD", Toast.LENGTH_SHORT).show();
}
}
}
}
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(onDownloadComplete);
try {
if (wakeLock.isHeld()) {
wakeLock.release();
}
} catch (Exception ignore) {
}
}
private void downloadStaffs() {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(DataUtility.playingUrl));
request.setDescription("Download File");
request.setTitle(thePlayingSura);
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, thePlayingSura + " - " + DataUtility.theKariName + ".mp3");
final DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
final long downloadId = manager.enqueue(request);
this.downloadId = downloadId;
final ProgressBar mProgressBar = (ProgressBar) findViewById(R.id.downloadProgress);
downloadingTv.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.VISIBLE);
new Thread(new Runnable() {
#SuppressLint("Range")
#Override
public void run() {
boolean downloading = true;
while (downloading) {
DownloadManager.Query q = new DownloadManager.Query();
q.setFilterById(downloadId);
Cursor cursor = manager.query(q);
cursor.moveToFirst();
#SuppressLint("Range") int bytes_downloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
#SuppressLint("Range") int bytes_total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) {
downloading = false;
}
final double dl_progress = (int) ((bytes_downloaded * 100l) / bytes_total);
runOnUiThread(new Runnable() {
#Override
public void run() {
mProgressBar.setProgress((int) dl_progress);
}
});
Log.d("MESSAGES", statusMessage(cursor));
cursor.close();
}
}
}).start();
}
#SuppressLint("Range")
private String statusMessage(Cursor c) {
String msg = "???";
switch (c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS))) {
case DownloadManager.STATUS_FAILED:
msg = "Download failed!";
break;
case DownloadManager.STATUS_PAUSED:
msg = "Download paused!";
break;
case DownloadManager.STATUS_PENDING:
msg = "Download pending!";
break;
case DownloadManager.STATUS_RUNNING:
msg = "Download in progress!";
break;
case DownloadManager.STATUS_SUCCESSFUL:
msg = "Download complete!";
break;
default:
msg = "Download is nowhere in sight";
break;
}
return (msg);
}
private void keepActivityAlive() {
powerManager = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "QuranAudio:WakeLock");
}
private void startService() {
Intent i = new Intent(this, BackgroundPlayingService.class);
startService(i);
}}
--------------------- extra --------------------------------------
I also had to create other service to remove notification when the
user kill app by clearing app from recent apps.
here is it
MyAppService extends Service {
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
Intent i = new Intent(MyAppService.this,BackgroundPlayingService.class);
try {
BackgroundPlayingService.playerNotificationManager.setPlayer(null);
stopService(i);
}catch (Exception ignore){}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}}
-------------------- WARNING ----------------------------------
I don't take this as a best approach, but it gave me a quick solution while waiting for a better approach.
I will pot the app link here when it's available in playstore.
I'm stuck and can't find a way to restart or reconnect the Bluetooth service in my app. The app has 3 fragments, tabs managed by FragmentPagerAdapter.
In the first fragment you can discover, associate and communicate with the BT device. In the second and third card it is necessary to interact with the device, it is not possible to get the connection or keep the service connected.
Here it goes the first fragment:
public class Conexiones extends Fragment implements ServiceConnection, SerialListener {
public BluetoothAdapter BTAdapter;
private ListView listView; // detectados
private ArrayList<String> mDeviceList;
public Button conectar, actualizar;
private ArrayAdapter m_DiscoveredPeersAdapter;
BluetoothDevice bdDevice;
BluetoothClass dbClass;
ArrayList<BluetoothDevice> listaBTDevices = null;
View view;
public SerialSocket socket;
public SerialService service;
public Conexiones(){
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
view = inflater.inflate(R.layout.fragment_conexiones, container, false );
BTAdapter = BluetoothAdapter.getDefaultAdapter();
listView = (ListView) this.view.findViewById(R.id.listView);
mDeviceList = new ArrayList<>();
m_DiscoveredPeersAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, mDeviceList);
listView.setAdapter(m_DiscoveredPeersAdapter);
listItemClicked = new ListItemClicked();
clicked = new ButtonClicked();
checkBTPermissions();
listaBTDevices = new ArrayList<BluetoothDevice>();
m_DiscoveredPeersAdapter.notifyDataSetChanged();
if (BTAdapter == null || !BTAdapter.isEnabled()) {
Intent turnOn = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(turnOn, 1);
}
int MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION = 1;
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION);
if (BTAdapter.isEnabled()){
BTAdapter.startDiscovery();
Log.e("mio", "dp de star discovery");
}
else{
Intent turnOn = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(turnOn, 1);
Log.e("mio", "no weisa");
}
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
activity.registerReceiver(mReceiver, filter);
return view;
}
#Override
public void onStart() {
Log.e("mio", "dentro de on start ");
dataInicialRecibida=false;
super.onStart();
//getPairedDevices();
this.conectar.setOnClickListener(clicked);
this.actualizar.setOnClickListener(clicked);
listView.setOnItemClickListener(listItemClicked);
MainActivity activity = (MainActivity) getActivity();
texto.setText("sistema listo");
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.i("BT", "recibe");
if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
//discovery starts, we can show progress dialog or perform other tasks
Log.i("BT", "empezamos BT");
}
if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
//discovery starts, we can show progress dialog or perform other tasks
Log.i("BT", "terminiamos BT");
try {
Method getUuidsMethod = BluetoothAdapter.class.getDeclaredMethod("getUuids", null);
ParcelUuid[] uuids = (ParcelUuid[]) getUuidsMethod.invoke(BTAdapter, null);
if(uuids != null) {
for (ParcelUuid uuid : uuids) {
Log.d(TAG, "UUID: " + uuid.getUuid().toString());
miu=uuid.getUuid().toString();
}
}else{
Log.d(TAG, "Uuids not found, be sure to enable Bluetooth!");
}
}catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
//discovery starts, we can show progress dialog or perform other tasks
Log.i("BT", "state changed BT");
}
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// mDeviceList.add(device.getName() + "\n" + device.getAddress());
// DeviceItem newDevice = new DeviceItem(device.getName(), device.getAddress(), "false");
Log.i("BTN", device.getName() + "\n" + device.getAddress());
// mDeviceList.notifyDataSetChanged();
if(listaBTDevices.size()<1) // this checks if the size of bluetooth device is 0,then add the
{
Log.i("mio", "lista devices menor a 1");// device to the arraylist.
m_DiscoveredPeersAdapter.add(device.getName()+"\n"+device.getAddress());
listaBTDevices.add(device);
m_DiscoveredPeersAdapter.notifyDataSetChanged();
}
else
{
Log.i("mio", "lista devices maor o igual a 1");
boolean flag = true; // flag to indicate that particular device is already in the arlist or not
for(int i = 0; i<listaBTDevices.size();i++)
{
if(device.getAddress().equals(listaBTDevices.get(i).getAddress()))
{
flag = false;
}
}
if(flag == true)
{
m_DiscoveredPeersAdapter.add(device.getName()+"\n"+device.getAddress());
listaBTDevices.add(device);
m_DiscoveredPeersAdapter.notifyDataSetChanged();
}
}
}
listView.setAdapter(new ArrayAdapter<String>(context,
android.R.layout.simple_list_item_1, mDeviceList));
}
};
#RequiresApi(api = Build.VERSION_CODES.M)
private void checkBTPermissions() {
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP){
int permissionCheck =getActivity().checkSelfPermission("Manifest.permission.ACCESS_FINE_LOCATION");
permissionCheck += getActivity().checkSelfPermission("Manifest.permission.ACCESS_COARSE_LOCATION");
if (permissionCheck != 0) {
this.requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, 1001); //Any number
}
}else{
Log.d(TAG, "checkBTPermissions: No need to check permissions. SDK version < LOLLIPOP.");
}
}
#Override
public void onDestroy() {
MainActivity activity = (MainActivity) getActivity();
activity.unregisterReceiver(mReceiver);
super.onDestroy();
Log.e("mio", "on destroy ");
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK){
Log.d(TAG, "caso okookok.");
BTAdapter.startDiscovery();
}else{
Log.d(TAG, "cas no ok.");
}
}
#Override
public void onStop() {
super.onStop();
Log.e("mio", "on stop");
MainActivity activity = (MainActivity) getActivity();
activity.unregisterReceiver(mReceiver);
}
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
service = ((SerialService.SerialBinder) binder).getService();
Log.e("mio", "on service conectado conexiones ");
if(initialStart && isResumed()) {
initialStart = false;
getActivity().runOnUiThread(this::connect);
}
}
#Override
public void onServiceDisconnected(ComponentName name) {
service = null;
texto.setText("desconectado BT");
Log.e("mio", "desconectado BT conexiones");
}
#Override
public void onSerialConnect() {
texto.setText("conexion BT");
Log.e("mio", "conectado BT conexiones");
}
#Override
public void onSerialConnectError(Exception e) {
texto.setText("error conexion BT");
Log.e("mio", "onSerialConnectError");
}
#Override
public void onSerialRead(byte[] data) {
receive(data);
texto.setText("leyendo BT");
Log.e("mio", "leyendo BT ");
}
private void receive(byte[] data) {
receiveText+=new String(data);
receiveText="";
}
#Override
public void onSerialIoError(Exception e) {
texto.setText("error conexion BT");
}
class ButtonClicked implements View.OnClickListener
{
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.button:
listaBTDevices.clear();
m_DiscoveredPeersAdapter.clear();
startSearching();
texto.setText("buscando dispositivos BT");
break;
case R.id.button3:
break;
}
}
}
class ListItemClicked implements AdapterView.OnItemClickListener
{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
bdDevice = listaBTDevices.get(position);
//bdClass = arrayListBluetoothDevices.get(position);
Log.i("Log", "The dvice : "+bdDevice.toString());
connect();
}
}
private boolean isNetworkAvailable() {
ConnectivityManager connectivityManager = (ConnectivityManager) service.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
private void getPairedDevices() {
// Set<BluetoothDevice> pairedDevice = BTAdapter.getBondedDevices();
// if(pairedDevice.size()>0)
{
// for(BluetoothDevice device : pairedDevice)
{
// arrayListpaired.add(device.getName()+"\n"+device.getAddress());
// arrayListPairedBluetoothDevices.add(device);
}
}
// adapter.notifyDataSetChanged();
}
public void send(String str) {
try {
SpannableStringBuilder spn = new SpannableStringBuilder(str+'\n');
byte[] data = (str).getBytes();
socket.write(data);
} catch (Exception e) {
onSerialIoError(e);
}
}
#SuppressWarnings("deprecation")
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// getActivity().bindService(new Intent(getActivity(), SerialService.class), this, Context.BIND_AUTO_CREATE);
}
private void connect() {
try {
BTAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice device = BTAdapter .getRemoteDevice(bdDevice.getAddress());
String deviceName = device.getName() != null ? device.getName() : device.getAddress();
socket = new SerialSocket();
service.connect(this, "Connected to " + deviceName);
socket.connect(getContext(), service, device);
MainActivity activity = (MainActivity) getActivity();
activity.setidEquipo(bdDevice.getAddress());
// conexión ok, cambiar color letras boton
} catch (Exception e) {
//onSerialConnectError(e);
Log.i("mio", "error del connect "+e);
// sino hay conexión mantener boton con letras naranjas
}
}
private void startSearching() {
Log.i("Log", "in the start searching method");
MainActivity activity = (MainActivity) getActivity();
IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
activity.registerReceiver(mReceiver, intentFilter);
BTAdapter.startDiscovery();
}
private void onBluetooth() {
if(!BTAdapter.isEnabled())
{
BTAdapter.enable();
Log.i("Log", "Bluetooth is Enabled");
}
}
private void offBluetooth() {
if(BTAdapter.isEnabled())
{
BTAdapter.disable();
}
}
}
The serial service:
public class SerialService extends Service implements SerialListener {
class SerialBinder extends Binder {
SerialService getService() { return SerialService.this; }
}
private enum QueueType {Connect, ConnectError, Read, IoError}
private class QueueItem {
QueueType type;
byte[] data;
Exception e;
QueueItem(QueueType type, byte[] data, Exception e) { this.type=type; this.data=data; this.e=e; }
}
private final Handler mainLooper;
private final IBinder binder;
private final Queue<QueueItem> queue1, queue2;
private SerialListener listener;
private boolean connected;
private String notificationMsg;
public SerialService() {
mainLooper = new Handler(Looper.getMainLooper());
binder = new SerialBinder();
queue1 = new LinkedList<>();
queue2 = new LinkedList<>();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public void connect(SerialListener listener, String notificationMsg) {
this.listener = listener;
connected = true;
this.notificationMsg = notificationMsg;
}
public void disconnect() {
listener = null;
connected = false;
notificationMsg = null;
}
public void attach(SerialListener listener) {
if(Looper.getMainLooper().getThread() != Thread.currentThread())
throw new IllegalArgumentException("not in main thread");
cancelNotification();
// use synchronized() to prevent new items in queue2
// new items will not be added to queue1 because mainLooper.post and attach() run in main thread
if(connected) {
synchronized (this) {
this.listener = listener;
}
}
for(QueueItem item : queue1) {
switch(item.type) {
case Connect: listener.onSerialConnect (); break;
case ConnectError: listener.onSerialConnectError (item.e); break;
case Read: listener.onSerialRead (item.data); break;
case IoError: listener.onSerialIoError (item.e); break;
}
}
for(QueueItem item : queue2) {
switch(item.type) {
case Connect: listener.onSerialConnect (); break;
case ConnectError: listener.onSerialConnectError (item.e); break;
case Read: listener.onSerialRead (item.data); break;
case IoError: listener.onSerialIoError (item.e); break;
}
}
queue1.clear();
queue2.clear();
}
public void detach() {
if(connected)
createNotification();
// items already in event queue (posted before detach() to mainLooper) will end up in queue1
// items occurring later, will be moved directly to queue2
// detach() and mainLooper.post run in the main thread, so all items are caught
listener = null;
}
public void onSerialConnect() {
Log.i("mio", "dentro on serial connect ");
if(connected) {
Log.i("mio", "dentro de connected ");
synchronized (this) {
if (listener != null) {
mainLooper.post(() -> {
if (listener != null) {
listener.onSerialConnect();
} else {
queue1.add(new QueueItem(QueueType.Connect, null, null));
}
});
} else {
queue2.add(new QueueItem(QueueType.Connect, null, null));
}
}
}
}
public void onSerialConnectError(Exception e) {
if(connected) {
synchronized (this) {
if (listener != null) {
mainLooper.post(() -> {
if (listener != null) {
listener.onSerialConnectError(e);
} else {
queue1.add(new QueueItem(QueueType.ConnectError, null, e));
cancelNotification();
disconnect();
}
});
} else {
queue2.add(new QueueItem(QueueType.ConnectError, null, e));
cancelNotification();
disconnect();
}
}
}
}
public void onSerialRead(byte[] data) {
if(connected) {
synchronized (this) {
if (listener != null) {
mainLooper.post(() -> {
if (listener != null) {
listener.onSerialRead(data);
} else {
queue1.add(new QueueItem(QueueType.Read, data, null));
}
});
} else {
queue2.add(new QueueItem(QueueType.Read, data, null));
}
}
}
}
public void onSerialIoError(Exception e) {
if(connected) {
synchronized (this) {
if (listener != null) {
mainLooper.post(() -> {
if (listener != null) {
listener.onSerialIoError(e);
} else {
queue1.add(new QueueItem(QueueType.IoError, null, e));
cancelNotification();
disconnect();
}
});
} else {
queue2.add(new QueueItem(QueueType.IoError, null, e));
cancelNotification();
disconnect();
}
}
}
}
}
The serial socket class:
class SerialSocket implements Runnable {
private static final UUID BLUETOOTH_SPP = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private final BroadcastReceiver disconnectBroadcastReceiver;
private Context context;
private SerialListener listener;
private BluetoothDevice device;
private BluetoothSocket socket;
private boolean connected;
SerialSocket() {
disconnectBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(listener != null)
listener.onSerialIoError(new IOException("background disconnect"));
disconnect(); // disconnect now, else would be queued until UI re-attached
}
};
}
/**
* connect-success and most connect-errors are returned asynchronously to listener
*/
void connect(Context context, SerialListener listener, BluetoothDevice device) throws IOException {
if(connected || socket != null)
throw new IOException("already connected");
this.context = context;
this.listener = listener;
this.device = device;
context.registerReceiver(disconnectBroadcastReceiver, new IntentFilter(Constants.INTENT_ACTION_DISCONNECT));
Executors.newSingleThreadExecutor().submit(this);
}
void disconnect() {
listener = null; // ignore remaining data and errors
// connected = false; // run loop will reset connected
if(socket != null) {
try {
socket.close();
} catch (Exception ignored) {
}
socket = null;
}
try {
context.unregisterReceiver(disconnectBroadcastReceiver);
} catch (Exception ignored) {
}
}
void write(byte[] data) throws IOException {
if (!connected)
throw new IOException("not connected");
socket.getOutputStream().write(data);
}
#Override
public void run() { // connect & read
try {
socket = device.createRfcommSocketToServiceRecord(BLUETOOTH_SPP);
socket.connect();
if(listener != null)
listener.onSerialConnect();
} catch (Exception e) {
if(listener != null)
listener.onSerialConnectError(e);
try {
socket.close();
} catch (Exception ignored) {
}
socket = null;
return;
}
connected = true;
try {
byte[] buffer = new byte[1024];
int len;
//noinspection InfiniteLoopStatement
while (true) {
len = socket.getInputStream().read(buffer);
byte[] data = Arrays.copyOf(buffer, len);
if(listener != null)
listener.onSerialRead(data);
}
} catch (Exception e) {
connected = false;
if (listener != null)
listener.onSerialIoError(e);
try {
socket.close();
} catch (Exception ignored) {
}
socket = null;
}
}
}
And the second fragment whereas I need to keep connection or connect to the same device already connected in fragment ("conexiones"):
public class Identificacion extends Fragment implements ServiceConnection, SerialListener {
private String deviceAddress;
private String newline = "\r\n";
private String receiveText;
private enum Connected { False, Pending, True }
private String sendText;
private SerialSocket socket;
private SerialService service;
private boolean initialStart = true;
private Connected connected = Connected.False;
// constructor requerido vacio
public Identificacion(){
}
#Override
public void onCreate(Bundle savedInstanceState) {
Log.e("mio", "dentro on creata identificacion");
super.onCreate(savedInstanceState);
setRetainInstance(true);
// deviceAddress = getArguments().getString("device");
}
#SuppressWarnings("deprecation") // onAttach(context) was added with API 23. onAttach(activity) works for all API versions
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
getActivity().bindService(new Intent(getActivity(), SerialService.class), this, Context.BIND_AUTO_CREATE);
}
#Override
public void onDetach() {
try { getActivity().unbindService(this); } catch(Exception ignored) {}
super.onDetach();
}
#Override
public void onResume() {
super.onResume();
/* if(initialStart && service !=null) {
initialStart = false;
getActivity().runOnUiThread(this::connect);
} */
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
view = inflater.inflate(R.layout.fragment_identificacion, container, false );
return view;
}
private void addListenerOnButton() {
bt12.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
EditText txtView = (EditText) aux1;
EditText txtView2 = (EditText) aux2;
TextView txtView3 = (TextView) aux3;
if (pin2 != null && pin2.length() == largo && pin4 != null && pin4.length() == largo){
//consultar BD si exta bien la pass, caso si:
Log.e("mio", "dentro de pin y pass con formato " + pin2);
pin=pin2;
String aenvio=(String)pin2+";"+pin4;
// enviar pin por BT
connect();
send(aenvio);
}
}
});
}// fin add listener button
private void connect() {
try {
MainActivity activity = (MainActivity) getActivity();
String par3= activity.getidEquipo();
deviceAddress=par3;
Log.e("mio", "connect identif valor deviceAddress "+deviceAddress);
//deviceAddress = getArguments().getString("device");
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
String deviceName = device.getName() != null ? device.getName() : device.getAddress();
// status("connecting...");
Log.e("mio", "connect identif deviceName "+deviceName);
connected = Connected.Pending;
onAttach(activity);
// socket =
//socket = new SerialSocket();
getActivity().bindService(new Intent(getActivity(), SerialService.class ), this, Context.BIND_ADJUST_WITH_ACTIVITY );
service.connect(this, "Connected to " + deviceName);
socket.connect(getContext(), service, device);
connected = Connected.True;
Log.e("mio", "connect identif salida "+deviceName);
} catch (Exception e) {
onSerialConnectError(e);
}
}
private void send(String str) {
if(connected != Connected.True) {
Toast.makeText(getActivity(), "not connected", Toast.LENGTH_SHORT).show();
Log.e("mio", "dentro send no conectado "+str);
// return;
}
try {
connect();
// SpannableStringBuilder spn = new SpannableStringBuilder(str+'\n');
Log.e("mio", "send conectado "+str);
// spn.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorSendText)), 0, spn.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Toast.makeText(getActivity(), "enviando login a esp", Toast.LENGTH_SHORT).show();
sendText=str; // poner aca el string de user;pass a enviar al esp32 app
byte[] data = (str + newline).getBytes();
socket.write(data);
} catch (Exception e) {
onSerialIoError(e);
}
}
private void receive(byte[] data) {
receiveText+=(new String(data));
}
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
service = ((SerialService.SerialBinder) binder).getService();
if(initialStart && isResumed()) {
initialStart = false;
getActivity().runOnUiThread(this::connect);
}
}
#Override
public void onServiceDisconnected(ComponentName name) {
service = null;
}
#Override
public void onSerialRead(byte[] data) {
Log.e("mio", "serial leer data ");
receive(data);
}
#Override
public void onSerialIoError(Exception e) {
Log.e("mio", "serial io error, desconectar ");
disconnect();
}
}
Managing code when you are using Bluetooth or system-related task is too hard because it creates a tightly coupled code eventually turn into the buggy code. So you can use this and this libraries to do your task neat and cleanly. Please check the example app for my suggested first and second libraries. If you face any struck in implementation let me know.
Run service from an activity or from the App class.
upd:
Or use singleton as a main point to communicate with the BluetoothHelper
I solved this of my way, by keeping the bluetooth socket static.
I've setted the 'boolean' object {TRUE, PENDING, FALSE}, in service and handled this object with service listener in others activities.
in the activities i unbind the service but do not disconnect socket, but as i don't disconnect from socket when change activities, there's a memory leak, but for me isn't a problem.
result:
the socket keeps connected and i can access the device from all activities, and the error handling (discconect, bluetooth off) happens thanks to listener
Create a service running in a different process not the main app process. Communicate with main app and service using broadcast receivers. This makes sure you can be anywhere in main app but you can still interact with Ble service
solved moving all the bluetooth code: broadcast receiver, service instance and socket instance, to the main activity, then in the fragments the service is attached (bind service) or (if null) started again.
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
Log.e("mio", "dentro de is visible to user identif ");
// connect();
if(service != null){
service.attach(this);
Log.e("mio", "caso attach ");
}
else{
Log.e("mio", "caso start service 1 ");
getActivity().startService(new Intent(getActivity(), SerialService.class)); // prevents service destroy on unbind from recreated activity caused by orientation change
// service.attach(this);
}
This solves the problem nice and easy, so the service is available when user see data in the fragment, and communication is well received and sent.
So basically, i got a list view that shows a list of bands.
When i press on one of the bands in the list, it creates a band profile page via an adapter.
In this layout, i got a play button, which starts a MediaPlayer service and play a song.
So far, so good.
The problem is, im trying to update the seekBar, located in the band profile page (created by the adapter mentioned above), from the MediaPlayer service Java file, but i'm unable to reach it.
i'm trying to use :
LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.activity_singleband, null, false);
seekBar = (SeekBar) layout.findViewById(R.id.seekBarBand);
but it seems that it uses another "instance" of the view, and not the one i'm currently viewing.
Here is the full MusicService Java file:
public class MusicService extends Service {
SeekBar seekBar;
Handler handler;
Runnable runnable;
NotificationManager nMN;
TextView BandName;
String songLink;
String songName;
String bandName;
String SongPlaying="";
String BandPlaying="";
public static MediaPlayer mp;
public static Boolean mpState = true;
public static Boolean mpPause = false;
public static Boolean isPlaying = false;
#Override
public void onCreate() {
super.onCreate();
}
// Play Cycle for seekBar
public void PlayCycle(){
seekBar.setProgress(mp.getCurrentPosition());
if (mp.isPlaying()){
runnable = new Runnable() {
#Override
public void run() {
PlayCycle();
}
};
handler.postDelayed(runnable, 1000);
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
songLink = intent.getStringExtra("SongLink");
songName = intent.getStringExtra("SongName");
bandName = intent.getStringExtra("BandName");
LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.activity_singleband, null, false);
seekBar = (SeekBar) layout.findViewById(R.id.seekBarBand);
BandName = (TextView) layout.findViewById(R.id.singleBandNameView);
BandName.setText("Bla Bla");
System.out.print("Band Name : "+ BandName.getText() );
handler = new Handler();
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean input) {
if (input){
mp.seekTo(progress);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
if (isPlaying && !songName.equals(SongPlaying) && !BandPlaying.equals(bandName)) {
Stop();
mp = new MediaPlayer();
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
Uri songUri = Uri.parse(songLink);
mp.setDataSource(getApplicationContext(), songUri);
SongPlaying = songName;
Toast.makeText(getApplicationContext(), "Please wait", Toast.LENGTH_SHORT).show();
isPlaying=true;
new prepare().execute();
}
catch (IOException e) {
e.printStackTrace();
}
}
else if (!isPlaying && !songName.equals(SongPlaying) && !BandPlaying.equals(bandName) ) {
mp = new MediaPlayer();
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
Uri songUri = Uri.parse(songLink);
mp.setDataSource(getApplicationContext(), songUri);
SongPlaying = songName;
Toast.makeText(getApplicationContext(), "Please wait", Toast.LENGTH_SHORT).show();
new prepare().execute();
isPlaying=true;
}
catch (IOException e) {
e.printStackTrace();
}
}
else {
Toast.makeText(getApplicationContext(), "Song is Already Playing", Toast.LENGTH_SHORT).show();
}
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
mp.release();
handler.removeCallbacks(runnable);
}
/// Permanent Notification
private void showNotification() {
nMN = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification n = new Notification.Builder(this)
.setContentTitle(songName)
.setContentText(bandName)
.setSmallIcon(R.drawable.ic_bandcamp)
//.addAction(R.drawable.ic_play_arrow_black_24dp, "button1", new pause())
.setOngoing(true)
.build();
nMN.notify(1, n);
}
/// PREPARE SONG TO PLAY //
public class prepare extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.prepare();
PlayCycle();
seekBar.setMax(mp.getDuration());
mp.start();
SongPlaying = songName;
isPlaying=true;
showNotification(); // show notification
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
Toast.makeText(getApplicationContext(),"Song Finished", Toast.LENGTH_LONG).show();
new stop().execute();
}
});
} catch (IOException io){ io.printStackTrace(); }
return null; }}
/// STOP PLAYING SONG //
public static void Stop()
{
mp.reset();
mp.release();
isPlaying=false;
}
public static class stop extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.reset();
mp.release();
isPlaying=false;
} catch (Exception io){ io.printStackTrace(); }
return null; }}
/// PAUSE PLAYING SONG//
public static class pause extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.pause(); }
catch (Exception io){ io.printStackTrace(); }
return null; }}
public static class start extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.start(); }
catch (Exception io){ io.printStackTrace(); }
return null; }}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
Would love to have some suggestions.
Thanks in advance.
Okey Firstly I suggest you to read more about Android Service classes. Secondly Do NOT place you visual components like a SeekBar or TextView in the Service because it doesn't make any sense. Service is just a background task and do not know about the UI. Since it's executed in the background you don't even need AsyncTask inside this so thirdly remove ALL AsyncTasks in your service and run their task like they don't need to be executed in the background.
So all what you have to do is create a Fragment/Activity for the UI, then bind your Fragment/Acitivity with your Service to communicate and update UI components.
The easiest way is make the Service a Singleton like it's done here:
public class MusicService extends Service {
private static MusicService instance = null;
public static MusicService getInstance() {
return instance;
}
Handler handler;
Runnable runnable;
NotificationManager nMN;
String songLink;
String songName;
String bandName;
String SongPlaying="";
String BandPlaying="";
public static MediaPlayer mp;
public static Boolean mpState = true;
public static Boolean mpPause = false;
public static Boolean isPlaying = false;
#Override
public void onCreate() {
instance = this;
super.onCreate();
}
// Play Cycle for seekBar
public void PlayCycle(){
seekBar.setProgress(mp.getCurrentPosition());
if (mp.isPlaying()){
runnable = new Runnable() {
#Override
public void run() {
PlayCycle();
}
};
handler.postDelayed(runnable, 1000);
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
songLink = intent.getStringExtra("SongLink");
songName = intent.getStringExtra("SongName");
bandName = intent.getStringExtra("BandName");
handler = new Handler();
if (isPlaying && !songName.equals(SongPlaying) && !BandPlaying.equals(bandName)) {
Stop();
mp = new MediaPlayer();
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
Uri songUri = Uri.parse(songLink);
mp.setDataSource(getApplicationContext(), songUri);
SongPlaying = songName;
Toast.makeText(getApplicationContext(), "Please wait", Toast.LENGTH_SHORT).show();
isPlaying=true;
new prepare().execute();
}
catch (IOException e) {
e.printStackTrace();
}
}
else if (!isPlaying && !songName.equals(SongPlaying) && !BandPlaying.equals(bandName) ) {
mp = new MediaPlayer();
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
Uri songUri = Uri.parse(songLink);
mp.setDataSource(getApplicationContext(), songUri);
SongPlaying = songName;
Toast.makeText(getApplicationContext(), "Please wait", Toast.LENGTH_SHORT).show();
new prepare().execute();
isPlaying=true;
}
catch (IOException e) {
e.printStackTrace();
}
}
else {
Toast.makeText(getApplicationContext(), "Song is Already Playing", Toast.LENGTH_SHORT).show();
}
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
mp.release();
handler.removeCallbacks(runnable);
}
/// Permanent Notification
private void showNotification() {
nMN = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification n = new Notification.Builder(this)
.setContentTitle(songName)
.setContentText(bandName)
.setSmallIcon(R.drawable.ic_bandcamp)
//.addAction(R.drawable.ic_play_arrow_black_24dp, "button1", new pause())
.setOngoing(true)
.build();
nMN.notify(1, n);
}
/// PREPARE SONG TO PLAY //
public class prepare extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.prepare();
PlayCycle();
seekBar.setMax(mp.getDuration());
mp.start();
SongPlaying = songName;
isPlaying=true;
showNotification(); // show notification
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
Toast.makeText(getApplicationContext(),"Song Finished", Toast.LENGTH_LONG).show();
new stop().execute();
}
});
} catch (IOException io){ io.printStackTrace(); }
return null; }}
/// STOP PLAYING SONG //
public static void Stop()
{
mp.reset();
mp.release();
isPlaying=false;
}
public static class stop extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.reset();
mp.release();
isPlaying=false;
} catch (Exception io){ io.printStackTrace(); }
return null; }}
/// PAUSE PLAYING SONG//
public static class pause extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.pause(); }
catch (Exception io){ io.printStackTrace(); }
return null; }}
public static class start extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.start(); }
catch (Exception io){ io.printStackTrace(); }
return null; }}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
// throw new UnsupportedOperationException("Not yet implemented");
return null;
}
The title it's not clear i think. In my project i want a service that runs in background and when the user says "hello phone" or some word/phrase my app starts to recognize the voice. Actually it "works" but not in right way... I have a service and this service detect the voice.
public class SpeechActivationService extends Service
{
protected AudioManager mAudioManager;
protected SpeechRecognizer mSpeechRecognizer;
protected Intent mSpeechRecognizerIntent;
protected final Messenger mServerMessenger = new Messenger(new IncomingHandler(this));
protected boolean mIsListening;
protected volatile boolean mIsCountDownOn;
static String TAG = "Icaro";
static final int MSG_RECOGNIZER_START_LISTENING = 1;
static final int MSG_RECOGNIZER_CANCEL = 2;
private int mBindFlag;
private Messenger mServiceMessenger;
#Override
public void onCreate()
{
super.onCreate();
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(this);
mSpeechRecognizer.setRecognitionListener(new SpeechRecognitionListener());
mSpeechRecognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
mSpeechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,
this.getPackageName());
//mSpeechRecognizer.startListening(mSpeechRecognizerIntent);
}
protected static class IncomingHandler extends Handler
{
private WeakReference<SpeechActivationService> mtarget;
IncomingHandler(SpeechActivationService target)
{
mtarget = new WeakReference<SpeechActivationService>(target);
}
#Override
public void handleMessage(Message msg)
{
final SpeechActivationService target = mtarget.get();
switch (msg.what)
{
case MSG_RECOGNIZER_START_LISTENING:
if (Build.VERSION.SDK_INT >= 16);//Build.VERSION_CODES.JELLY_BEAN)
{
// turn off beep sound
target.mAudioManager.setStreamMute(AudioManager.STREAM_SYSTEM, true);
}
if (!target.mIsListening)
{
target.mSpeechRecognizer.startListening(target.mSpeechRecognizerIntent);
target.mIsListening = true;
Log.d(TAG, "message start listening"); //$NON-NLS-1$
}
break;
case MSG_RECOGNIZER_CANCEL:
target.mSpeechRecognizer.cancel();
target.mIsListening = false;
Log.d(TAG, "message canceled recognizer"); //$NON-NLS-1$
break;
}
}
}
// Count down timer for Jelly Bean work around
protected CountDownTimer mNoSpeechCountDown = new CountDownTimer(5000, 5000)
{
#Override
public void onTick(long millisUntilFinished)
{
// TODO Auto-generated method stub
}
#Override
public void onFinish()
{
mIsCountDownOn = false;
Message message = Message.obtain(null, MSG_RECOGNIZER_CANCEL);
try
{
mServerMessenger.send(message);
message = Message.obtain(null, MSG_RECOGNIZER_START_LISTENING);
mServerMessenger.send(message);
}
catch (RemoteException e)
{
}
}
};
#Override
public int onStartCommand (Intent intent, int flags, int startId)
{
//mSpeechRecognizer.startListening(mSpeechRecognizerIntent);
try
{
Message msg = new Message();
msg.what = MSG_RECOGNIZER_START_LISTENING;
mServerMessenger.send(msg);
}
catch (RemoteException e)
{
}
return START_NOT_STICKY;
}
#Override
public void onDestroy()
{
super.onDestroy();
if (mIsCountDownOn)
{
mNoSpeechCountDown.cancel();
}
if (mSpeechRecognizer != null)
{
mSpeechRecognizer.destroy();
}
}
protected class SpeechRecognitionListener implements RecognitionListener
{
#Override
public void onBeginningOfSpeech()
{
// speech input will be processed, so there is no need for count down anymore
if (mIsCountDownOn)
{
mIsCountDownOn = false;
mNoSpeechCountDown.cancel();
}
Log.d(TAG, "onBeginingOfSpeech"); //$NON-NLS-1$
}
#Override
public void onBufferReceived(byte[] buffer)
{
String sTest = "";
}
#Override
public void onEndOfSpeech()
{
Log.d("TESTING: SPEECH SERVICE", "onEndOfSpeech"); //$NON-NLS-1$
}
#Override
public void onError(int error)
{
if (mIsCountDownOn)
{
mIsCountDownOn = false;
mNoSpeechCountDown.cancel();
}
Message message = Message.obtain(null, MSG_RECOGNIZER_START_LISTENING);
try
{
mIsListening = false;
mServerMessenger.send(message);
}
catch (RemoteException e)
{
}
Log.d(TAG, "error = " + error); //$NON-NLS-1$
}
#Override
public void onEvent(int eventType, Bundle params)
{
}
#Override
public void onPartialResults(Bundle partialResults)
{
}
#Override
public void onReadyForSpeech(Bundle params)
{
if (Build.VERSION.SDK_INT >= 16);//Build.VERSION_CODES.JELLY_BEAN)
{
mIsCountDownOn = true;
mNoSpeechCountDown.start();
mAudioManager.setStreamMute(AudioManager.STREAM_SYSTEM, false);
}
Log.d("TESTING: SPEECH SERVICE", "onReadyForSpeech"); //$NON-NLS-1$
}
#Override
public void onResults(Bundle results)
{
ArrayList<String> data = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
Log.d(TAG, (String) data.get(0));
//mSpeechRecognizer.startListening(mSpeechRecognizerIntent);
mIsListening = false;
Message message = Message.obtain(null, MSG_RECOGNIZER_START_LISTENING);
try
{
mServerMessenger.send(message);
}
catch (RemoteException e)
{
}
Log.d(TAG, "onResults"); //$NON-NLS-1$
}
#Override
public void onRmsChanged(float rmsdB)
{
}
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
}
And i start service in my MainActivity just to try:
Intent i = new Intent(context, SpeechActivationService.class);
startService(i);
It detect the voice input...and TOO MUCH!!! Every time it detects something it's a "bipbip". Too many bips!! It's frustrating.. I only want that it starts when i say "hello phone" or "start" or a specific word!! I try to look at this https://github.com/gast-lib/gast-lib/blob/master/library/src/root/gast/speech/activation/WordActivator.java but really i don't know how use this library. I try see this question onCreate of android service not called but i not understand exactly what i have to do.. Anyway, i already import the gast library.. I only need to know how use it. Anyone can help me step by step? Thanks
Use setStreamSolo(AudioManager.STREAM_VOICE_CALL, true) instead of setStreamMute. Remember to add setStreamSolo(AudioManager.STREAM_VOICE_CALL, false) in case MSG_RECOGNIZER_CANCEL
My mqtt server does not disconnect while the user kills the app. Still other user can see status is online. Once I close the app I need to disconnect the mqtt server.
1) My first time connection is successful
2) After I close the app the mqtt does not disconnect
3) If anybody sent message it automatically subscribe again
private static BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (LBUtil.isNetworkAvailable(context)) {
if (BuildConfig.DEBUG) {
LBLog.v(TAG, "network is available now");
}
}
}
};
#Override
public IBinder onBind(Intent arg0) {
return null;
}
Here start the mqtt service.
#Override
public void onCreate() {
super.onCreate();
LBLog.v(TAG, "start mqtt service");
mApp = (LBApplication) this.getApplicationContext();
mHandler = LBMessageManager.messageHandler;
mServerUri = LBUtil.getMqttBrokerUrl(this);
// register re-subscribe listener
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(LBMessageManager.ACTION_MQTT_SUBSCRIBE);
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
LBLog.v(TAG, "re-subscribe mqtt");
isConnected = false;
if(!mMqttClient.isConnected()){
subscribeMqttTopic();
}
}
};
registerReceiver(receiver, intentFilter);
subscribeMqttTopic();
}
private void subscribeMqttTopic() {
subscriberThread = new Thread(new Runnable() {
public void run() {
do {
try {
mClientId = mApp.getDeviceToken() == null? mApp.getAuthToken():mApp.getDeviceToken();
mTopic = mApp.getSecretKey() + mApp.getUserId();
mWillTopic = mApp.getSecretKey() + mApp.getPartnerId();
if(mServerUri == null){
LBLog.e(TAG, "mServerUri is null");
}
if(mClientId == null){
LBLog.e(TAG, "mClientId is null");
}
if(mMqttClient == null){
mMqttClient = new MqttClient(mServerUri, mClientId, null);
}
Messenger messenger = new Messenger(mHandler);
LBMessageCallback callback = new LBMessageCallback(mApp, messenger);
mMqttClient.setCallback(callback);
final MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(false);
options.setUserName(LBUtil.getMqttServerUsername(mApp));
options.setPassword(LBUtil.getMqttServerPassword(mApp).toCharArray());
// set will message
LBMqttMessage mqttMessage = new LBMqttMessage();
mqttMessage.setFrom(mApp.getUserId());
mqttMessage.setTo(mApp.getPartnerId());
mqttMessage.setType(LBMessageManager.TYPE_STATUS);
mqttMessage.setState(LBMessageManager.STATE_INACTIVE);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());
String jsonString = null;
try {
FilterProvider filters = new SimpleFilterProvider().addFilter("lbMqttMessageFilter", SimpleBeanPropertyFilter.filterOutAllExcept("state", "to", "from"));
jsonString = mapper.writer(filters).writeValueAsString(mqttMessage);
LBLog.v(TAG, "will message=" + jsonString);
} catch (JsonProcessingException e) {
LBLog.e(TAG, "enableMqtt", e);
}
options.setWill(mMqttClient.getTopic(mWillTopic), jsonString.getBytes(), LBMessageMqtt.QOS_1, false);
// connect
mMqttClient.connect(options);
// subscribe
LBLog.v(TAG, "Subscribing mTopic=" + mTopic);
mMqttClient.subscribe(mTopic);
LBLog.v(TAG, "subscribe success");
isConnected = true;
} catch (MqttException e) {
Log.v(TAG, e.toString());
isConnected = false;
try { // wait some time before retry
Thread.sleep(5000);
} catch (InterruptedException e1) {
Log.v(TAG, e1.toString());
}
}
} while (isConnected == false);
}
});
subscriberThread.start();
}
#Override
public void onDestroy() {
super.onDestroy();
LBLog.v(TAG, "destroying mqtt service...");
unregisterReceiver(receiver);
if(mMqttClient != null && mMqttClient.isConnected()){
try {
mMqttClient.disconnect();
} catch (MqttException e) {
LBLog.e(TAG, "onDestroy", e);
}
}
}
}
When exit the application you should manually finish your service. Also there is one call from the broadcast that we don't see its code.
LBUtil.isNetworkAvailable(context)
Make sure that this thing does not starts your service again.