Calling finish() from SurfaceView does not destroy Activity - java

I want to finish() the activity VideoPlayer from the class RenderView. However calling finish() from RenderView does not call onDestroy(). The Activity is not destroyed and does not return back to the previous Main Activity.
public class VideoPlayer extends Activity {
#Override
protected void onPause(){
super.onPause();
renderView.pause();
}
#Override
protected void onStop() {
super.onStop();
}
#Override
public void onDestroy() {
super.onDestroy();
naClose2();
}
}
mFinished = true but it returns back to the method parseServerInfo() where finish() was called and continues executing the rest of the code.
EDIT
public class RenderView extends SurfaceView implements SurfaceHolder.Callback {
private Context mContext;
private Runnable prDisplayVideoTask = new Runnable() {
public void run() {
if(zoomState.isPlaying()==false){
if(zoomState.getFlag()==FlagType.PAUSE){
zoomState.setFlag(FlagType.NONE);
naPause();
}
} else {
naStart();
}
prVideoDisplayHandler.postDelayed(this, prDelay);
}
};
public RenderView(...) {
super(_context);
this.mContext = _context;
init(address, windowWidth, windowHeight, videoWidth, videoHeight,
server_ip, server_port);
SurfaceHolder holder = getHolder();
holder.setFormat(PixelFormat.RGBA_8888);
holder.addCallback(this);
}
#SuppressLint("NewApi")
public void init(...) {
parseServerInfo(receivedData);
prVideoDisplayHandler.removeCallbacks(prDisplayVideoTask);
prVideoDisplayHandler.postDelayed(prDisplayVideoTask, prDelay);
}
public void pause(){
naPause();
prVideoDisplayHandler.removeCallbacks(prDisplayVideoTask);
}
public void resume(){
prVideoDisplayHandler.postDelayed(prDisplayVideoTask, prDelay);
}
public void parseServerInfo(String data) {
if (numCameras == 0) {
Toast.makeText(mContext, "No stream detected!", Toast.LENGTH_LONG).show();
// Finish is called here
VideoPlayer videoplayer = (VideoPlayer) mContext;
videoplayer.finish();
return;
}
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (mCreated == true) {
surfaceDestroyed(holder);
}
Surface surface = holder.getSurface();
render(surface);
mCreated = true;
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
mCreated = false;
}
}
Hope someone could help point out what I am doing wrong.

This is why it is continuing on and freezing up (as mentioned in the comments to your question). It should. That is how the java language works. Here,
#SuppressLint("NewApi")
public void init(...) {
parseServerInfo(receivedData);
...
}
You call parseServerInfo(receivedData); which does
public void parseServerInfo(String data) {
if (numCameras == 0) {
Toast.makeText(mContext, "No stream detected!", Toast.LENGTH_LONG).show();
// Finish is called here
VideoPlayer videoplayer = (VideoPlayer)getContext();
videoplayer.finish();
return;
}
}
So you are seeing the Toast, getting finish called, and saying good to go. But you aren't looking back at where you came from. With comments, what your init method should say is
#SuppressLint("NewApi")
public void init(...) {
// make a call to check that the number of cameras is not 0
parseServerInfo(receivedData);
// AND CONTINUE NO MATTER WHAT...
prVideoDisplayHandler.removeCallbacks(prDisplayVideoTask);
prVideoDisplayHandler.postDelayed(prDisplayVideoTask, prDelay);
}
What you need is
public boolean parseServerInfo(String data) {
if (numCameras == 0) {
Toast.makeText(mContext, "No stream detected!", Toast.LENGTH_LONG).show();
// Finish is called here... AND FALSE IS RETURNED
VideoPlayer videoplayer = (VideoPlayer) mContext;
videoplayer.finish();
return false;
}
return true;
}
then
#SuppressLint("NewApi")
public void init(...) {
// make a call to check that the number of cameras is not 0
// AND CONTINUE IF GOOD (TRUE)
if(parseServerInfo(receivedData)){
prVideoDisplayHandler.removeCallbacks(prDisplayVideoTask);
prVideoDisplayHandler.postDelayed(prDisplayVideoTask, prDelay);
}
}
this will parse your data, finish the activity if it should, and then stop progress with your SurfaceView init method. Sorry for so much redundant code but it is just easiest to explain :P

You have to reference the Activity which you would like to finish inside the SurfaceView. You get the context from the constructor of the SurfaceView class. Use this to finish() your activity, like so
//class member
private Context mContext;
public RenderView(...) {
super(_context);
//make the context accessible from the whole class
this.mContext = _context;
...
}
Finally you call finish() on the that context to finish your VideoPlayer activity.

Related

Preventing user from calling Parent functions

UPDATE(24/10/19) : I modified the language of the question a little bit. Downvoters, if its still unclear, please let me know publicly via comments, so I could refine it more (I just see a closing vote saying unclear, neither the mod name nor the reason). Please know that English is not my first language and I really want help on this.
So i guess this is more related to java fundamentals and OOP, but i have seen some patterns followed by usual Android classes. Google is often deprecating old functions in favour of better functions, and I am thinking of doing something similar with my custom View.
I want to build a video player in android that should be easy to create and should be returning a callback on several events. For that i want the user to use my functions instead of Built in video view's function. So i am applying several approaches to prevent user from using those built-in functions:
I am using the #Deprecated notation to show the function name with a strike , eg getVolume()
I have overriden those functions that i don't want user to use and replaced their implementation with errors like :
#Override #deprecated
public void getWidth(){
throw new UnsupportedOperationException("useMyVideoPlayer#getVideoWidth()");
}
I am implementing those functions by myself in init() or their respective alternatives. Here is the code( check the comment on the code line : super.setOnErrorListener(errorListener);)
public class MyVideoPlayer extends VideoView {
public static final String TAG = "MyVP>>";
private MyPlayerCurrentPlaybackState currentPlaybackState;
private MediaController mediaController;
#Nullable
private MyVideoPlayerListener playerListener;
public MyVideoPlayer(Context context) {
super(context);
init();
}
public MyVideoPlayer(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyVideoPlayer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public MyVideoPlayer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
public void init() {
currentPlaybackState = MyPlayerCurrentPlaybackState.STOPPED;
mediaController = new MediaController(this.getContext());
mediaController.setAnchorView(this);
MediaPlayer.OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
Log.e(TAG, "internalListener:onCompletion: called");
mp.reset();
mp.setDisplay(MyVideoPlayer.this.getHolder());
currentPlaybackState = MyPlayerCurrentPlaybackState.STOPPED;
if (playerListener != null) {
playerListener.onCompleted(mp);
} else {
Log.e(TAG, "onCompletionListener:onCompletion: playerListener is null");
}
}
};
super.setOnCompletionListener(onCompletionListener);
MediaPlayer.OnErrorListener errorListener = new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
currentPlaybackState = MyPlayerCurrentPlaybackState.STOPPED;
if (playerListener != null) {
playerListener.onError(what, extra);
} else {
Log.e(TAG, "errorListener:onError: playerListener is null");
}
return true;// indicates we handled error
}
};
super.setOnErrorListener(errorListener);// <---setting error listener *inside* the view only and making setOnErrorListener(...) deprecated so that user won't use it
MediaPlayer.OnPreparedListener preparedListener = new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
//play(); // or should we call mp.start?
if (playerListener != null) {
playerListener.onPlayerPrepared(MyVideoPlayer.this);
} else {
Log.e(TAG, "preparedListener:onPrepared: player listener is null");
}
}
};
super.setOnPreparedListener(preparedListener);
}
#Nullable
public MyVideoPlayerListener getMyPlayerListener() {
return playerListener;
}
public void setMyPlayerListener(#NonNull MyVideoPlayerListener playerListener) {
this.playerListener = playerListener;
}
//---------following methods throw exception, do not use-----vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
#Override #Deprecated
public void setOnCompletionListener(#Nullable MediaPlayer.OnCompletionListener implementorsListener) {
//update: user is now not adding the on completion listener at all. he/she will only use our methods now.
throw new UnsupportedOperationException("Use MyVideoPlayer#setMyPlayerListener(...) ");
}
#Override #Deprecated
public void setOnErrorListener(MediaPlayer.OnErrorListener implementorsListener) {
//update: user is now not adding the on completion listener at all. he/she will only use our methods now.
throw new UnsupportedOperationException("Use MyVideoPlayer#setMyPlayerListener(...) ");
}
#Override #Deprecated
public int getDuration() {
throw new UnsupportedOperationException("Use MyVideoPlayer#gettotalDuration(...) ");
}
#Deprecated
public void start() {
// did because it doesn't look cool
throw new UnsupportedOperationException("Use MyVideoPlayer#play() ");
}
#Override #Deprecated
public void stopPlayback() {
// did because it doesn't look cool
throw new UnsupportedOperationException("Use MyVideoPlayer#stop() ");
}
//----------------------------------------------------------------------------------------------
#Override #Deprecated
public void setOnPreparedListener(MediaPlayer.OnPreparedListener implementorsListener) {
throw new UnsupportedOperationException("use MyVideoPlayer#onPlayerPrepared()");
}
public void play() {
super.start();
if (playerListener != null) {
playerListener.onPlay();
} else {
Log.e(TAG, "play: player listener is null");
}
currentPlaybackState = MyPlayerCurrentPlaybackState.PLAYING;
}
#Override
public void pause() {
// didn't throwed any exception because its already cool
super.pause();
currentPlaybackState = MyPlayerCurrentPlaybackState.PAUSED;
if (playerListener != null) {
playerListener.onPause();
} else {
Log.e(TAG, "play: player listener is null");
}
}
#Override
public void resume() {
// didn't throwed any exception because its already cool
super.start();
if (playerListener != null) {
playerListener.onResume();
} else {
Log.e(TAG, "play: player listener is null");
}
currentPlaybackState = MyPlayerCurrentPlaybackState.PLAYING;
}
public void stop() {
if (currentPlaybackState != MyPlayerCurrentPlaybackState.STOPPED) {
super.stopPlayback();
if (playerListener != null) {
playerListener.onStop();
} else {
Log.e(TAG, "play: player listener is null");
}
currentPlaybackState = MyPlayerCurrentPlaybackState.STOPPED;
}
}
public int gettotalDuration() {
return currentPlaybackState ==
MyPlayerCurrentPlaybackState.STOPPED ? 0 : super.getDuration();
}
//returns current video volume in range 0-100
public int getVolume() {
// Get the system's audio service and get media volume from it.
AudioManager audioManager =
(AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
if (audioManager != null) {
double volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
double max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
if (max <= 0) {
return 0;
}
// Return a range from 0-100.
return (int) ((volume / max) * 100.0f);
}
return 0;
}
public enum MyPlayerCurrentPlaybackState {
STOPPED, PAUSED, PLAYING
}
public interface MyVideoPlayerListener {
void onPlay();
void onPause();
void onResume();
void onCompleted(MediaPlayer mp);
void onError( int what, int extra);
void onPlayerPrepared(MyVideoPlayer myVideoPlayer);
void onStop();
}
/* must implement features
public interface VideoPlayer {
void play();
void pause();
void resume();
int getCurrentPosition();
void seekTo(int videoPosition);
int getDuration();
int getVolume();
void stopPlayback();
void disablePlaybackControls();
void enablePlaybackControls();
void setVideoPath(String videoUrl);
void addPlayerCallback(PlayerCallback callback);
void removePlayerCallback(PlayerCallback callback);
}
*
* */
}
I hope the above example shows my intentions: i want user to NOT USE built in methods like start() , setOnErrorListener(...), etc , but the library would either handle those functions itself (and give a callback to user) or has defined some other functions that i want the user to use, instead of built in functions( for eg i want user to call custom views' play() function instead of already present start() function coming from the parent via inheritance.)
But when I call the code via these lines:
MyVideoPlayer mvp = findViewById(R.id.mvp_main);
mvp.setVideoURI(Uri.parse(MyTags.CONTENT_URL));
mvp.setMyPlayerListener(new MyVideoPlayer.MyVideoPlayerListener() {
#Override
public void onPlay() {
Log.e(TAG, "onPlay: Video is now playing" );
}
#Override
public void onPause() {
Log.e(TAG, "onPause: Video Paused" );
}
#Override
public void onResume() {
Log.e(TAG, "onResume: video resumed" );
}
#Override
public void onCompleted(MediaPlayer mp) {
Log.e(TAG, "onCompleted: video playback completed" );
}
#Override
public void onError(int what, int extra) {
Log.e(TAG, "onError: error happenned: what:"+what+",extra:"+extra );
}
#Override
public void onPlayerPrepared(MyVideoPlayer myVideoPlayer) {
Log.e(TAG, "onPlayerPrepared: video is prepared,plau video" );
myVideoPlayer.play();
}
#Override
public void onStop() {
Log.e(TAG, "onStop: media playback stopped" );
}
});
i.e when the user uses my library in their app, It would give an exception whens calling those library defined functions like mvp.play() saying java.lang.UnsupportedOperationException: Use MyVideoPlayer#play() , indicating my overridden start() function is being called instead of super.start() . Why? Also, am I using the #Deprecated annotation correctly (that is just for the sake of showing a strikethrough warning) or does this annotation make some unwanted changes as well?
you can apply proxy/decoration pattern:
write you own Videoplayer to implement and extend those classes that the original Vidoeview implements.
here is the a pseudo code which could give you a idea:
//extends and implements the same interface and parent as the VideoView did, o that they will have the same methods to work properly.
public class MyVideoViewer extends SurfaceView implements
MediaController.MediaPlayerControl{
//private modifier to prevent others from directly calling the system's player
private VideoView view;
//you can just delete start method, so users using your player cannot even see this method.
/**void start(){
}*/
public void init(){
// do your things. can necessary method from videoplayer
view.setOnCompletionListener(listener) etc.
}
public void play()
{
view.start();
}
//indirectly call all necessary methods to make sure the system's player work properly.
#override
public void draw(arg1,arg2){
view.draw(arg1,arg2);
}
}
by doing, your player only exposes the methods you want it to expose, hide all the unnecessary methods behind your users. still it can work properly, because underneath, there is a proper system Videoview working for your player.

How to detect application runs foreground or background using Single Activity or Java class

I want to sent report to the server which means how long user Use the application in single day..I can achieve using this to method
#Override
protected void onResume() {
super.onResume();
//commonclassMethod.getInstance(UserForground);
}
#Override
protected void onStop() {
super.onStop();
//commonclassMethod.getInstance(UserBackground);
}
What happen i need to call call this method in every activity....
What i need,is there any possible to find user forground background method in single java class or activity..
Thanks in Advance.
You can can achieve this just by adding an method isAppIsInBackground(Context context) in class which is extending Application class
In that class define that method:
public static boolean isAppIsInBackground(Context context) {
boolean isInBackground = true;
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {
List<ActivityManager.RunningAppProcessInfo> runningProcesses = am.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
for (String activeProcess : processInfo.pkgList) {
if (activeProcess.equals(context.getPackageName())) {
isInBackground = false;
}
}
}
}
} else {
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
ComponentName componentInfo = taskInfo.get(0).topActivity;
if (componentInfo.getPackageName().equals(context.getPackageName())) {
isInBackground = false;
}
}
return isInBackground;
}
It will true if app is in background
Or another better approach would be just extend your each Activity by an BaseActivity, in this BaseActivity's override methods
protected void onResume() {
super.onResume();
//commonclassMethod.getInstance(UserForground);
}
protected void onStop() {
super.onStop();
//commonclassMethod.getInstance(UserBackground);
}
You can implement callback method to solve your case.
For example:
You create an interface first, then define a method, which would act as a callback. In this example we would have two classes, one classA and another classB
Interface:
public interface OnCustomEventListener{
public void onEvent(); //method, which can have parameters
}
the listener itself in classB (we only set the listener in classB)
private OnCustomEventListener mListener; //listener field
//setting the listener
public void setCustomEventListener(OnCustomEventListener eventListener) {
this.mListener=eventListener;
}
in classA, how we start listening for whatever classB has to tell
classB.setCustomEventListener(new OnCustomEventListener(){
public void onEvent(){
//do whatever you want to do when the event is performed.
}
});
how do we trigger an event from classB (for example on button pressed)
if(this.mListener!=null){
this.mListener.onEvent();
}
Here is some nice tutorials link1,link2,link3 which describes callbacks and the use-case well.
Create a class extending application and use registerActivityLifecycleCallbacks() to get the activity lifecycle
public class MyApp extends Application {
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
#Override
public void onActivityStarted(Activity activity) {
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
if(activity.getClass().getSimpleName().equalsIgnoreCase(MainActivity.class.getSimpleName())){
//Do the required thing here
}
}
#Override
public void onActivityStopped(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
});
}
}
Also dont forget to register activity in manifest
<application
android:name=".MyApp"
You have two options.
1) Do a abstract BaseActivity and make all Activities extend it. This way you only write the code in one BaseActivity and all children activities consume it.
2) Use a custom counter class to monitor app to foreground. Here is my implementation of it if you would like to copy it.
/**
* Created by App Studio 35 on 6/23/17.
*/
public class AppLifeCycleTracker implements Application.ActivityLifecycleCallbacks {;
/*///////////////////////////////////////////////////////////////
// METHODS
*////////////////////////////////////////////////////////////////
private static final String TAG = Globals.SEARCH_STRING + AppLifeCycleTracker.class.getSimpleName();
private static AppLifeCycleTracker INSTANCE;
private static int numActivitiesInMemory = 0;
private ArrayList<IAppToForegroundListener> mAppToForegroundListeners;
private boolean isRefreshing;
private Object lockAccess = new Object();
private AlertDialog mAlertDialog = null;
/*///////////////////////////////////////////////////////////////
// PROPERTIES
*////////////////////////////////////////////////////////////////
private ArrayList<IAppToForegroundListener> getAppToForegroundListeners(){
return mAppToForegroundListeners == null ? mAppToForegroundListeners = new ArrayList<IAppToForegroundListener>() : mAppToForegroundListeners;
}
public boolean getIsRefreshing(){
return isRefreshing;
}
public boolean getAppIsInBackground(){
return numActivitiesInMemory < 1;
}
/*///////////////////////////////////////////////////////////////
// CONSTRUCTOR
*////////////////////////////////////////////////////////////////
private AppLifeCycleTracker(){
}
public synchronized static AppLifeCycleTracker getInstance(){
if(INSTANCE == null){
INSTANCE = new AppLifeCycleTracker();
}
return INSTANCE;
}
/*///////////////////////////////////////////////////////////////
// LIFE CYCLE OVERRIDES
*////////////////////////////////////////////////////////////////
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
#Override
public void onActivityStarted(final Activity activity) {
//App went into background, so set a flag to avoid loading while we refresh
if(numActivitiesInMemory == 0 && !(activity instanceof SplashScreenActivity) && !(activity instanceof CreateAccountActivity)){
A35Log.v(TAG, "App Returned to Foreground, refreshing Token");
//first load on splash it goes from 0 to 1 so hold off on splash
synchronized (lockAccess) {
isRefreshing = true;
}
if (DeviceInfo.getInstance(activity).getIsConnectedToInternet()) {
CognitoManager.refreshToken(activity, new GenericHandler() {
#Override
public void onSuccess() {
A35Log.v(TAG, "Token Refresh Complete, notifying listeners");
//we are good, keep going
for(IAppToForegroundListener listener : getAppToForegroundListeners()){
listener.onRefreshTokenComplete();
}
synchronized (lockAccess) {
isRefreshing = false;
}
}
#Override
public void onFailure(Exception exception) {
//boot them to login screen
if(activity instanceof LoginActivity || activity instanceof SplashScreenActivity){
return;
}
startLoginActivity(activity);
synchronized (lockAccess) {
isRefreshing = false;
}
}
});
} else {
showInternetRequiredDialog(activity);
}
}
numActivitiesInMemory++;
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
numActivitiesInMemory--;
//if numActivities == 0 then you are in the background
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
/*///////////////////////////////////////////////////////////////
// METHODS
*////////////////////////////////////////////////////////////////
public void addAppToForegroundListener(IAppToForegroundListener listener){
getAppToForegroundListeners().add(listener);
}
public void removeAppToForegroundListener(IAppToForegroundListener listener){
getAppToForegroundListeners().remove(listener);
}
private void startLoginActivity(final Activity activity){
((AMApplication) activity.getApplication()).logoutCurrentUser(activity, false, false, null, true, null);
}
/*///////////////////////////////////////////////////////////////
// INTERFACES
*////////////////////////////////////////////////////////////////
public interface IAppToForegroundListener {
/*///////////////////////////////////////////////////////////////
// METHODS
*////////////////////////////////////////////////////////////////
void onRefreshTokenComplete();
}
private void showInternetRequiredDialog(Activity activity){
final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle("Error").setMessage("Internet is required to use this app").setNegativeButton(R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if(mAlertDialog != null && mAlertDialog.isShowing()) {
mAlertDialog.dismiss();
}
}
});
mAlertDialog = builder.create();
mAlertDialog.show();
}
}
Of course this does a little more then you are looking for as mine manages refreshing the token with cognito and forcing refresh on returning from background and things like that, so just disregard that piece. But the rest is all the same still. Hope that helps.
I'm assuming you don't need an example of a BaseActivity so I won't patronize you by pasting that.
Startup in Application class
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(AppLifeCycleTracker.getInstance());
}
Then you ONLY need to access from BaseActivity or BaseFragment IF you need to be notified when the app is in foreground or background at an Activity or fragment level. Which for your situation is not the case.
But if you ever wanted to use it, simply do this:
#Override
public void onAttach(Context context) {
super.onAttach(context);
AppLifeCycleTracker.getInstance().addAppToForegroundListener(this);
}
#Override
public void onDetach() {
super.onDetach();
AppLifeCycleTracker.getInstance().removeAppToForegroundListener(this);
}
But again, I must emphasize, this part is ONLY if you care to make your activity or fragment be aware of when the app comes back to foreground to force refresh or other behaviors. Replace onDetach with onDestroy if using Activity, but for your scenario you can skip that whole last section of code, you don't need it.

Android Beacon - Issue with bind/unbind(this)

I'm trying to create my own "BeaconManager" to develop different actions more easily.
So I've created a new class and I've implement "BeaconConsumer" and its functions :
public class MybeaconManager implements BeaconConsumer{
private BeaconManager beaconManager;
private final String TAG = "MybeaconManager";
private boolean mEnterArea = false;
private boolean mAlreadyArea = false;
public MybeaconManager(Context ctx){
beaconManager = BeaconManager.getInstanceForApplication(ctx);
beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"));
beaconManager.bind(this);
}
public void bindBeacon(BeaconConsumer consumer){
beaconManager.bind(consumer);
}
public void unBindBeacon(BeaconConsumer consumer){
beaconManager.unbind(consumer);
}
public boolean isEnterInArea() {
return mEnterArea;
}
public boolean isAlreadyInArea() {
return mAlreadyArea;
}
public void sendNotification(String Notif) {
}
#Override
public void onBeaconServiceConnect() {
beaconManager.addMonitorNotifier(new MonitorNotifier() {
#Override
public void didEnterRegion(Region region) {
mEnterArea = true;
}
#Override
public void didExitRegion(Region region) {
mEnterArea = false;
}
#Override
public void didDetermineStateForRegion(int i, Region region) {
}
});
}
Next to this, I have my MainActivity :
public class MainActivity extends Activity {
MybeaconManager mybeaconManager;
BeaconManager beaconManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mybeaconManager = new MybeaconManager(this);
if (mybeaconManager.isEnterInArea()){
Log.i("BeaconTest", "I'm detecting a Beacon");
}
}
#Override
protected void onDestroy() {
super.onDestroy();
mybeaconManager.unBindBeacon((BeaconConsumer) this);
}
#Override
protected void onStart() {
super.onStart();
}
#Override
protected void onStop() {
super.onStop();
}
}
So as you can see, I'm trying to use the functions didEnterRegion/didExitRegion more easily in a way that I only have to use on line in my MainActivity code.
The problem is, the bind/unbind(this) don't compile well and I think it's because I don't implement "BeaconConsumer" on the MainActivity because he can't get the consumer right.
It's telling me : "Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference" and return me on the bind thing.
So do you have any ideas on how I can deal with this in a way that I keep my beaconManager ?
Thank you in advance.
PS : Sorry if my English is not perfect
BeaconConsumer interface is designed to be implemented by an Activity or Service class. If you want to implement this interface in a POJO as shown in the question, you need to chain the method definitions shown below.
#Override
public Context getApplicationContext() {
return getActivity().getApplicationContext();
}
#Override
public void unbindService(ServiceConnection serviceConnection) {
getActivity().unbindService(serviceConnection);
}
#Override
public boolean bindService(Intent intent, ServiceConnection serviceConnection, int i) {
return getActivity().bindService(intent, serviceConnection, i);
}
I suspect your code already has empty implementations of these methods, otherwise your code would not compile. Make sure you have provided full implementations as shown above.

Update UI from fragment

I have a fragment that gets data from a DialogFragment via intent with onActivityResult(...);
I am trying to update the fragment's UI but runOnUiThread does not work.
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
ArrayList<Farm> farms = data.getParcelableArrayListExtra("farms_list");
float totalArea = 0;
for(Farm farm : farms) {
if (farm.isSelected()) {
Log.d("farm", farm.getName());
totalArea += farm.getArea();
}
}
final float finalTotalArea = totalArea;
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
area.setText(String.valueOf(finalTotalArea));
}
});
}
The area TextView does not update.
I get the reference to the activity with onAttach() method.
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (!(context instanceof PageFragmentCallbacks)) {
throw new ClassCastException(
"Activity must implement PageFragmentCallbacks");
}
mCallbacks = (PageFragmentCallbacks) context;
this.activity = (Activity) context;
}
onActivityResult is being called on main thread. More here. For some reason views can't be edited from this method (probably not created or drawn).
One trick that you can do is set var boolean updateTextView = false;
On onActivityResult set it on true, save data that you got, and than onResume check updateTextView and if true set data to your TextView.
What about trying to wrap it in a proper thread that you launch afterwards?
new Thread() {
public void run() {
try {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
area.setText(String.valueOf(finalTotalArea));
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
Seems like your Activity is loosing your Fragment somewhere, what I did in this case was to add a Method to my Callbacks, maybe reattachDialog(DialogFragment dialogFragment) and calling it in onResume of the DialogFragment with reattachDialog(this).
And in your Activity you do this:
#Override
public void reattachFragment(DialogFragment dialogFragment) {
this.dialogFragment = dialogFragment;
}
Or in Fragment:
#Override
public void reattachFragment(DialogFragment dialogFragment) {
getActivity().setDialog(dialogFragment);
}

Communication between Activity and GCMListenerService Android

I am working on an android application with push notification feature using GCM. I have created a class called PushNotificationService which extends GCMListenerService. Inside the onMessageReceived(String from, Bundle data) I am able to get the message in the push notification.
Now, I want to access a method inside my MainActivity class whenever a particular message is received in the push.
Below is my code :-
PushNotificationService.java
public class PushNotificationService extends GcmListenerService {
#Override
public void onMessageReceived(String from, Bundle data) {
// TODO Auto-generated method stub
super.onMessageReceived(from, data);
String message = data.getString("message");
if(message.equalsIgnoreCase("Begin Task"))
{
//call method from MainActivity.class
}
}
}
MainActivty.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void beginTask()
{
Log.d("GCM","Message Received from Server");
finish();
}
}
I want the beginTask() method to execute whenever the message "Begin Task" is received.
I know one approach is via Service->Interface->Activity architecture but I am not able to use this as I never create an object of PushNotificationService.
Please help.
UPDATE :-
I am now using Otto Library and below is my code.
Added new MyBus.java
public class MyBus extends Bus {
private static Bus bus;
//isRegistered is used to track the current registration status
private static boolean isRegistered;
private Handler handler = new Handler(Looper.getMainLooper());
public MyBus() {
if (bus == null) {
//ANY will allow event bus to run even with services
//and broadcast receivers
bus = new Bus(ThreadEnforcer.ANY);
}
}
#Override
public void register(Object obj) {
//The bus is registered when an activity starts
bus.register(obj);
isRegistered = true;
}
#Override
public void unregister(Object obj) {
//The bus is unregistered when an activity goes to background
bus.unregister(obj);
isRegistered = false;
}
#Override
public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
//post the event in main thread or background thread
bus.post(event);
} else {
handler.post(new Runnable() {
#Override
public void run() {
bus.post(event);
}
});
}
}
public boolean isRegistered(){
return isRegistered;
}
}
PushNotificationService.java
public class PushNotificationService extends GcmListenerService {
#Override
public void onMessageReceived(String from, Bundle data) {
// TODO Auto-generated method stub
super.onMessageReceived(from, data);
MyBus myBus = new MyBus();
myBus.register(myBus);
String message = data.getString("message");
if(message.equalsIgnoreCase("Begin Task"))
{
myBus.post(message);
}
}
}
MainActivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Subscribe
public void beginTask()
{
Log.d("GCM","Message Received from Server");
}
}
The problem is still not solved. The beginTask() inside MainActivity.java is still not getting called.
Use eventBus libraries to facilitate this process...
I use Otto for this process
http://square.github.io/otto/
Here is an another eventBus library https://greenrobot.github.io/EventBus/
Steps:
1.Create an event from the service
2.Add a listener in the activity
3.If the activity is running the method will be executed
**EDIT 1 : **
I have abstracted the otto bus like this.
package com.mypackage.eventBus;
import android.os.Handler;
import android.os.Looper;
import com.squareup.otto.Bus;
import com.squareup.otto.ThreadEnforcer;
/**
* Created by gowtham on 10/6/15.
*/
public class MyBus extends Bus {
private static Bus bus;
//isRegistered is used to track the current registration status
private static boolean isRegistered;
private Handler handler = new Handler(Looper.getMainLooper());
public MyBus() {
if (bus == null) {
//ANY will allow event bus to run even with services
//and broadcast receivers
bus = new Bus(ThreadEnforcer.ANY);
}
}
#Override
public void register(Object obj) {
//The bus is registered when an activity starts
bus.register(obj);
isRegistered = true;
}
#Override
public void unregister(Object obj) {
//The bus is unregistered when an activity goes to background
bus.unregister(obj);
isRegistered = false;
}
#Override
public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
//post the event in main thread or background thread
bus.post(event);
} else {
handler.post(new Runnable() {
#Override
public void run() {
bus.post(event);
}
});
}
}
public boolean isRegistered(){
return isRegistered;
}
}
create an instance of the above object and try posting event
EDIT 2 for Jcarlo's comment
Follow these steps to find the state of the activity.
In your activity's onResume call MyBus.getInstance().register(this).
In your activity's onPause call MyBus.getInstance().unregister(this).
In your GCM IntentService before posting the message
if(MyBus.getInstance().isRegistered()){
//app is alive
//post data
}else{
//show notification
}
Hope this helps
You can use LocalBroadcastManager. Create a LocalBroadcastManager object mBroadcaster = LocalBroadcastManager.getInstance(this); on onCreate of your GCMListener and send broadcast with
#Override
public void onCreate() {
super.onCreate();
mBroadcaster = LocalBroadcastManager.getInstance(this);
}
#Override
public void onMessageReceived(String from, Bundle data) {
super.onMessageReceived(from, data);
String message = data.getString("message");
if(message.equalsIgnoreCase("Begin Task")) {
Intent i = new Intent();
i.setAction("yourPackageName");
i.putExtra("DATA", yourData);
mBroadcaster.send(i);
}
}
Then you can receive message in MainActivity using a BroadcastReceiver.
BroadCastReceiver mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
beginTask();
}
};
Also you need to register and unregister the receiver in onStart and onStop of your activity
#Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter();
filter.addAction("yourPackageName);
LocalBroadcastManager.getInstance(this).registerReceiver((mBroadcastReceiver), filter);
}
#Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver);
}

Categories