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.
Related
I'm a beginner in Android Studio. I've recently picked up some Demo Apps for managing BLE devices, made them work separately, and am now trying to join two of them in a single App. Both Apps used a BLE Service, so I have to join them into a single service (or have them work together).
While looking at the code I noticed that one of these Service classes has no onCreate() method. Then I looked into the implementation and found the Service is Instantiated using a nested class of the Service that extends the Binder class.
Here's the relevant code from the service class:
#SuppressLint("NewApi")
public class BluetoothLeService extends Service {
private final String TAG = BluetoothLeService.class.getSimpleName();
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private String mBluetoothDeviceAddress;
private BluetoothGatt mBluetoothGatt;
private BluetoothGattCharacteristic mNotifyCharacteristic;
private static EncryptDecode encryptDecode = new EncryptDecode(); // Encryption and Decryption tool task
private IBleOperateCallback mBleOperateCallback;
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mBleOperateCallback.bleData(SmctConstant.KEY_BLE_CONNECT_STATE, SmctConstant.VALUE_BLE_CONNECTED);
Log.i(TAG, "Connected to GATT server.");
mBluetoothGatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
mBleOperateCallback.bleData(SmctConstant.KEY_BLE_CONNECT_STATE, SmctConstant.VALUE_BLE_DISCONNECTED);
close();
Log.i(TAG, "Disconnected from GATT server.");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
mBleOperateCallback.bleData(SmctConstant.KEY_BLE_CONNECT_STATE,
SmctConstant.VALUE_BLE_SERVICE_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
};
public class LocalBinder extends Binder {
public BluetoothLeService getService() {
return BluetoothLeService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public boolean onUnbind(Intent intent) {
close();
return super.onUnbind(intent);
}
private final IBinder mBinder = new LocalBinder();
/**
* Initializes a reference to the local Bluetooth adapter.
*
* #return Return true if the initialization is successful.
*/
public boolean initialize() {
// For API level 18 and above, get a reference to BluetoothAdapter
// through
// BluetoothManager.
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
Log.e(TAG, "Unable to initialize BluetoothManager.");
return false;
}
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
return false;
}
return true;
}
...
SOME MORE FUNCTIONS...
...
}
And this is how the Service instance gets declared in the Activity that uses it:
BluetoothLeService mBluetoothLeService;
mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
I am trying to understand: how exactly is the Class instantiated without the onCreate() method? I've checked the onCreate() method from the Service class and it just throws and exception. I need to understand it because the other service I'm using does have such method and I want to join them.
Also: What is the difference between using this LocalBinder nested class and straight up using a class constructor?
EDIT: Here's the onCreate() method from the extended class Service. You can see it just throws a Runtime Exception. onStart() is identical.
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package android.app;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.IBinder;
import java.io.FileDescriptor;
import java.io.PrintWriter;
public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
public static final int START_CONTINUATION_MASK = 15;
public static final int START_FLAG_REDELIVERY = 1;
public static final int START_FLAG_RETRY = 2;
public static final int START_NOT_STICKY = 2;
public static final int START_REDELIVER_INTENT = 3;
public static final int START_STICKY = 1;
public static final int START_STICKY_COMPATIBILITY = 0;
public static final int STOP_FOREGROUND_DETACH = 2;
public static final int STOP_FOREGROUND_REMOVE = 1;
public Service() {
super((Context)null);
throw new RuntimeException("Stub!");
}
public final Application getApplication() {
throw new RuntimeException("Stub!");
}
public void onCreate() {
throw new RuntimeException("Stub!");
}
/** #deprecated */
#Deprecated
public void onStart(Intent intent, int startId) {
throw new RuntimeException("Stub!");
}
public int onStartCommand(Intent intent, int flags, int startId) {
throw new RuntimeException("Stub!");
}
public void onDestroy() {
throw new RuntimeException("Stub!");
}
public void onConfigurationChanged(Configuration newConfig) {
throw new RuntimeException("Stub!");
}
public void onLowMemory() {
throw new RuntimeException("Stub!");
}
public void onTrimMemory(int level) {
throw new RuntimeException("Stub!");
}
public abstract IBinder onBind(Intent var1);
public boolean onUnbind(Intent intent) {
throw new RuntimeException("Stub!");
}
public void onRebind(Intent intent) {
throw new RuntimeException("Stub!");
}
public void onTaskRemoved(Intent rootIntent) {
throw new RuntimeException("Stub!");
}
public final void stopSelf() {
throw new RuntimeException("Stub!");
}
public final void stopSelf(int startId) {
throw new RuntimeException("Stub!");
}
public final boolean stopSelfResult(int startId) {
throw new RuntimeException("Stub!");
}
public final void startForeground(int id, Notification notification) {
throw new RuntimeException("Stub!");
}
public final void stopForeground(boolean removeNotification) {
throw new RuntimeException("Stub!");
}
public final void stopForeground(int flags) {
throw new RuntimeException("Stub!");
}
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
throw new RuntimeException("Stub!");
}
}
EDIT2: As Gabe pointed out in his answer: This is just the stub code from the Service, not the actual implementation. So I got confused by the onCreate() method that Android Studio showed me.
There's a default implementation of onCreate in the Service class. If you don't override it, you just use that default implementation. That's sufficient to create the Service correctly, if you don't need additional logic.
Your other question should be asked separately (1 question per post), but there's not enough code for me to answer it- I have no idea what the variable service is. However, you never create a service via constructor- it will not be initialized properly. You always call startService or bindService and let Android create it.
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.
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.
I would like to ask about a strange situation, that happened during usage of Google Maps API v2.
There is an error in logcat that says:
The Google Play services resources were not found. Check your project
configuration to ensure that the resources are included.
The things that I have:
Google Map is displayed perfectly along with markers - I have acquired relevant code from Google.
google-play-services.jar library is included perfectly with Eclipse
(project->properties->android->add...).
The checkboxes in Eclipse (project->properties->java build path->order build path) are all checked properly.
The code
GooglePlayServicesUtil.isGooglePlayServicesAvailable(context);
returns true.
This code is running at a device, Nexus 4, not emulator.
I am trying to invoke event, that would allow me to get current position by this class:
public class FindMyLocationManager implements LocationListener, LocationSource
{
private OnLocationChangedListener mListener;
private LocationManager locationManager;
private GoogleMap map;
private Context ctx;
private int intervalTime;
private int intervalDistance;
public void setMap(GoogleMap map)
{
this.map = map;
}
public int getIntervalTime()
{
return intervalTime;
}
public void setIntervalTime(int intervalTime)
{
this.intervalTime = intervalTime;
}
public int getIntervalDistance()
{
return intervalDistance;
}
public void setIntervalDistance(int intervalDistance)
{
this.intervalDistance = intervalDistance;
}
public FindMyLocationManager(Context mContext)
{
this.ctx = mContext;
locationManager = (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
}
#Override
public void activate(OnLocationChangedListener listener)
{
mListener = listener;
isGooglePlayOk(); //returns true
if(isGPSAvailable())
{
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, intervalTime, intervalDistance, this);
}
else if(isCompassAvailable())
{
Log.d("DEBUG", "No GPS here");
}
else
{
Log.d("DEBUG", "Nothing here");
}
}
private boolean isGPSAvailable()
{
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
}
public boolean isGooglePlayOk()
{
int isAvailable = GooglePlayServicesUtil.isGooglePlayServicesAvailable(ctx);
if(isAvailable == ConnectionResult.SUCCESS)
{
Toast.makeText(ctx, "Can connect to Goolge Play", Toast.LENGTH_SHORT).show();
return true;
}
else if(GooglePlayServicesUtil.isUserRecoverableError(isAvailable))
{
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(isAvailable, (Activity)ctx, 9001);
dialog.show();
}
else
{
Toast.makeText(ctx, "Can't connect to Goolge Play", Toast.LENGTH_SHORT).show();
}
return false;
}
private boolean isCompassAvailable()
{
PackageManager pm =
ctx.getPackageManager();
return pm.hasSystemFeature(PackageManager.FEATURE_SENSOR_COMPASS);
}
#Override
public void deactivate()
{
locationManager.removeUpdates((android.location.LocationListener)this);
mListener = null;
}
public void restart()
{
locationManager.removeUpdates((android.location.LocationListener)this);
if(isGPSAvailable())
{
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, intervalTime, intervalDistance, this);
}
else if(isCompassAvailable())
{
Log.d("DEBUG", "No GPS");
}
else
{
Log.d("DEBUG", "Nothing at all");
}
}
// the compiler never enters here
#Override
public void onLocationChanged(Location location)
{
Toast.makeText(this.ctx, location.getLatitude() + " " + location.getLongitude(), Toast.LENGTH_LONG).show();
if(mListener != null)
{
mListener.onLocationChanged(location);
}
map.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(location.getLatitude(), location.getLongitude())));
}
#Override
public void onProviderDisabled(String arg0)
{
// TODO Auto-generated method stub
}
#Override
public void onProviderEnabled(String arg0)
{
// TODO Auto-generated method stub
}
#Override
public void onStatusChanged(String arg0, int arg1, Bundle arg2)
{
// TODO Auto-generated method stub
}
}
And here is the usage of above code:
// this method is called in many places in the program, like onCreate of my view with map or onResume
private void setUpMapIfNeeded()
{
if(map == null)
{
map = ((SupportMapFragment)getFragmentManager().findFragmentById(R.id.map)).getMap();
if(map != null)
{
map.setMyLocationEnabled(true);
locationManager.setMap(map);
locationManager.setIntervalDistance(0);
locationManager.setIntervalTime(0);
map.setLocationSource(locationManager); //Here I apply the object from above class
//if(currentModel != null)
//currentModel = getCurrentModel(); TODO
//moveCameraInstantly(map.);
focusCamera();
fillMapWithMarkers(FindMyApplication.MAP_MARKER_MODELS);
}
}
}
UPDATE
So it seems that the error itself is harmless, but I still don't get the onLocationChanged event.
UPDATE 2
This code is based on How to get My Location changed event with Google Maps android API v2? .
If i understand correctly you have defined the location update method, but have not started requesting the location updates.
To send the request for location updates, create a location client and a request in onCreate():
protected void onCreate(Bundle savedInstanceState) {
...
mLocationClient = new LocationClient(this, this, this);
mLocationRequest = LocationRequest.create();
}
Then connect it in onStart():
protected void onStart() {
...
mLocationClient.connect();
}
Then make the update request in onConnected():
public void onConnected(Bundle dataBundle) {
...
mLocationClient.requestLocationUpdates(mLocationRequest, this);
}
Here is a complete guide on how to do this correctly:
http://developer.android.com/training/location/receive-location-updates.html#StartUpdates
The Google Play services resources were not found. error is a common bug in the library.
if the map shows like it should then you did everything correctly, there is something in the library that is causing the problem that google needs to fix. I get this error in my app even when I dont use google maps.
since you have google play service you should be using the new location API and not the old one.
http://developer.android.com/training/location/index.html
EDIT
Also this piece of code seems suspicious:
if(map == null) {
map = <something new>;
if(map != null) {
<do thing>
}
}
Is your map always null before entering this method?
EDIT2
map.setOnMyLocationChangeListener(new GoogleMap.OnMyLocationChangeListener() {
#Override
public void onMyLocationChange(Location location) {
Log.d("DEBUG", "setOnMyLocationChangeListener");
setUpMapIfNeeded();
}
});
if it doesn't work, then try also:
map.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
#Override
public onCameraChange(CameraPosition cameraPosition) {
Log.d("DEBUG", "setOnCameraChangeListener");
setUpMapIfNeeded();
}
});
I need log results on this.
Here is my custom media controller. I would like to add listeners for fast forward and rewind buttons. I am able to add listeners for previous/next videos. But not on these buttons. Can any one point out a reference or give an idea on how to implement it?
public class MediaController extends MediaController {
MyListener mListener;
public MediaController(Context context) {
super(context);
}
public MediaController(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MediaController(Context context, boolean useFastForward) {
super(context, useFastForward);
}
#Override
public void show(int timeout) {
super.show(3000);
}
#Override
public void hide() {
super.hide();
}
public interface MyListener {
public void onSetVisibilityCalled();
}
public void registerListener(MyListener myListener) {
this.mListener = myListener;
}
public void setVisibility(int visibility) {
super.setVisibility(visibility);
if (mListener != null)
mListener.onSetVisibilityCalled();
}
}
Listeners for prev/next buttons
// Media control event listener
MediaController.setPrevNextListeners(new OnClickListener() {
#Override
public void onClick(View v) {
System.out.println("forward button pressed");
}
}, new OnClickListener() {
#Override
public void onClick(View v) {
System.out.println("backward button pressed");
}
});
Found a way myself by adding custom VideoView. Added condition by overriding seekTo() method.
public interface SeekListener {
void onSeekTo(boolean ffwdrwd);
}
#Override
public void seekTo(int pos) {
boolean ffwdrwd = false;
if (super.getCurrentPosition() <= pos)
ffwdrwd = false;
else
ffwdrwd = true;
if (mListener != null) {
mListener.onSeekTo(ffwdrwd);
}
super.seekTo(pos);
}
And in my activity,
videoView.setSeekListener(new SeekListener() {
#Override
public void onSeekTo(boolean ffwdrwd) {
if(ffwdrwd)
//Movie Player - Seeking backward
else
//Movie Player - Seeking forward
}
});
You cannot set the on click listeners for the default MediaController's fast-forward or rewind buttons. The trickplay functionality is implemented naively using seekTo. You can find the code here. Search for mFfwdListener.
If you think about it, they have to let the user provide next/previous implementations (unless they were to implement the whole playlist, also). I guess they figure if you want something better than a naive implementation, you won't mind setting up a simple UI for it.