Resume ProgressBar in Activity from current running Service - android

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);
}
}
...

Related

MediaSession not getting any callbacks from MediaStyle Notification

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.

Service still running despite the App is killed

I am trying to understand the difference bwtettn STICKY and NOT_STICKY intent of a service in onStartCommand().
I created the MainActivity and the Service shown in the code below. I expected when I press the button mBtnStopApp, the onDestroy() wil be called and then
I should receive this log ** Log.w(TAG, SubTag.bullet("++++++++ SERVICE IS NOT RUNNING ++++++++")); **
because onStartCommand() returns NOT_STICKY intnet which means, when the App is killed, the service should also be stopped
but that never happens.
what happens is, when i press the button mBtnStopApp, the App is finished but i receive the following log:
** Log.w(TAG, SubTag.bullet("++++++++ SERVICE IS RUNNING ++++++++")); **
please let me know why the service is still running despite it returns START_NOT_STICKY and the App was killed
MainActivity
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private TextView mTv = null;
private Button mBtnStartStickyService = null;
private Button mBtnStartNonStickyService = null;
private Button mBtnStopApp = null;
private Button mBtnStopNonStickyService = null;
private Button mBtnStopStickyService = null;
private ServicesUtils mServiceUtils = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.w(TAG, SubTag.bullet("#onCreate"));
this.mServiceUtils = new ServicesUtils(this);
this.mBtnStartNonStickyService = (Button) findViewById(R.id.btnNonStickyService);
this.mBtnStartStickyService = (Button) findViewById(R.id.btnStickyService);
this.mBtnStopApp = (Button) findViewById(R.id.btnStopApp);
this.mBtnStopNonStickyService = (Button) findViewById(R.id.btnStopNonStickyService);
this.mBtnStopStickyService = (Button) findViewById(R.id.btnStopStickyService);
this.mTv = (TextView) findViewById(R.id.tv);
this.mBtnStartNonStickyService.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ComponentName componentName = startService(setRunningStateForService(NonStickyService.class, "start"));
Log.i(TAG, "#mBtnStartNonStickyService: componentName: " + componentName);
}
});
this.mBtnStopNonStickyService.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ComponentName componentName = startService(setRunningStateForService(NonStickyService.class, "stop"));
Log.i(TAG, "#mBtnStopNonStickyService: componentName: " + componentName);
}
});
this.mBtnStopApp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
}
private Intent setRunningStateForService(Class<NonStickyService> serviceClass, String state) {
Log.w(TAG, SubTag.bullet("#setRunningStateForService"));
Intent intent = new Intent(this, serviceClass);
intent.putExtra("running", state);
return intent;
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.w(TAG, SubTag.bullet("#onDestroy"));
if (this.isNonStickyServiceRunning()) {
Log.w(TAG, SubTag.bullet("++++++++ SERVICE IS RUNNING ++++++++"));
} else {
Log.w(TAG, SubTag.bullet("++++++++ SERVICE IS NOT RUNNING ++++++++"));
}
}
private boolean isNonStickyServiceRunning() {
ActivityManager manager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (NonStickyService.class.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
}
code:
public class NonStickyService extends Service {
private static final String TAG = NonStickyService.class.getSimpleName();
private HeavyWorkRunnable mHeavyWorkRunnable = null;
private String running = null;
#Override
public void onCreate() {
super.onCreate();
Log.w(TAG, SubTag.bullet("#onCreate"));
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.w(TAG, SubTag.bullet("#onStartCommand"));
running = intent.getStringExtra("running");
if (running != null && !running.isEmpty() && running.equals("start")) {
this.StartHeavyWorkThread();
}
return Service.START_NOT_STICKY;
}
private void StartHeavyWorkThread() {
Log.w(TAG, SubTag.bullet("#StartHeavyWorkThread"));
this.mHeavyWorkRunnable = new HeavyWorkRunnable();
new Thread(this.mHeavyWorkRunnable).start();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
Log.w(TAG, SubTag.bullet("#onBind"));
return null;
}
#Override
public void onDestroy() {
Log.w(TAG, SubTag.bullet("#onDestroy"));
super.onDestroy();
this.running = "stop";
}
class HeavyWorkRunnable implements Runnable {
int counter = 0;
private Bundle b = null;
public void run() {
Log.w(TAG,SubTag.bullet("#HeavyWorkRunnable"));
while(running.equals("start")) {
b = new Bundle();
b.putInt("what", ++counter);
Log.w(TAG,SubTag.bullet("#HeavyWorkRunnable: counter: " + counter));
Log.w(TAG,SubTag.bullet("#HeavyWorkRunnable: b.getInt('what'): " + b.getInt("what")));
Message msg = handler.obtainMessage();
msg.setData(b);
handler.sendMessage(msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.i(TAG,SubTag.bullet("NonStickyService will Stop"));
stopSelf();
}
}
android.os.Handler handler = new android.os.Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int what = msg.getData().getInt("what");
Log.w(TAG,SubTag.bullet("#handler: what: " + what));
}
};
}

How to save state of a progressbar when app is killed?

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
}

How can I call a Method of an activity from background service?

I want to update my ChatMessageAdapter by received new data from background service so that I want to call UpdateAdapter method from background to update adapter.
here is my ServiceClass:
public class MyService extends Service{
private String loginUserInfoId;
SessionManager session;
DatabaseHelper db;
MessageListActivity mLA;
long totalSize = 0;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
db = new DatabaseHelper(getApplicationContext());
mLA = new MessageListActivity();
session = new SessionManager(getApplicationContext());
session.checkLogin();
HashMap<String, String> user = session.getUserDetails();
loginUserInfoId = user.get(SessionManager.KEY_USER_INFO_ID);
if(isInternetOn()) {
new syncMessageFromServer().execute();
new SyncPendingMessageToServer();
}
return super.onStartCommand(intent, flags, startId);
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
private class syncMessageFromServer extends AsyncTask<Void, Integer, String> {
#Override
protected void onPreExecute() {
// setting progress bar to zero
//progressBar.setProgress(0);
super.onPreExecute();
}
#Override
protected void onProgressUpdate(Integer... progress) {
}
#Override
protected String doInBackground(Void... params) {
return uploadFile();
}
#SuppressWarnings("deprecation")
private String uploadFile() {
String str = "";
HttpResponse response;
HttpClient myClient = new DefaultHttpClient();
HttpPost myConnection = new HttpPost("http://192.168.1.2/AndroidApp/GetAllMessage/" + loginUserInfoId);
try {
response = myClient.execute(myConnection);
str = EntityUtils.toString(response.getEntity(), "UTF-8");
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
JSONArray jArray = new JSONArray(str);
for (int i = 0; i <= jArray.length() - 1; i++) {
JSONObject row = jArray.getJSONObject(i);
ChatMessage cm = new ChatMessage();
String offlineFileURL = "";
/******* Firstly take data in model object ******/
cm.setOriginalMsgThreadId(row.getString("MessageThreadId"));
cm.setSenderUserInfoId(row.getString("SenderUserId")); cm.setReceiverUserInfoId(row.getString("MultipleReceiversId"));
cm.setMessageStatus("SENT");
cm.setIsPending(0);
cm.setMessageText(row.getString("MessageText"));
cm.setMediaURL(offlineFileURL);
cm.setThumbImage(offlineFileURL);
cm.setMediaMIMEType("");
cm.setMediaSize(0);
cm.setMediaName("");
cm.setLatitude("");
cm.setLongitude("");
cm.setSendTimeStamp(row.getString("SendTime"));
cm.setReceiveTimeStamp(row.getString("ReadTime"));
mLA.UpdateAdapter(ChatMessage cm);
long messageThreadId = db.SendMessage(cm);
}
} catch (JSONException e) {
e.printStackTrace();
}
return str;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if(isInternetOn()) {
new syncMessageFromServer().execute();
}
}
}
}
and this is my MessageActivityList Class:
public class MessageListActivity extends ActionBarActivity {
private String receiverUserInfoId;
private String loginUserInfoId;
private String orgMsgThreadId;
private String userName;
private String uploadedFileURL = "";
DatabaseHelper db;
SessionManager session;
private ChatMessageAdapter chatMessageAdapter;
private EditText chatText;
private ImageButton buttonSend;
private ListView listView;
private static final String TAG = MessageListActivity.class.getSimpleName();
// Camera activity request codes
private static final int CAMERA_CAPTURE_IMAGE_REQUEST_CODE = 100;
private static final int CAMERA_CAPTURE_VIDEO_REQUEST_CODE = 200;
public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;
private Uri fileUri; // file url to store image/video
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_message_list);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
.penaltyLog()
.build());
db = new DatabaseHelper(getApplicationContext());
session = new SessionManager(getApplicationContext());
session.checkLogin();
HashMap<String, String> user = session.getUserDetails();
loginUserInfoId = user.get(SessionManager.KEY_USER_INFO_ID);
Intent intent=getIntent();
Bundle extra = intent.getExtras();
receiverUserInfoId=extra.getString("UserInfoId");
orgMsgThreadId = extra.getString("OrgMessageThreadId");
userName=extra.getString("UserName");
setTitle(userName);
listView = (ListView) findViewById(R.id.messageList);
chatMessageAdapter = new ChatMessageAdapter(getApplicationContext(), R.layout.activity_single_message);
listView.setAdapter(chatMessageAdapter);
buttonSend = (ImageButton) findViewById(R.id.buttonSend);
chatText = (EditText) findViewById(R.id.chatText);
chatText.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
if(chatText.getText().toString().trim().length() > 0){
sendChatMessage();
}
}
return false;
}
});
buttonSend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
sendChatMessage();
}
});
listView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
// listView.setAdapter(chatMessageAdapter);
//to scroll the list view to bottom on data change
chatMessageAdapter.registerDataSetObserver(new DataSetObserver() {
#Override
public void onChanged() {
super.onChanged();
listView.setSelection(chatMessageAdapter.getCount() - 1);
}
});
setListData();
}
public void UpdateAdapter(ChatMessage cm) {
chatMessageAdapter.add(cm);
}
}
What to do for calling this UpdateAdapter method to update my ChatMessage received by the server?
You should be using a LocalBroadcastReceiver.
Register for receiving the updates in onResume and unregister in onPause.
U can use Broadcast receiver for updating the UI from service.
Register the broadcast receiver in onCreate() of ur Activity:
private UpdateReceiver updateReceiver;
if (UpdateReceiver == null)
{
updateReceiver = new UpdateReceiver();
IntentFilter intentFilter = new
IntentFilter("REFRESH_DATA");
registerReceiver(updateReceiver, intentFilter);
}
Unregister in onDestroy() of ur Activity
if (updateReceiver != null) unregisterReceiver(updateReceiver);
Define the Broadcast receiver in ur Activity :
private class UpdateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("REFRESH_DATA")) {
//update adapter from here
}
}
}
In ur service in which situation u want to update the adapter, there call sendBroadcast. Like:
Intent intent=new Intent("REFRESH_DATA");
//u can pass the data through putExtras
sendBroadcast(intent);
use Broadcast receiver for updating the UI from service and register the broadcast receiver in onCreate() of ur MainActivity where you want to recieve
code will like below
private UpdateReceiver updateReceiver;
if (UpdateReceiver == null)
{
updateReceiver = new UpdateReceiver();
IntentFilter intentFilter = new
IntentFilter("REFRESH_DATA");
registerReceiver(updateReceiver, intentFilter);
}
and create class to check update is recieved or not
private class UpdateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("REFRESH_DATA")) {
//update adapter from here
}
}
}
register a BroadcastReceiver inside your activity. call that method in onReceive method of this receiver. and from your Service sendBroadcast to this receiver, remember to add the required data to your intent and fetch data from onReceive method intent.

Cancel AsyncTask inside ListArrayAdapter and dismiss notification

The aim of the application is to offer a simple way to download and extract a bunch of archives (~200mo), to delete it, and to open it. The download display a notification which display the progress.
I'm using a ListView to display every archive that can be downloaded. Each array contain an image, some text, and two buttons (for reading/deleting, downloading, or cancel). The data are retrieved from a json.
I've got some issues while I try to cancel the AsyncTask which display the notification and download the file. When I call the async.cancel(true);, the notification and the download are still running.
Maybe am I calling the asyncTask from the wrong place, but I don't know how to fix this.
My adapter :
public class ListArrayAdapter extends ArrayAdapter<String> {
private final Context context;
public Drawable d;
private ArrayList<HashMap<String, String>> list;
public boolean finishDownload = false;
JSONArray jsonArray;
// Constructor
public ListArrayAdapter(Context context, List<String> values,
ArrayList<HashMap<String, String>> list) {
super(context, R.layout.activity_list_array_adapter, values);
this.context = context;
this.list = list;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.activity_list_array_adapter,
parent, false);
// Déclaration
TextView date = (TextView) rowView.findViewById(R.id.date);
TextView title = (TextView) rowView.findViewById(R.id.title);
TextView description = (TextView) rowView.findViewById(R.id.description);
ImageView imageView = (ImageView) rowView.findViewById(R.id.cover);
ProgressBar pd = (ProgressBar) rowView.findViewById(R.id.progress);
final Button button1 = (Button) rowView.findViewById(R.id.button1);
final Button button2 = (Button) rowView.findViewById(R.id.button2);
// Fill the array with the json file
title.setText(list.get(position).get("Title"));
date.setText(list.get(position).get("Date"));
description.setText(list.get(position).get("Description"));
new ParseImage(position, imageView, pd).execute();
final File file = new File(Environment.getExternalStorageDirectory()
.getPath() + "/Magazine/" + list.get(position).get("Name") + ".zip");
//If the file exist, let the user open it
if (file.exists()) {
button1.setText("Lire");
button2.setVisibility(View.VISIBLE);
button2.setText("Supprimer");
}
// else, let the user download it
else {
button1.setText("Télécharger");
button2.setVisibility(View.GONE);
}
final DownloadTask async = new DownloadTask(context, position, list);
button1.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
if (button1.getText() == "Télécharger") {
button1.setEnabled(false);
button2.setVisibility(View.VISIBLE);
button2.setText("Annuler");
finishDownload = false;
async.execute(0);
} else if (button1.getText() == "Lire") {
Intent i1 = new Intent(context, WebActivity.class);
context.startActivity(i1);
}
}
});
button2.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
if (button2.getText() == "Annuler") {
async.cancel(true);
button1.setEnabled(true);
button2.setVisibility(View.GONE);
button1.setText("Télécharger");
} else if (button2.getText() == "Supprimer") {
button1.setEnabled(true);
button2.setVisibility(View.GONE);
button1.setText("Télécharger");
if (file.exists()) {
file.delete();
}
}
}
});
return rowView;
}
public class DownloadTask extends AsyncTask<Integer, Integer, Void> {
private NotificationHelper mNotificationHelper;
public int position;
public ArrayList<HashMap<String, String>> list;
public DownloadTask(Context context, int position,
ArrayList<HashMap<String, String>> list) {
mNotificationHelper = new NotificationHelper(context);
this.position = position;
this.list = list;
}
#Override
protected void onPreExecute() {
mNotificationHelper.createNotification();
}
#Override
protected Void doInBackground(Integer... integers) {
int count = 0;
try {
Thread.sleep(1);
URL url = new URL(list.get(position).get("Content"));
URLConnection conection = url.openConnection();
conection.connect();
int lenghtOfFile = conection.getContentLength();
InputStream input = new BufferedInputStream(url.openStream(),
8192);
OutputStream output = new FileOutputStream(Environment
.getExternalStorageDirectory().getPath()
+ "/Magazine/"
+ list.get(position).get("Name")
+ ".zip");
byte data[] = new byte[1024];
long total = 0;
int progress_temp = 0;
while ((count=input.read(data)) != -1)
{
total += count;
progress_temp = (int) total*100/lenghtOfFile;
output.write(data, 0, count);
}
publishProgress(progress_temp);
output.flush();
output.close();
input.close();
} catch (Exception e) {
Log.e("Error: ", e.getMessage());
}
return null;
}
#Override
protected void onProgressUpdate(Integer... progress) {
mNotificationHelper.progressUpdate(progress[0]);
}
#Override
protected void onCancelled() {
mNotificationHelper.completed();
super.onCancelled();
}
#Override
protected void onPostExecute(Void result) {
mNotificationHelper.completed();
finishDownload = true;
}
}
}
The notificationHelper class which manage the notification :
public class NotificationHelper {
private Context mContext;
private int NOTIFICATION_ID = 1;
private Notification mNotification;
private NotificationManager mNotificationManager;
private PendingIntent mContentIntent;
private CharSequence mContentTitle;
public NotificationHelper(Context context) {
mContext = context;
}
#SuppressWarnings("deprecation")
public void createNotification() {
mNotificationManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);
int icon = android.R.drawable.stat_sys_download;
CharSequence tickerText = mContext.getString(R.string.download_ticker);
long when = System.currentTimeMillis();
mNotification = new Notification(icon, tickerText, when);
mContentTitle = mContext.getString(R.string.content_title);
CharSequence contentText = "0% téléchargé";
Intent notificationIntent = new Intent(mContext, MainActivity.class);
mContentIntent = PendingIntent.getActivity(mContext, 0,
notificationIntent, 0);
mNotification.setLatestEventInfo(mContext, mContentTitle, contentText,
mContentIntent);
mNotification.flags = Notification.FLAG_ONGOING_EVENT;
mNotificationManager.notify(NOTIFICATION_ID, mNotification);
}
#SuppressWarnings("deprecation")
public void progressUpdate(int percentageComplete) {
CharSequence contentText = percentageComplete + "% téléchargé";
mNotification.setLatestEventInfo(mContext, mContentTitle, contentText,
mContentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mNotification);
}
public void completed() {
mNotificationManager.cancel(NOTIFICATION_ID);
}
}
The cancel() method on AsyncTasks (confusingly) doesn't actually stop the Thread from running, but rather sets a flag on the Task. You can find some discussion about that here. It seems that currently, if you want your Thread to actually stop mid-execution, you'll need to manually check that flag in your looping code using the isCancelled() method. For example, in your while loop in doInBackground:
while ((count=input.read(data)) != -1)
{
total += count;
progress_temp = (int) total*100/lenghtOfFile;
output.write(data, 0, count);
if(isCancelled())
{
//Code here that may...
// break out of the loop,
// stop the thread,
// close the notification,
// perform any cleanup, etc...
}
}
You can find another example of this in the AsyncTask docs under the "Usage" header. Hope this helps!

Categories

Resources