I created a service that extends MediaBrowserServiceCompat. This service holds a reference to my player and creates a new MediaSession with a callback. Everytime the player changes state, I update the MediaSession's playback state and create a MediaStyle notification. The notification is showing when I start to play something in my player, but the buttons in the notification are not triggering the MediaSession callback, they don't do anything. I'm setting the right flags in the MediaSession, I'm setting the session as active, I'm setting the correct actions in the playback state, I'm passing the session token to the notification but still not getting any callbacks from it. I really don't know what I'm doing wrong. All this code is inside a module imported by my app.
My NotificationHelper class:
private final MusicService mService;
private final NotificationCompat.Action mPlayAction;
private final NotificationCompat.Action mPauseAction;
private final NotificationCompat.Action mNextAction;
private final NotificationCompat.Action mPrevAction;
private final NotificationManager mNotificationManager;
public MediaNotificationManager(MusicService service) {
mService = service;
mNotificationManager =
(NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
mPlayAction =
new NotificationCompat.Action(
R.drawable.exo_icon_play,
"Play",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PLAY));
mPauseAction =
new NotificationCompat.Action(
R.drawable.exo_icon_pause,
"Pause",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PAUSE));
mNextAction =
new NotificationCompat.Action(
R.drawable.exo_icon_next,
"Next",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_SKIP_TO_NEXT));
mPrevAction =
new NotificationCompat.Action(
R.drawable.exo_icon_previous,
"Previous",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS));
// Cancel all notifications to handle the case where the Service was killed and
// restarted by the system.
mNotificationManager.cancelAll();
}
public Notification getNotification(MediaMetadataCompat metadata,
#NonNull PlaybackStateCompat state,
MediaSessionCompat.Token token) {
boolean isPlaying = state.getState() == PlaybackStateCompat.STATE_PLAYING;
MediaDescriptionCompat description = metadata.getDescription();
NotificationCompat.Builder builder =
buildNotification(state, token, isPlaying, description);
return builder.build();
}
private NotificationCompat.Builder buildNotification(#NonNull PlaybackStateCompat state,
MediaSessionCompat.Token token,
boolean isPlaying,
MediaDescriptionCompat description) {
// Create the (mandatory) notification channel when running on Android Oreo.
if (isAndroidOOrHigher()) {
createChannel();
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(mService, CHANNEL_ID)
.setSmallIcon(R.drawable.exo_notification_small_icon)
.setContentTitle("Track title")
.setContentText("Artist - Album")
.setLargeIcon(BitmapFactory.decodeResource(mService.getResources(), R.drawable.exo_notification_small_icon))
.setStyle(new MediaStyle().setShowActionsInCompactView(0).setMediaSession(token));
builder.addAction(mPrevAction);
builder.addAction(isPlaying ? mPauseAction : mPlayAction);
builder.addAction(mNextAction);
return builder;
}
// Does nothing on versions of Android earlier than O.
#RequiresApi(Build.VERSION_CODES.O)
private void createChannel() {
if (mNotificationManager.getNotificationChannel(CHANNEL_ID) == null) {
// The user-visible name of the channel.
CharSequence name = "MediaSession";
// The user-visible description of the channel.
String description = "MediaSession and MediaPlayer";
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
// Configure the notification channel.
mChannel.setDescription(description);
mChannel.enableLights(true);
// Sets the notification light color for notifications posted to this
// channel, if the device supports this feature.
mChannel.setLightColor(Color.RED);
mChannel.enableVibration(true);
mChannel.setVibrationPattern(
new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
mNotificationManager.createNotificationChannel(mChannel);
Log.d(TAG, "createChannel: New channel created");
} else {
Log.d(TAG, "createChannel: Existing channel reused");
}
}
private boolean isAndroidOOrHigher() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
My Service class:
public class MusicService extends MediaBrowserServiceCompat {
private static final String TAG = MusicService.class.getSimpleName();
private MediaSessionCompat mSession;
private PlayerManager playerManager;
private MediaSessionCallback mCallback;
private MediaNotificationManager mediaNotificationManager;
#Override
public void onCreate() {
super.onCreate();
playerManager = PlayerManager.getInstance(this);
playerManager.addListener(new PlayerManagerServiceListener());
mediaNotificationManager = new MediaNotificationManager(this);
// Create a new MediaSession.
mSession = new MediaSessionCompat(this, "MusicService");
mCallback = new MediaSessionCallback();
mSession.setCallback(mCallback);
mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
setSessionToken(mSession.getSessionToken());
mSession.setActive(true);
}
#Override
public void onDestroy() {
mSession.release();
Log.d(TAG, "onDestroy: MediaPlayerAdapter stopped, and MediaSession released");
}
#Override
public BrowserRoot onGetRoot(#NonNull String clientPackageName,
int clientUid,
Bundle rootHints) {
return new BrowserRoot("root", null);
}
#Override
public void onLoadChildren(
#NonNull final String parentMediaId,
#NonNull final Result<List<MediaBrowserCompat.MediaItem>> result) {
result.sendResult(null);
}
// MediaSession Callback: Transport Controls -> MediaPlayerAdapter
public class MediaSessionCallback extends MediaSessionCompat.Callback {
#Override
public void onPlay() {
playerManager.play();
}
#Override
public void onPause() {
playerManager.pause();
}
#Override
public void onStop() {
playerManager.stop();
}
#Override
public void onSkipToNext() {
playerManager.next();
}
#Override
public void onSkipToPrevious() {
playerManager.previous();
}
#Override
public void onSeekTo(long pos) {
playerManager.seekTo(pos);
}
}
public class PlayerManagerServiceListener implements PlayerManager.PlayerManagerListener {
#Override
public void onError(#Nullable Exception error) {
}
#Override
public void onProgress(long duration, long position) {
}
#Override
public void onPlayerChange(int change) {
}
#Override
public void onTrackChange(TrackVO track) {
}
#Override
public void onListChange(List tracks) {
}
#Override
public void onPlaybackStateChange(int playbackState) {
PlaybackStateCompat.Builder playbackstateBuilder = new PlaybackStateCompat.Builder();
int playbackStateCompat = -1;
switch(playbackState) {
case PlaybackStateListener.STATE_PLAYING:
playbackStateCompat = PlaybackStateCompat.STATE_PLAYING;
//playbackstateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PAUSE);
break;
case PlaybackStateListener.STATE_PAUSED:
playbackStateCompat = PlaybackStateCompat.STATE_PAUSED;
//playbackstateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PLAY);
break;
}
if (playbackStateCompat == -1) {
return;
}
mSession.setActive(true);
playbackstateBuilder.setActions(
PlaybackStateCompat.ACTION_PLAY |
PlaybackStateCompat.ACTION_PLAY_PAUSE |
PlaybackStateCompat.ACTION_SKIP_TO_NEXT |
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
playbackstateBuilder.setState(playbackStateCompat, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 0);
PlaybackStateCompat state = playbackstateBuilder.build();
MediaMetadataCompat mediaMetadata = new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, playerManager.getCurrenTrack().getName())
.build();
mSession.setMetadata(mediaMetadata);
mSession.setPlaybackState(state);
Notification notification = mediaNotificationManager.getNotification(
mediaMetadata,
state,
getSessionToken()
);
Intent intent = new Intent(MusicService.this, MusicService.class);
ContextCompat.startForegroundService(MusicService.this, intent);
startForeground(417, notification);
}
}
}
MediaBrowserHelper to initialize the service:
public class MediaBrowserHelper {
private static final String TAG = MediaBrowserHelper.class.getSimpleName();
private final Context mContext;
private final Class<? extends MediaBrowserServiceCompat> mMediaBrowserServiceClass;
private final List<Callback> mCallbackList = new ArrayList<>();
private final MediaBrowserConnectionCallback mMediaBrowserConnectionCallback;
private final MediaControllerCallback mMediaControllerCallback;
private final MediaBrowserSubscriptionCallback mMediaBrowserSubscriptionCallback;
private MediaBrowserCompat mMediaBrowser;
#Nullable
private MediaControllerCompat mMediaController;
public MediaBrowserHelper(Context context,
Class<? extends MediaBrowserServiceCompat> serviceClass) {
mContext = context;
mMediaBrowserServiceClass = serviceClass;
mMediaBrowserConnectionCallback = new MediaBrowserConnectionCallback();
mMediaControllerCallback = new MediaControllerCallback();
mMediaBrowserSubscriptionCallback = new MediaBrowserSubscriptionCallback();
}
public void onStart() {
if (mMediaBrowser == null) {
mMediaBrowser =
new MediaBrowserCompat(
mContext,
new ComponentName(mContext, mMediaBrowserServiceClass),
mMediaBrowserConnectionCallback,
null);
mMediaBrowser.connect();
}
Log.d(TAG, "onStart: Creating MediaBrowser, and connecting");
}
public void onStop() {
if (mMediaController != null) {
mMediaController.unregisterCallback(mMediaControllerCallback);
mMediaController = null;
}
if (mMediaBrowser != null && mMediaBrowser.isConnected()) {
mMediaBrowser.disconnect();
mMediaBrowser = null;
}
resetState();
Log.d(TAG, "onStop: Releasing MediaController, Disconnecting from MediaBrowser");
}
/**
* Called after connecting with a {#link MediaBrowserServiceCompat}.
* <p>
* Override to perform processing after a connection is established.
*
* #param mediaController {#link MediaControllerCompat} associated with the connected
* MediaSession.
*/
protected void onConnected(#NonNull MediaControllerCompat mediaController) {
}
/**
* Called after loading a browsable {#link MediaBrowserCompat.MediaItem}
*
* #param parentId The media ID of the parent item.
* #param children List (possibly empty) of child items.
*/
protected void onChildrenLoaded(#NonNull String parentId,
#NonNull List<MediaBrowserCompat.MediaItem> children) {
}
/**
* Called when the {#link MediaBrowserServiceCompat} connection is lost.
*/
protected void onDisconnected() {
}
#NonNull
protected final MediaControllerCompat getMediaController() {
if (mMediaController == null) {
throw new IllegalStateException("MediaController is null!");
}
return mMediaController;
}
/**
* The internal state of the app needs to revert to what it looks like when it started before
* any connections to the {#link MusicService} happens via the {#link MediaSessionCompat}.
*/
private void resetState() {
performOnAllCallbacks(new CallbackCommand() {
#Override
public void perform(#NonNull Callback callback) {
callback.onPlaybackStateChanged(null);
}
});
Log.d(TAG, "resetState: ");
}
public MediaControllerCompat.TransportControls getTransportControls() {
if (mMediaController == null) {
Log.d(TAG, "getTransportControls: MediaController is null!");
throw new IllegalStateException("MediaController is null!");
}
return mMediaController.getTransportControls();
}
public void registerCallback(Callback callback) {
if (callback != null) {
mCallbackList.add(callback);
// Update with the latest metadata/playback state.
if (mMediaController != null) {
final MediaMetadataCompat metadata = mMediaController.getMetadata();
if (metadata != null) {
callback.onMetadataChanged(metadata);
}
final PlaybackStateCompat playbackState = mMediaController.getPlaybackState();
if (playbackState != null) {
callback.onPlaybackStateChanged(playbackState);
}
}
}
}
private void performOnAllCallbacks(#NonNull CallbackCommand command) {
for (Callback callback : mCallbackList) {
if (callback != null) {
command.perform(callback);
}
}
}
/**
* Helper for more easily performing operations on all listening clients.
*/
private interface CallbackCommand {
void perform(#NonNull Callback callback);
}
// Receives callbacks from the MediaBrowser when it has successfully connected to the
// MediaBrowserService (MusicService).
private class MediaBrowserConnectionCallback extends MediaBrowserCompat.ConnectionCallback {
// Happens as a result of onStart().
#Override
public void onConnected() {
try {
// Get a MediaController for the MediaSession.
mMediaController =
new MediaControllerCompat(mContext, mMediaBrowser.getSessionToken());
mMediaController.registerCallback(mMediaControllerCallback);
// Sync existing MediaSession state to the UI.
mMediaControllerCallback.onMetadataChanged(mMediaController.getMetadata());
mMediaControllerCallback.onPlaybackStateChanged(
mMediaController.getPlaybackState());
MediaBrowserHelper.this.onConnected(mMediaController);
} catch (RemoteException e) {
Log.d(TAG, String.format("onConnected: Problem: %s", e.toString()));
throw new RuntimeException(e);
}
mMediaBrowser.subscribe(mMediaBrowser.getRoot(), mMediaBrowserSubscriptionCallback);
}
}
// Receives callbacks from the MediaBrowser when the MediaBrowserService has loaded new media
// that is ready for playback.
public class MediaBrowserSubscriptionCallback extends MediaBrowserCompat.SubscriptionCallback {
#Override
public void onChildrenLoaded(#NonNull String parentId,
#NonNull List<MediaBrowserCompat.MediaItem> children) {
MediaBrowserHelper.this.onChildrenLoaded(parentId, children);
}
}
// Receives callbacks from the MediaController and updates the UI state,
// i.e.: Which is the current item, whether it's playing or paused, etc.
private class MediaControllerCallback extends MediaControllerCompat.Callback {
#Override
public void onMetadataChanged(final MediaMetadataCompat metadata) {
performOnAllCallbacks(new CallbackCommand() {
#Override
public void perform(#NonNull Callback callback) {
callback.onMetadataChanged(metadata);
}
});
}
#Override
public void onPlaybackStateChanged(#Nullable final PlaybackStateCompat state) {
performOnAllCallbacks(new CallbackCommand() {
#Override
public void perform(#NonNull Callback callback) {
callback.onPlaybackStateChanged(state);
}
});
}
// This might happen if the MusicService is killed while the Activity is in the
// foreground and onStart() has been called (but not onStop()).
#Override
public void onSessionDestroyed() {
resetState();
onPlaybackStateChanged(null);
MediaBrowserHelper.this.onDisconnected();
}
}
}
Manifest:
<service android:name="com.amco.playermanager.MusicService">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
<receiver android:name="android.support.v4.media.session.MediaButtonReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</receiver>
It turns out that the whole problem was caused by having another BroadcastReceiver handling MEDIA_BUTTON declared in my app's Manifest. By removing that receiver everything works now.
Related
I have registered my beacon on the Google Beacon Platform and download the "Beacon Tools" android app to scan the beacon. Below I have attached its screenshot.
Now I'm developing an android app for that beacon and using this app I can get attachments that I set for the beacon. I 'am using Nearby Messages API for this. Now I want to get UUID and distance between user & beacon. I 'm new to these things and read many stackoverflow questions and answers. but those didn't solve my problem
Below I have mentioned my classes.
MainActivity.java
public class MainActivity extends AppCompatActivity implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = MainActivity.class.getSimpleName();
private static final int PERMISSIONS_REQUEST_CODE = 1111;
private static final String KEY_SUBSCRIBED = "subscribed";
/**
* The entry point to Google Play Services.
*/
private GoogleApiClient mGoogleApiClient;
/**
* The container {#link android.view.ViewGroup} for the minimal UI associated with this sample.
*/
private RelativeLayout mContainer;
/**
* Tracks subscription state. Set to true when a call to
* {#link Messages#subscribe(GoogleApiClient, MessageListener)} succeeds.
*/
private boolean mSubscribed = false;
/**
* Adapter for working with messages from nearby beacons.
*/
private ArrayAdapter<String> mNearbyMessagesArrayAdapter;
/**
* Backing data structure for {#code mNearbyMessagesArrayAdapter}.
*/
private List<String> mNearbyMessagesList = new ArrayList<>();
FirebaseDatabase db = FirebaseDatabase.getInstance();
DatabaseReference databaseReference;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
//setSupportActionBar(toolbar);
if (savedInstanceState != null) {
mSubscribed = savedInstanceState.getBoolean(KEY_SUBSCRIBED, false);
}
mContainer = (RelativeLayout) findViewById(R.id.main_activity_container);
if (!havePermissions()) {
Log.i(TAG, "Requesting permissions needed for this app.");
requestPermissions();
}
final List<String> cachedMessages = Utils.getCachedMessages(this);
if (cachedMessages != null) {
mNearbyMessagesList.addAll(cachedMessages);
}
final ListView nearbyMessagesListView = (ListView) findViewById(
R.id.nearby_messages_list_view);
mNearbyMessagesArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
mNearbyMessagesList);
if (nearbyMessagesListView != null) {
nearbyMessagesListView.setAdapter(mNearbyMessagesArrayAdapter);
}
}
#Override
protected void onResume() {
super.onResume();
getSharedPreferences(getApplicationContext().getPackageName(), Context.MODE_PRIVATE)
.registerOnSharedPreferenceChangeListener(this);
if (havePermissions()) {
buildGoogleApiClient();
}
}
#TargetApi(Build.VERSION_CODES.M)
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
if (requestCode != PERMISSIONS_REQUEST_CODE) {
return;
}
for (int i = 0; i < permissions.length; i++) {
String permission = permissions[i];
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
if (shouldShowRequestPermissionRationale(permission)) {
Log.i(TAG, "Permission denied without 'NEVER ASK AGAIN': " + permission);
showRequestPermissionsSnackbar();
} else {
Log.i(TAG, "Permission denied with 'NEVER ASK AGAIN': " + permission);
showLinkToSettingsSnackbar();
}
} else {
Log.i(TAG, "Permission granted, building GoogleApiClient");
buildGoogleApiClient();
}
}
}
private synchronized void buildGoogleApiClient() {
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Nearby.MESSAGES_API, new MessagesOptions.Builder()
.setPermissions(NearbyPermissions.BLE).build())
.addConnectionCallbacks(this)
.enableAutoManage(this, this)
.build();
}
}
#Override
protected void onPause() {
getSharedPreferences(getApplicationContext().getPackageName(), Context.MODE_PRIVATE)
.unregisterOnSharedPreferenceChangeListener(this);
super.onPause();
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
if (mContainer != null) {
Snackbar.make(mContainer, "Exception while connecting to Google Play services: " +
connectionResult.getErrorMessage(),
Snackbar.LENGTH_INDEFINITE).show();
}
}
#Override
public void onConnectionSuspended(int i) {
Log.w(TAG, "Connection suspended. Error code: " + i);
}
#Override
public void onConnected(#Nullable Bundle bundle) {
Log.i(TAG, "GoogleApiClient connected");
subscribe();
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (TextUtils.equals(key, Utils.KEY_CACHED_MESSAGES)) {
mNearbyMessagesList.clear();
mNearbyMessagesList.addAll(Utils.getCachedMessages(this));
mNearbyMessagesArrayAdapter.notifyDataSetChanged();
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_SUBSCRIBED, mSubscribed);
}
private boolean havePermissions() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED;
}
private void requestPermissions() {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST_CODE);
}
/**
* Calls {#link Messages#subscribe(GoogleApiClient, MessageListener, SubscribeOptions)},
* using a {#link Strategy} for BLE scanning. Attaches a {#link ResultCallback} to monitor
* whether the call to {#code subscribe()} succeeded or failed.
*/
private void subscribe() {
// In this sample, we subscribe when the activity is launched, but not on device orientation
// change.
if (mSubscribed) {
Log.i(TAG, "Already subscribed.");
return;
}
SubscribeOptions options = new SubscribeOptions.Builder()
.setStrategy(Strategy.BLE_ONLY)
.build();
Nearby.Messages.subscribe(mGoogleApiClient, getPendingIntent(), options)
.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(#NonNull Status status) {
if (status.isSuccess()) {
Log.i(TAG, "Subscribed successfully.");
startService(getBackgroundSubscribeServiceIntent());
} else {
Log.e(TAG, "Operation failed. Error: " +
NearbyMessagesStatusCodes.getStatusCodeString(
status.getStatusCode()));
}
}
});
}
private PendingIntent getPendingIntent() {
return PendingIntent.getService(this, 0,
getBackgroundSubscribeServiceIntent(), PendingIntent.FLAG_UPDATE_CURRENT);
}
private Intent getBackgroundSubscribeServiceIntent() {
return new Intent(this, BackgroundSubscribeIntentService.class);
}
/**
* Displays {#link Snackbar} instructing user to visit Settings to grant permissions required by
* this application.
*/
private void showLinkToSettingsSnackbar() {
if (mContainer == null) {
return;
}
Snackbar.make(mContainer,
R.string.permission_denied_explanation,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.settings, new View.OnClickListener() {
#Override
public void onClick(View view) {
// Build intent that displays the App settings screen.
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package",
BuildConfig.APPLICATION_ID, null);
intent.setData(uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}).show();
}
/**
* Displays {#link Snackbar} with button for the user to re-initiate the permission workflow.
*/
private void showRequestPermissionsSnackbar() {
if (mContainer == null) {
return;
}
Snackbar.make(mContainer, R.string.permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
#Override
public void onClick(View view) {
// Request permission.
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
PERMISSIONS_REQUEST_CODE);
}
}).show();
}
}
BackgroundSubscribeIntentService.java
public class BackgroundSubscribeIntentService extends IntentService {
private static final String TAG = "BackSubIntentService";
private static final int MESSAGES_NOTIFICATION_ID = 1;
private static final int NUM_MESSAGES_IN_NOTIFICATION = 5;
public BackgroundSubscribeIntentService() {
super("BackgroundSubscribeIntentService");
}
#Override
public void onCreate() {
super.onCreate();
updateNotification();
}
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
Nearby.Messages.handleIntent(intent, new MessageListener() {
#Override
public void onFound(Message message) {
Utils.saveFoundMessage(getApplicationContext(), message);
updateNotification();
}
#Override
public void onLost(Message message) {
Utils.removeLostMessage(getApplicationContext(), message);
updateNotification();
}
});
}
}
private void updateNotification() {
List<String> messages = Utils.getCachedMessages(getApplicationContext());
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Intent launchIntent = new Intent(getApplicationContext(), MainActivity.class);
launchIntent.setAction(Intent.ACTION_MAIN);
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0,
launchIntent, PendingIntent.FLAG_UPDATE_CURRENT);
String contentTitle = getContentTitle(messages);
String contentText = getContentText(messages);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.star_on)
.setContentTitle(contentTitle)
.setContentText(contentText)
.setStyle(new NotificationCompat.BigTextStyle().bigText(contentText))
.setOngoing(true)
.setContentIntent(pi);
notificationManager.notify(MESSAGES_NOTIFICATION_ID, notificationBuilder.build());
}
private String getContentTitle(List<String> messages) {
switch (messages.size()) {
case 0:
return getResources().getString(R.string.scanning);
case 1:
return getResources().getString(R.string.one_message);
default:
return getResources().getString(R.string.many_messages, messages.size());
}
}
private String getContentText(List<String> messages) {
String newline = System.getProperty("line.separator");
if (messages.size() < NUM_MESSAGES_IN_NOTIFICATION) {
return TextUtils.join(newline, messages);
}
return TextUtils.join(newline, messages.subList(0, NUM_MESSAGES_IN_NOTIFICATION)) +
newline + "…";
}
}
Utils.java
public final class Utils {
static final String KEY_CACHED_MESSAGES = "cached-messages";
/**
* Fetches message strings stored in {#link SharedPreferences}.
*
* #param context The context.
* #return A list (possibly empty) containing message strings.
*/
static List<String> getCachedMessages(Context context) {
SharedPreferences sharedPrefs = getSharedPreferences(context);
String cachedMessagesJson = sharedPrefs.getString(KEY_CACHED_MESSAGES, "");
if (TextUtils.isEmpty(cachedMessagesJson)) {
return Collections.emptyList();
} else {
Type type = new TypeToken<List<String>>() {}.getType();
return new Gson().fromJson(cachedMessagesJson, type);
}
}
/**
* Saves a message string to {#link SharedPreferences}.
*
* #param context The context.
* #param message The Message whose payload (as string) is saved to SharedPreferences.
*/
static void saveFoundMessage(Context context, Message message) {
ArrayList<String> cachedMessages = new ArrayList<>(getCachedMessages(context));
Set<String> cachedMessagesSet = new HashSet<>(cachedMessages);
String messageString = new String(message.getContent());
if (!cachedMessagesSet.contains(messageString)) {
cachedMessages.add(0, new String(message.getContent()));
getSharedPreferences(context)
.edit()
.putString(KEY_CACHED_MESSAGES, new Gson().toJson(cachedMessages))
.apply();
}
}
/**
* Removes a message string from {#link SharedPreferences}.
* #param context The context.
* #param message The Message whose payload (as string) is removed from SharedPreferences.
*/
static void removeLostMessage(Context context, Message message) {
ArrayList<String> cachedMessages = new ArrayList<>(getCachedMessages(context));
cachedMessages.remove(new String(message.getContent()));
getSharedPreferences(context)
.edit()
.putString(KEY_CACHED_MESSAGES, new Gson().toJson(cachedMessages))
.apply();
}
/**
* Gets the SharedPReferences object that is used for persisting data in this application.
*
* #param context The context.
* #return The single {#link SharedPreferences} instance that can be used to retrieve and
modify values.
*/
static SharedPreferences getSharedPreferences(Context context) {
return context.getSharedPreferences(
context.getApplicationContext().getPackageName(),
Context.MODE_PRIVATE);
}
}
How can I get UUID and distance of the beacon and where I can apply the code. I have no prior experience of beacons.
The code shown in the question sets up a background subscription to BLE beacon Nearby messages that are delivered to a service by a PendingIntent: Nearby.Messages.subscribe(mGoogleApiClient, getPendingIntent(), options).setResultCallback(...);
Unfortunately, Google Nearby does not support delivering signal strength or distance estimates to background subscriptions. You can only do this with foreground subscriptions:
RSSI and Distance Callbacks
In addition to found and lost callbacks, a foreground subscription can update your MessageListener when Nearby has new information about the BLE signal associated with a message.
Note:
These extra callbacks are currently only delivered for BLE beacon messages (both attachments and beacon IDs).
These extra callbacks are not delivered to background (PendingIntent) subscriptions.
See https://developers.google.com/nearby/messages/android/advanced
If you want to get signal strength and distance estimates, with Google Nearby you must rewrite your code to use a foreground only subscription. The results will only be delivered if your app is in the foreground.
MessageListener messageListener = new MessageListener() {
#Override
public void onFound(final Message message) {
Log.i(TAG, "Found message: " + message);
}
#Override
public void onBleSignalChanged(final Message message, final BleSignal bleSignal) {
Log.i(TAG, "Message: " + message + " has new BLE signal information: " + bleSignal);
}
#Override
public void onDistanceChanged(final Message message, final Distance distance) {
Log.i(TAG, "Distance changed, message: " + message + ", new distance: " + distance);
}
#Override
public void onLost(final Message message) {
Log.i(TAG, "Lost message: " + message);
}
};
Nearby.getMessagesClient(this).subscribe(messageListener, options);
The link above shows a full example of how to do that.
I was trying to create a location service which gives location updates when app is in background specially for Android O. I tried many examples available on the internet by creating a foreground service or passing pendingIntent in requestLocationUpdates. But none of them worked. My service stops giving me location update after 10 minutes. I tried to put job scheduler also but that also doesn't work. I found the github project provided by Google this, but those examples are also not working.
Here is my service from one of the above example:
public class LocationUpdatesService extends Service {
private static final String PACKAGE_NAME =
"com.google.android.gms.location.sample.locationupdatesforegroundservice";
private static final String TAG = LocationUpdatesService.class.getSimpleName();
/**
* The name of the channel for notifications.
*/
private static final String CHANNEL_ID = "channel_01";
static final String ACTION_BROADCAST = PACKAGE_NAME + ".broadcast";
static final String EXTRA_LOCATION = PACKAGE_NAME + ".location";
private static final String EXTRA_STARTED_FROM_NOTIFICATION = PACKAGE_NAME +
".started_from_notification";
private final IBinder mBinder = new LocalBinder();
/**
* The desired interval for location updates. Inexact. Updates may be more or less frequent.
*/
private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000;
/**
* The fastest rate for active location updates. Updates will never be more frequent
* than this value.
*/
private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
UPDATE_INTERVAL_IN_MILLISECONDS / 2;
/**
* The identifier for the notification displayed for the foreground service.
*/
private static final int NOTIFICATION_ID = 12345678;
/**
* Used to check whether the bound activity has really gone away and not unbound as part of an
* orientation change. We create a foreground service notification only if the former takes
* place.
*/
private boolean mChangingConfiguration = false;
private NotificationManager mNotificationManager;
/**
* Contains parameters used by {#link com.google.android.gms.location.FusedLocationProviderApi}.
*/
private LocationRequest mLocationRequest;
/**
* Provides access to the Fused Location Provider API.
*/
private FusedLocationProviderClient mFusedLocationClient;
/**
* Callback for changes in location.
*/
private LocationCallback mLocationCallback;
private Handler mServiceHandler;
/**
* The current location.
*/
private Location mLocation;
public LocationUpdatesService() {
}
#Override
public void onCreate() {
super.onCreate();
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
mLocationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
onNewLocation(locationResult.getLastLocation());
}
};
createLocationRequest();
getLastLocation();
HandlerThread handlerThread = new HandlerThread(TAG);
handlerThread.start();
mServiceHandler = new Handler(handlerThread.getLooper());
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Android O requires a Notification Channel.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getString(R.string.app_name);
// Create the channel for the notification
NotificationChannel mChannel =
new NotificationChannel(CHANNEL_ID, name, NotificationManager.IMPORTANCE_DEFAULT);
// Set the Notification Channel for the Notification Manager.
mNotificationManager.createNotificationChannel(mChannel);
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service started");
boolean startedFromNotification = intent.getBooleanExtra(EXTRA_STARTED_FROM_NOTIFICATION,
false);
// We got here because the user decided to remove location updates from the notification.
if (startedFromNotification) {
removeLocationUpdates();
stopSelf();
}
// Tells the system to not try to recreate the service after it has been killed.
return START_NOT_STICKY;
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mChangingConfiguration = true;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
// Called when a client (MainActivity in case of this sample) comes to the foreground
// and binds with this service. The service should cease to be a foreground service
// when that happens.
Log.i(TAG, "in onBind()");
stopForeground(true);
mChangingConfiguration = false;
return mBinder;
}
#Override
public void onRebind(Intent intent) {
// Called when a client (MainActivity in case of this sample) returns to the foreground
// and binds once again with this service. The service should cease to be a foreground
// service when that happens.
Log.i(TAG, "in onRebind()");
stopForeground(true);
mChangingConfiguration = false;
super.onRebind(intent);
}
#Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "Last client unbound from service");
// Called when the last client (MainActivity in case of this sample) unbinds from this
// service. If this method is called due to a configuration change in MainActivity, we
// do nothing. Otherwise, we make this service a foreground service.
if (!mChangingConfiguration && Utils.requestingLocationUpdates(this)) {
Log.i(TAG, "Starting foreground service");
/*
// TODO(developer). If targeting O, use the following code.
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) {
mNotificationManager.startServiceInForeground(new Intent(this,
LocationUpdatesService.class), NOTIFICATION_ID, getNotification());
} else {
startForeground(NOTIFICATION_ID, getNotification());
}
*/
startForeground(NOTIFICATION_ID, getNotification());
}
return true; // Ensures onRebind() is called when a client re-binds.
}
#Override
public void onDestroy() {
mServiceHandler.removeCallbacksAndMessages(null);
}
/**
* Makes a request for location updates. Note that in this sample we merely log the
* {#link SecurityException}.
*/
public void requestLocationUpdates() {
Log.i(TAG, "Requesting location updates");
Utils.setRequestingLocationUpdates(this, true);
startService(new Intent(getApplicationContext(), LocationUpdatesService.class));
try {
mFusedLocationClient.requestLocationUpdates(mLocationRequest,
mLocationCallback, Looper.myLooper());
} catch (SecurityException unlikely) {
Utils.setRequestingLocationUpdates(this, false);
Log.e(TAG, "Lost location permission. Could not request updates. " + unlikely);
}
}
/**
* Removes location updates. Note that in this sample we merely log the
* {#link SecurityException}.
*/
public void removeLocationUpdates() {
Log.i(TAG, "Removing location updates");
try {
mFusedLocationClient.removeLocationUpdates(mLocationCallback);
Utils.setRequestingLocationUpdates(this, false);
stopSelf();
} catch (SecurityException unlikely) {
Utils.setRequestingLocationUpdates(this, true);
Log.e(TAG, "Lost location permission. Could not remove updates. " + unlikely);
}
}
/**
* Returns the {#link NotificationCompat} used as part of the foreground service.
*/
private Notification getNotification() {
Intent intent = new Intent(this, LocationUpdatesService.class);
CharSequence text = Utils.getLocationText(mLocation);
// Extra to help us figure out if we arrived in onStartCommand via the notification or not.
intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true);
// The PendingIntent that leads to a call to onStartCommand() in this service.
PendingIntent servicePendingIntent = PendingIntent.getService(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// The PendingIntent to launch activity.
PendingIntent activityPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, MainActivity.class), 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.addAction(R.drawable.ic_launch, getString(R.string.launch_activity),
activityPendingIntent)
.addAction(R.drawable.ic_cancel, getString(R.string.remove_location_updates),
servicePendingIntent)
.setContentText(text)
.setContentTitle(Utils.getLocationTitle(this))
.setOngoing(true)
.setPriority(Notification.PRIORITY_HIGH)
.setSmallIcon(R.mipmap.ic_launcher)
.setTicker(text)
.setWhen(System.currentTimeMillis());
// Set the Channel ID for Android O.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(CHANNEL_ID); // Channel ID
}
return builder.build();
}
private void getLastLocation() {
try {
mFusedLocationClient.getLastLocation()
.addOnCompleteListener(new OnCompleteListener<Location>() {
#Override
public void onComplete(#NonNull Task<Location> task) {
if (task.isSuccessful() && task.getResult() != null) {
mLocation = task.getResult();
} else {
Log.w(TAG, "Failed to get location.");
}
}
});
} catch (SecurityException unlikely) {
Log.e(TAG, "Lost location permission." + unlikely);
}
}
private void onNewLocation(Location location) {
Log.i(TAG, "New location: " + location);
mLocation = location;
// Notify anyone listening for broadcasts about the new location.
Intent intent = new Intent(ACTION_BROADCAST);
intent.putExtra(EXTRA_LOCATION, location);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
// Update notification content if running as a foreground service.
if (serviceIsRunningInForeground(this)) {
mNotificationManager.notify(NOTIFICATION_ID, getNotification());
}
}
/**
* Sets the location request parameters.
*/
private void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
/**
* Class used for the client Binder. Since this service runs in the same process as its
* clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
LocationUpdatesService getService() {
return LocationUpdatesService.this;
}
}
/**
* Returns true if this is a foreground service.
*
* #param context The {#link Context}.
*/
public boolean serviceIsRunningInForeground(Context context) {
ActivityManager manager = (ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
Integer.MAX_VALUE)) {
if (getClass().getName().equals(service.service.getClassName())) {
if (service.foreground) {
return true;
}
}
}
return false;
}
}
Here is my MainActivity:
public class MainActivity extends AppCompatActivity implements
OnSharedPreferenceChangeListener {
private static final String TAG = MainActivity.class.getSimpleName();
// Used in checking for runtime permissions.
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
// The BroadcastReceiver used to listen from broadcasts from the service.
private MyReceiver myReceiver;
// A reference to the service used to get location updates.
private LocationUpdatesService mService = null;
// Tracks the bound state of the service.
private boolean mBound = false;
// UI elements.
private Button mRequestLocationUpdatesButton;
private Button mRemoveLocationUpdatesButton;
// Monitors the state of the connection to the service.
private final ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
LocationUpdatesService.LocalBinder binder = (LocationUpdatesService.LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
mBound = false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myReceiver = new MyReceiver();
setContentView(R.layout.activity_main);
// Check that the user hasn't revoked permissions by going to Settings.
// if (Utils.requestingLocationUpdates(this)) {
if (!checkPermissions()) {
requestPermissions();
}
// }
}
#Override
protected void onStart() {
super.onStart();
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
mRequestLocationUpdatesButton = (Button) findViewById(R.id.request_location_updates_button);
mRemoveLocationUpdatesButton = (Button) findViewById(R.id.remove_location_updates_button);
mRequestLocationUpdatesButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!checkPermissions()) {
requestPermissions();
} else {
mService.requestLocationUpdates();
}
}
});
mRemoveLocationUpdatesButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mService.removeLocationUpdates();
}
});
// Restore the state of the buttons when the activity (re)launches.
setButtonsState(Utils.requestingLocationUpdates(this));
// Bind to the service. If the service is in foreground mode, this signals to the service
// that since this activity is in the foreground, the service can exit foreground mode.
bindService(new Intent(this, LocationUpdatesService.class), mServiceConnection,
Context.BIND_AUTO_CREATE);
}
#Override
protected void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(myReceiver,
new IntentFilter(LocationUpdatesService.ACTION_BROADCAST));
}
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(myReceiver);
super.onPause();
}
#Override
protected void onStop() {
if (mBound) {
// Unbind from the service. This signals to the service that this activity is no longer
// in the foreground, and the service can respond by promoting itself to a foreground
// service.
unbindService(mServiceConnection);
mBound = false;
}
PreferenceManager.getDefaultSharedPreferences(this)
.unregisterOnSharedPreferenceChangeListener(this);
super.onStop();
}
/**
* Returns the current state of the permissions needed.
*/
private boolean checkPermissions() {
return PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION);
}
private void requestPermissions() {
boolean shouldProvideRationale =
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION);
// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.");
Snackbar.make(
findViewById(R.id.activity_main),
R.string.permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
#Override
public void onClick(View view) {
// Request permission
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
})
.show();
} else {
Log.i(TAG, "Requesting permission");
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
Log.i(TAG, "onRequestPermissionResult");
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
if (grantResults.length <= 0) {
// If user interaction was interrupted, the permission request is cancelled and you
// receive empty arrays.
Log.i(TAG, "User interaction was cancelled.");
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission was granted.
mService.requestLocationUpdates();
} else {
// Permission denied.
setButtonsState(false);
Snackbar.make(
findViewById(R.id.activity_main),
R.string.permission_denied_explanation,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.settings, new View.OnClickListener() {
#Override
public void onClick(View view) {
// Build intent that displays the App settings screen.
Intent intent = new Intent();
intent.setAction(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package",
BuildConfig.APPLICATION_ID, null);
intent.setData(uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
})
.show();
}
}
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
// Update the buttons state depending on whether location updates are being requested.
if (s.equals(Utils.KEY_REQUESTING_LOCATION_UPDATES)) {
setButtonsState(sharedPreferences.getBoolean(Utils.KEY_REQUESTING_LOCATION_UPDATES,
false));
}
}
/**
* Receiver for broadcasts sent by {#link LocationUpdatesService}.
*/
private class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Location location = intent.getParcelableExtra(LocationUpdatesService.EXTRA_LOCATION);
if (location != null) {
Toast.makeText(MainActivity.this, Utils.getLocationText(location),
Toast.LENGTH_SHORT).show();
}
}
}
private void setButtonsState(boolean requestingLocationUpdates) {
if (requestingLocationUpdates) {
mRequestLocationUpdatesButton.setEnabled(false);
mRemoveLocationUpdatesButton.setEnabled(true);
} else {
mRequestLocationUpdatesButton.setEnabled(true);
mRemoveLocationUpdatesButton.setEnabled(false);
}
}
}
Can someone tell me what will be the correct solution for the background service for Android O.
On Android 10 (API level 29) and higher, you must declare foreground service type.
Need to add android:foregroundServiceType="location" in Service.
<service android:name=".LocationUpdateService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="location"
/>
I am building a simple downloader app, with a pause/resume button. When I click on pause button and kill the app the download progress isn't shown anymore, after I open the app again. I want to save the download progress state even if the app is killed. Can anyone help me with this? I'm building the app using download manager pro library from github.
here's the code:
Activity Class:
public class MainActivity extends AppCompatActivity {
String[] permissions = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
public static final int REQUEST_ID_MULTIPLE_PERMISSIONS = 100;
EditText edt_url;
Button btn_download;
TextView fileNametv, ProgressbarTv;
private MyDownloadService mService;
ProgressBar progressBar;
String filename, url;
/* UrlFileNameListener listener;*/
Integer progress;
MyReceiver receiver;
public static MainActivity instance;
/* RecyclerView recyclerView;
List<Model> downloadList;
LinearLayoutManager manager;
DownloadAdapter adapter;
// ImageView pausebtn;
int position;*/
File myDirectory;
Boolean mBound = false;
Button pause_btn;
int tasktoken;
/* Model model;
Parcelable mListState;
private final String KEY_RECYCLER_STATE = "recycler_state";
private final String KEY_LIST_STATE = "list_state";*/
private ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mBound = true;
MyDownloadService.LocalBinder localBinder = (MyDownloadService.LocalBinder) iBinder;
mService = localBinder.getService();
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
mBound = false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* if(savedInstanceState!=null){
mListState = savedInstanceState.getParcelable(KEY_RECYCLER_STATE);
downloadList =savedInstanceState.getParcelableArrayList(KEY_LIST_STATE);
} */
setContentView(R.layout.activity_main);
edt_url = findViewById(R.id.edt_url);
btn_download = findViewById(R.id.btn_download);
pause_btn = findViewById(R.id.pause_resume);
/*recyclerView = findViewById(R.id.recycler_view);
downloadList = new ArrayList<>();
manager = new LinearLayoutManager(this);
adapter = new DownloadAdapter(downloadList, this);
recyclerView.setLayoutManager(manager);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
manager.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);
recyclerView.setAdapter(adapter);*/
fileNametv = findViewById(R.id.download_file_name);
ProgressbarTv = findViewById(R.id.progress_tv);
progressBar = findViewById(R.id.download_progress);
// pausebtn = findViewById(R.id.pause_resume);
instance = this;
/* if (progress!= null && filename != null) {
savedInstanceState.putInt("progress", progress);
savedInstanceState.putString("filename",filename);
// savedInstanceState.putString("filename", model.getFileName());
// savedInstanceState.putParcelable("list",mListState);
}*/
receiver = new MyReceiver();
btn_download.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getDownloadProcess();
}
});
pause_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mService.pauseDownload(tasktoken);
}
});
LocalBroadcastManager.getInstance(this)
.registerReceiver(receiver,
new IntentFilter("download"));
if (checkAndRequestPermissions()) {
myDirectory = new File(Environment.getExternalStorageDirectory() + "/" + "RITSDownloads2");
if (!myDirectory.exists()) {
myDirectory.mkdir();
}
}
}
#Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this,MyDownloadService.class);
bindService(intent,mServiceConnection,Context.BIND_AUTO_CREATE);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("progress", progress);
outState.putString("filename",filename);
/* mListState = manager.onSaveInstanceState();
outState.putParcelable(KEY_RECYCLER_STATE,mListState);
outState.putParcelableArrayList(KEY_LIST_STATE, (ArrayList<? extends Parcelable>) adapter.getDownloadList());*/
}
#Override
protected void onPause() {
super.onPause();
//mListState = manager.onSaveInstanceState();
}
#Override
protected void onResume() {
super.onResume();
// manager.onRestoreInstanceState(mListState);
}
#Override
public void onBackPressed() {
progress = progressBar.getProgress();
super.onBackPressed();
}
/* #Override
public void onRestoreInstanceState(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onRestoreInstanceState(savedInstanceState, persistentState);
manager.onRestoreInstanceState(mListState);
savedInstanceState.getParcelable(KEY_RECYCLER_STATE);
savedInstanceState.getParcelableArrayList(KEY_LIST_STATE);
}
*/
private void getDownloadProcess() {
url = edt_url.getText().toString();
filename = URLUtil.guessFileName(url, null, null);
//listener.setUrl(url,filename);
edt_url.setText("");
/* model = new Model();
model.setFileName(filename);
downloadList.add(model);
adapter.notifyDataSetChanged();*/
fileNametv.setText(filename);
Intent intent = new Intent(MainActivity.this, MyDownloadService.class);
intent.putExtra("filename", filename);
intent.putExtra("url", url);
intent.setAction(DownloadActions.ACTION.Download_ACTION);
startService(intent);
}
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
progress = intent.getIntExtra("progress", 1);
ReportStructure reportStructure = MyDownloadService.downloadManagerPro.singleDownloadStatus(intent.getIntExtra("tasktoken",1));
tasktoken = intent.getIntExtra("tasktoken",1);
// model.setProgress(progress);
/*int position = downloadList.indexOf(model);
DownloadAdapter.DownloadHolder holder = getDownloadHolder(position);
holder.progressBar.setProgress(progress);*/
progressBar.setProgress(progress);
}
}
/* public DownloadAdapter.DownloadHolder getDownloadHolder(int position) {
return (DownloadAdapter.DownloadHolder) recyclerView.findViewHolderForLayoutPosition(position);
}
*/
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
savedInstanceState.getInt("progress");
savedInstanceState.getString("filename");
/* Log.d("savedInstancestate",savedInstanceState.toString());
//savedInstanceState.getInt("position");
if(savedInstanceState!=null){
List<Model> downloadList = savedInstanceState.getParcelableArrayList(KEY_LIST_STATE);
adapter = new DownloadAdapter(downloadList,this);
}
*/
}
private boolean checkAndRequestPermissions() {
if (ContextCompat.checkSelfPermission(this, permissions[0]) != PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(this, permissions[1]) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, permissions, REQUEST_ID_MULTIPLE_PERMISSIONS);
return false;
} else {
return true;
}
}
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String permissions[], #NonNull int[] grantResults) {
String TAG = "LOG_PERMISSION";
Log.d(TAG, "Permission callback called-------");
switch (requestCode) {
case REQUEST_ID_MULTIPLE_PERMISSIONS: {
Map<String, Integer> perms = new HashMap<>();
// Initialize the map with both permissions
perms.put(this.permissions[0], PackageManager.PERMISSION_GRANTED);
perms.put(this.permissions[1], PackageManager.PERMISSION_GRANTED);
// Fill with actual results from user
if (grantResults.length > 0) {
for (int i = 0; i < permissions.length; i++)
perms.put(permissions[i], grantResults[i]);
// Check for both permissions
if (perms.get(this.permissions[0]) == PackageManager.PERMISSION_GRANTED
&& perms.get(this.permissions[1]) == PackageManager.PERMISSION_GRANTED
) {
Log.d(TAG, "Phone state and storage permissions granted");
// process the normal flow
//else any one or both the permissions are not granted
//TODO Do your stuff here after permissions granted
} else {
Log.d(TAG, "Some permissions are not granted ask again ");
//permissions is denied (this is the first time, when "never ask again" is not checked) so ask again explaining the usage of permissions
// //shouldShowRequestPermissionRationale will return true
//show the dialog or snackbar saying its necessary and try again otherwise proceed with setup.
if (ActivityCompat.shouldShowRequestPermissionRationale(this, this.permissions[0]) ||
ActivityCompat.shouldShowRequestPermissionRationale(this, this.permissions[1])) {
showDialogOK("Phone state and storage permissions required for this app",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
checkAndRequestPermissions();
break;
case DialogInterface.BUTTON_NEGATIVE:
// proceed with logic by disabling the related features or quit the app.
break;
}
}
});
}
//permissions is denied (and never ask again is checked)
//shouldShowRequestPermissionRationale will return false
else {
Toast.makeText(this, "Go to settings and enable permissions", Toast.LENGTH_LONG)
.show();
//proceed with logic by disabling the related features or quit the app.
}
}
}
}
}
}
private void showDialogOK(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", okListener)
.create()
.show();
}
}
Service Class:
public class MyDownloadService extends Service implements DownloadManagerListener {
private static final String LOG_TAG = "tag";
public static DownloadManagerPro downloadManagerPro;
File myDirectory;
int taskToken;
String name;
Intent notificationIntent;
Notification notification;
PendingIntent pendingIntent;
private IBinder binder = new LocalBinder();
#Nullable
#Override
public IBinder onBind(Intent intent) {
return binder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals(DownloadActions.ACTION.Download_ACTION)) {
Log.d(LOG_TAG, "Received Start Foreground Intent");
}
name = intent.getStringExtra("filename");
final String url = intent.getStringExtra("url");
Log.d(LOG_TAG, name);
Log.d(LOG_TAG, url);
downloadManagerPro = new DownloadManagerPro(this.getApplicationContext());
downloadManagerPro.init("RITSDownloads2/", 16, this);
myDirectory = new File(Environment.getExternalStorageDirectory() + "/" + "RITSDownloads2");
if (!myDirectory.exists()) {
myDirectory.mkdir();
}
taskToken = downloadManagerPro.addTask(name, url, 16, true, true);
Log.d(LOG_TAG, String.valueOf(taskToken));
try {
downloadManagerPro.startDownload(taskToken);
notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setAction(DownloadActions.ACTION.Download_ACTION);
pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
notification = new NotificationCompat.Builder(this)
.setContentTitle("Downloading")
.setTicker("Rits Download")
.setContentText(name)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setContentIntent(pendingIntent)
.setOngoing(true)
.build();
startForeground(DownloadActions.NOTIFICATION_ID.FOREGROUND_SERVICE,
notification);
// stopForeground(true);
// stopSelf();
} catch (IOException e) {
e.printStackTrace();
}
return START_STICKY;
}
#Override
public void OnDownloadStarted(long taskId) {
Log.d(LOG_TAG, "DownloadStarted");
}
#Override
public void OnDownloadPaused(long taskId) {
}
#Override
public void onDownloadProcess(long taskId, double percent, long downloadedLength) {
final int progress = (int) percent;
final int taskToken = (int) taskId;
// int position = positions.get(taskToken);
notification = new NotificationCompat.Builder(this)
.setContentTitle("Downloading")
.setTicker("Rits Download")
.setContentText(name)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setContentIntent(pendingIntent)
.setOngoing(true)
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
.setProgress(100, progress, false)
.build();
startForeground(DownloadActions.NOTIFICATION_ID.FOREGROUND_SERVICE,
notification);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("progress", progress);
intent.setAction("download");
intent.putExtra("tasktoken",taskToken);
ReportStructure structure = downloadManagerPro.singleDownloadStatus(taskToken);
String name =structure.name;
intent.putExtra("name",name);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
}
#Override
public void OnDownloadFinished(long taskId) {
}
#Override
public void OnDownloadRebuildStart(long taskId) {
}
#Override
public void OnDownloadRebuildFinished(long taskId) {
}
public void pauseDownload(int taskToken){
ReportStructure reportStructure = downloadManagerPro.singleDownloadStatus(taskToken);
if(reportStructure.state == TaskStates.DOWNLOADING){
downloadManagerPro.pauseDownload(taskToken);
} else if(reportStructure.state == TaskStates.PAUSED){
try {
downloadManagerPro.startDownload(taskToken);
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void OnDownloadCompleted(long taskId) {
Log.d(LOG_TAG, "Download Complete");
/* MainActivity.instance.pausebtn.post(new Runnable() {
#Override
public void run() {
MainActivity.instance.pausebtn.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_done));
}
});*/
notification = new NotificationCompat.Builder(this)
.setContentTitle("Download Complete")
.setTicker("Rits Download")
.setContentText(name)
.setSmallIcon(R.drawable.ic_action_done)
.setContentIntent(pendingIntent)
.setOngoing(true)
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
.build();
startForeground(DownloadActions.NOTIFICATION_ID.FOREGROUND_SERVICE,
notification);
}
#Override
public void connectionLost(long taskId) {
}
#Override
public void onTaskRemoved(Intent rootIntent) {
/* Intent restartService = new Intent(getApplicationContext(),this.getClass());
restartService.setPackage(getPackageName());
PendingIntent restartPendingIntent =PendingIntent.getService(getApplicationContext(), 1,restartService, PendingIntent.FLAG_ONE_SHOT);
AlarmManager myAlarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
myAlarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartPendingIntent);*/
}
public class LocalBinder extends Binder{
public MyDownloadService getService(){
return MyDownloadService.this;
}
}
}
I don't know how you want to save the state but I do know that an activity has a onStop() method that you can override.
The onStop() method runs when the app is killed so I would imagine you would want to do your "saving" in this method.
As for saving something I believe you would want to use the SharedPreferences class. You can find more information about that here.
You can find the lifecycle of an activity here
hope this helps
Save the progress on SharedPreferences onDestroy() and retrieve it from there when activity is created.
Cache:
#Override
protected void onDestroy() {
super.onDestroy();
SharedPreferences sharedPreferences = getSharedPreferences("space_name", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putInt("progress", progress).apply();
}
And retrieve:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SharedPreferences sharedPreferences = getSharedPreferences("space_name", MODE_PRIVATE);
progress = sharedPreferences.getInt("progress", 0); // 0 default value in case is empty
}
I m working pedometer app and i have one service class which extends service class .
public class StepService extends Service {
private static final String TAG = "name.bagi.levente.pedometer.StepService";
private SharedPreferences mSettings;
private PedometerSettings mPedometerSettings;
private SharedPreferences mState;
private SharedPreferences.Editor mStateEditor;
private Utils mUtils;
private SensorManager mSensorManager;
private Sensor mSensor;
private StepDetector mStepDetector;
// private StepBuzzer mStepBuzzer; // used for debugging
private StepDisplayer mStepDisplayer;
private PaceNotifier mPaceNotifier;
private DistanceNotifier mDistanceNotifier;
private SpeedNotifier mSpeedNotifier;
private CaloriesNotifier mCaloriesNotifier;
private SpeakingTimer mSpeakingTimer;
private PowerManager.WakeLock wakeLock;
private NotificationManager mNM;
private int mSteps;
private int mPace;
private float mDistance;
private float mSpeed;
private float mCalories;
/**
* Class for clients to access. Because we know this service always
* runs in the same process as its clients, we don't need to deal with
* IPC.
*/
public class StepBinder extends Binder {
StepService getService() {
return StepService.this;
}
}
#Override
public void onCreate() {
// Log.i(TAG, "[SERVICE] onCreate");
super.onCreate();
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
showNotification();
// Load settings
mSettings = PreferenceManager.getDefaultSharedPreferences(this);
mPedometerSettings = new PedometerSettings(mSettings);
mState = getSharedPreferences("state", 0);
mUtils = Utils.getInstance();
mUtils.setService(this);
mUtils.initTTS();
acquireWakeLock();
// Start detecting
mStepDetector = new StepDetector();
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
registerDetector();
// Register our receiver for the ACTION_SCREEN_OFF action. This will make our receiver
// code be called whenever the phone enters standby mode.
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
registerReceiver(mReceiver, filter);
mStepDisplayer = new StepDisplayer(mPedometerSettings, mUtils);
mStepDisplayer.setSteps(mSteps = mState.getInt("steps", 0));
mStepDisplayer.addListener(mStepListener);
mStepDetector.addStepListener(mStepDisplayer);
mPaceNotifier = new PaceNotifier(mPedometerSettings, mUtils);
mPaceNotifier.setPace(mPace = mState.getInt("pace", 0));
mPaceNotifier.addListener(mPaceListener);
mStepDetector.addStepListener(mPaceNotifier);
mDistanceNotifier = new DistanceNotifier(mDistanceListener, mPedometerSettings, mUtils);
mDistanceNotifier.setDistance(mDistance = mState.getFloat("distance", 0));
mStepDetector.addStepListener(mDistanceNotifier);
mSpeedNotifier = new SpeedNotifier(mSpeedListener, mPedometerSettings, mUtils);
mSpeedNotifier.setSpeed(mSpeed = mState.getFloat("speed", 0));
mPaceNotifier.addListener(mSpeedNotifier);
mCaloriesNotifier = new CaloriesNotifier(mCaloriesListener, mPedometerSettings, mUtils);
mCaloriesNotifier.setCalories(mCalories = mState.getFloat("calories", 0));
mStepDetector.addStepListener(mCaloriesNotifier);
mSpeakingTimer = new SpeakingTimer(mPedometerSettings, mUtils);
mSpeakingTimer.addListener(mStepDisplayer);
mSpeakingTimer.addListener(mPaceNotifier);
mSpeakingTimer.addListener(mDistanceNotifier);
mSpeakingTimer.addListener(mSpeedNotifier);
mSpeakingTimer.addListener(mCaloriesNotifier);
mStepDetector.addStepListener(mSpeakingTimer);
// Used when debugging:
// mStepBuzzer = new StepBuzzer(this);
// mStepDetector.addStepListener(mStepBuzzer);
// Start voice
reloadSettings();
// Tell the user we started.
Toast.makeText(this, getText(R.string.started), Toast.LENGTH_SHORT).show();
}
#Override
public void onStart(Intent intent, int startId) {
// Log.i(TAG, "[SERVICE] onStart");
super.onStart(intent, startId);
}
#Override
public void onDestroy() {
// Log.i(TAG, "[SERVICE] onDestroy");
mUtils.shutdownTTS();
// Unregister our receiver.
unregisterReceiver(mReceiver);
unregisterDetector();
mStateEditor = mState.edit();
mStateEditor.putInt("steps", mSteps);
mStateEditor.putInt("pace", mPace);
mStateEditor.putFloat("distance", mDistance);
mStateEditor.putFloat("speed", mSpeed);
mStateEditor.putFloat("calories", mCalories);
mStateEditor.commit();
mNM.cancel(R.string.app_name);
wakeLock.release();
super.onDestroy();
// Stop detecting
mSensorManager.unregisterListener(mStepDetector);
// Tell the user we stopped.
Toast.makeText(this, getText(R.string.stopped), Toast.LENGTH_SHORT).show();
}
private void registerDetector() {
mSensor = mSensorManager.getDefaultSensor(
Sensor.TYPE_ACCELEROMETER /*|
Sensor.TYPE_MAGNETIC_FIELD |
Sensor.TYPE_ORIENTATION*/);
mSensorManager.registerListener(mStepDetector,
mSensor,
SensorManager.SENSOR_DELAY_FASTEST);
}
private void unregisterDetector() {
mSensorManager.unregisterListener(mStepDetector);
}
#Override
public IBinder onBind(Intent intent) {
// Log.i(TAG, "[SERVICE] onBind");
return mBinder;
}
/**
* Receives messages from activity.
*/
private final IBinder mBinder = new StepBinder();
public interface ICallback {
public void stepsChanged(int value);
public void paceChanged(int value);
public void distanceChanged(float value);
public void speedChanged(float value);
public void caloriesChanged(float value);
}
private ICallback mCallback;
public void registerCallback(ICallback cb) {
mCallback = cb;
//mStepDisplayer.passValue();
//mPaceListener.passValue();
}
private int mDesiredPace;
private float mDesiredSpeed;
/**
* Called by activity to pass the desired pace value,
* whenever it is modified by the user.
* #param desiredPace
*/
public void setDesiredPace(int desiredPace) {
mDesiredPace = desiredPace;
if (mPaceNotifier != null) {
mPaceNotifier.setDesiredPace(mDesiredPace);
}
}
/**
* Called by activity to pass the desired speed value,
* whenever it is modified by the user.
* #param desiredSpeed
*/
public void setDesiredSpeed(float desiredSpeed) {
mDesiredSpeed = desiredSpeed;
if (mSpeedNotifier != null) {
mSpeedNotifier.setDesiredSpeed(mDesiredSpeed);
}
}
public void reloadSettings() {
mSettings = PreferenceManager.getDefaultSharedPreferences(this);
if (mStepDetector != null) {
mStepDetector.setSensitivity(
Float.valueOf(mSettings.getString("sensitivity", "10"))
);
}
if (mStepDisplayer != null) mStepDisplayer.reloadSettings();
if (mPaceNotifier != null) mPaceNotifier.reloadSettings();
if (mDistanceNotifier != null) mDistanceNotifier.reloadSettings();
if (mSpeedNotifier != null) mSpeedNotifier.reloadSettings();
if (mCaloriesNotifier != null) mCaloriesNotifier.reloadSettings();
if (mSpeakingTimer != null) mSpeakingTimer.reloadSettings();
}
public void resetValues() {
mStepDisplayer.setSteps(0);
mPaceNotifier.setPace(0);
mDistanceNotifier.setDistance(0);
mSpeedNotifier.setSpeed(0);
mCaloriesNotifier.setCalories(0);
}
/**
* Forwards pace values from PaceNotifier to the activity.
*/
private StepDisplayer.Listener mStepListener = new StepDisplayer.Listener() {
public void stepsChanged(int value) {
mSteps = value;
passValue();
}
public void passValue() {
if (mCallback != null) {
mCallback.stepsChanged(mSteps);
}
}
};
/**
* Forwards pace values from PaceNotifier to the activity.
*/
private PaceNotifier.Listener mPaceListener = new PaceNotifier.Listener() {
public void paceChanged(int value) {
mPace = value;
passValue();
}
public void passValue() {
if (mCallback != null) {
mCallback.paceChanged(mPace);
}
}
};
/**
* Forwards distance values from DistanceNotifier to the activity.
*/
private DistanceNotifier.Listener mDistanceListener = new DistanceNotifier.Listener() {
public void valueChanged(float value) {
mDistance = value;
passValue();
}
public void passValue() {
if (mCallback != null) {
mCallback.distanceChanged(mDistance);
}
}
};
/**
* Forwards speed values from SpeedNotifier to the activity.
*/
private SpeedNotifier.Listener mSpeedListener = new SpeedNotifier.Listener() {
public void valueChanged(float value) {
mSpeed = value;
passValue();
}
public void passValue() {
if (mCallback != null) {
mCallback.speedChanged(mSpeed);
}
}
};
/**
* Forwards calories values from CaloriesNotifier to the activity.
*/
private CaloriesNotifier.Listener mCaloriesListener = new CaloriesNotifier.Listener() {
public void valueChanged(float value) {
mCalories = value;
passValue();
}
public void passValue() {
if (mCallback != null) {
mCallback.caloriesChanged(mCalories);
}
}
};
/**
* Show a notification while this service is running.
*/
private void showNotification() {
CharSequence text = getText(R.string.app_name);
Notification notification = new Notification(R.drawable.ic_notification, null,
System.currentTimeMillis());
notification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
Intent pedometerIntent = new Intent();
pedometerIntent.setComponent(new ComponentName(this, Pedometer.class));
pedometerIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
pedometerIntent, 0);
/* notification.setLatestEventInfo(this, text,
getText(R.string.notification_subtitle), contentIntent);
mNM.notify(R.string.app_name, notification);*/
}
// BroadcastReceiver for handling ACTION_SCREEN_OFF.
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Check action just to be on the safe side.
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
// Unregisters the listener and registers it again.
StepService.this.unregisterDetector();
StepService.this.registerDetector();
if (mPedometerSettings.wakeAggressively()) {
wakeLock.release();
acquireWakeLock();
}
}
}
};
private void acquireWakeLock() {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
int wakeFlags;
if (mPedometerSettings.wakeAggressively()) {
wakeFlags = PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP;
}
else if (mPedometerSettings.keepScreenOn()) {
wakeFlags = PowerManager.SCREEN_DIM_WAKE_LOCK;
}
else {
wakeFlags = PowerManager.PARTIAL_WAKE_LOCK;
}
wakeLock = pm.newWakeLock(wakeFlags, TAG);
wakeLock.acquire();
}
}
ho to make this service live even when app killed also I have to count footstep even when app killed.
I m calling service like this.
startService(new Intent(Pedometer.this,
StepService.class));
#Override
public void onTaskRemoved(Intent rootIntent)
{
super.onTaskRemoved(rootIntent);
startStepService();
}
private void startStepService()
{
startService(new Intent(this,StepService.class));
}
I created an Android app that can downloading file.
I used Groundy library to support the service. And I created the NotiThread from this (but I edited it for support my requirement)..
Here is my Activity code ...
public class PacksActivity extends Activity {
private Context context;
private RelativeLayout download;
private ProgressBar progressBar;
private NotiThread notification;
private String url;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.packs);
context = this;
url = getIntent().getStringExtra("URL");
progressBar = (ProgressBar)findViewById(R.id.progress);
progressBar.setIndeterminate(false);
progressBar.setMax(100);
download = (RelativeLayout) findViewById(R.id.label_circle);
download.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
File dest = new File(context.getFilesDir(), new File(url).getName());
if(!dest.exists()){
progressBar.setVisibility(View.VISIBLE);
notification = new NotiThread(context);
notification.run();
Bundle extras = new Bundler().add(DownloadTemplate.PARAM_URL, url).build();
Groundy.create(context, DownloadTemplate.class)
.receiver(mReceiver)
.params(extras)
.queue();
}
}
});
}
private ResultReceiver mReceiver = new ResultReceiver(new Handler()) {
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
switch (resultCode) {
case Groundy.STATUS_PROGRESS:
int progress = resultData.getInt(Groundy.KEY_PROGRESS);
progressBar.setProgress(progress);
if((progress % 10) == 0)notification.progressUpdate(progress);
break;
case Groundy.STATUS_FINISHED:
Toast.makeText(context, "Success Download file", Toast.LENGTH_LONG);
progressBar.setVisibility(View.GONE);
notification.completed();
break;
case Groundy.STATUS_ERROR:
Toast.makeText(context, resultData.getString(Groundy.KEY_ERROR), Toast.LENGTH_LONG).show();
progressBar.setVisibility(View.GONE);
notification.completed();
break;
}
}
};
}
This is my NotiThread class
public class NotiThread extends Thread {
private NotificationManager mNoti = null;
private final Context mContext;
private final int mUnique;
private Notification noti;
public NotiThread(Context context) {
super("NotiThread");
mContext = context;
mNoti = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mUnique = 1;
}
#Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
noti = new Notification(R.drawable.icon1024, "title", System.currentTimeMillis());
noti.flags |= Notification.FLAG_ONGOING_EVENT;
RemoteViews contentView = new RemoteViews(mContext.getPackageName(), R.layout.custom_notification);
contentView.setTextViewText(R.id.noti_title, "title");
contentView.setProgressBar(R.id.noti_progress, 100, 0, false);
contentView.setTextViewText(R.id.noti_text, "0%");
noti.contentView = contentView;
noti.contentIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, mContext.getClass()), PendingIntent.FLAG_ONE_SHOT);
mNoti.notify(mUnique, noti);
}
public void progressUpdate(int percentageComplete) {
noti.contentView.setProgressBar(R.id.noti_progress, 100, percentageComplete, false);
noti.contentView.setTextViewText(R.id.noti_text, percentageComplete + "%");
mNoti.notify(mUnique, noti);
}
public void completed() {
mNoti.cancel(mUnique);
}
}
And this is GroundyTask class
public class DownloadTemplate extends GroundyTask {
public static final String PARAM_URL = "com.app.service.PARAM_URL";
#Override
protected boolean doInBackground() {
try {
String url = getParameters().getString(PARAM_URL);
File dest = new File(getContext().getFilesDir(), new File(url).getName());
DownloadUtils.downloadFile(getContext(), url, dest, DownloadUtils.getDownloadListenerForTask(this));
return true;
} catch (Exception e) {
return false;
}
}
}
I want to ask, how to resume activity to update the ProgressBar from the current running service when enter to the activity and from notification?
You can check if the service is still running when coming back to your activity. How I do it is I use a singleton Network class. Here I define the callback object that is used in the Groundy create function. This singleton also keeps track of the state of the task (running, or not running when cancelled, finished or failed). When the activity is created again, this singleton class still exists so the progress callback is still being fired. The only thing you need to make sure is to re-register your listeners from the new activity to the existing singleton. Here's a simple example..
Singleton class:
public static SingletonClass getInstance(Object o) {
if (instance == null) {
instance = new SingletonClass();
}
if (o instanceof OnProgressListener) {
progressListener = (OnProgressListener) o;
}
...
return instance;
}
Callback object:
public Object callbackObject = new Object() {
#OnProgress(YourGroundyTask.class)
public void onProgress(#Param(Groundy.PROGRESS) int progress) {
if (progressListener != null){
progressListener.onDownloadProgress(progress);
}
}
...