Key Point: My application should be capable of running in background.
A rough structure of my application calls is:
HomeActivity(AppCompatActivity) -> MainActivity(AppCompatActivity) -> CameraRecorderService(IntentService) AND SensorService(Service)
Brief Explanation: On a button press in HomeActivity, MainActivity is triggered using Intent. As soon as MainActivity is called, in its onCreate() method, I call the two services i.e. CameraRecorderService and SensorService.
The CameraRecorderService records the video in an infinite loop unless it receives an event from SensorService.
The current application is working completely fine unless the app goes in background, i.e. I press the home button. It gives D/Camera﹕ app passed NULL surface , E/MediaRecorderJNI﹕ Application lost the surface.
The relevant code for MainActivity and CameraRecorderService are:
"MainActivity.java"
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback{
public static SurfaceView mSurfaceView; //declared static so that it can be accessed by Services
public static SurfaceHolder mSurfaceHolder;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView1);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
Intent intent2 = new Intent(MainActivity.this, SensorService.class);
intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startService(intent2);
Intent intent = new Intent(MainActivity.this, CameraRecorderService.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startService(intent);
ImageButton btnStop = (ImageButton) findViewById(R.id.stop);
btnStop.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
stopService(new Intent(MainActivity.this, CameraRecorderService.class));
stopService(new Intent(MainActivity.this, SensorService.class));
System.exit(0);
}
});
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
"CameraRecorderService.java"
public class CameraRecorderService extends IntentService {
public CameraRecorderService(){
super("CameraRecorderService");
}
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private static Camera mServiceCamera;
private boolean mRecordingStatus;
private MediaRecorder mMediaRecorder;
static boolean accidentStatus = false;
boolean manualStopStatus = false;
int accidentOnVideoIndex = 0;
#Override
public void onCreate() {
super.onCreate();
mSurfaceView = MainActivity.mSurfaceView; //Getting the surface from Activity
mSurfaceHolder = MainActivity.mSurfaceHolder;
}
#Override
public void onDestroy() {
super.onDestroy();
manualStopStatus = true;
if(mRecordingStatus){
stopRecording();
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
protected void onHandleIntent(Intent intent) {
while(!accidentStatus){
int i =1;
while(i < 3) {
if (!accidentStatus && !manualStopStatus) {
accidentOnVideoIndex = i;
startRecording("someName" + i);
try {
Thread.sleep(5000);
if(mRecordingStatus)
stopRecording();
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}else{
break;
}
}
}
if(!manualStopStatus) {
startRecording("someName3");
try {
Thread.sleep(5000);
stopRecording();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public boolean startRecording(String fileName){
try {
mRecordingStatus = true;
mServiceCamera = Camera.open();
Camera.Parameters p = mServiceCamera.getParameters();
final List<Camera.Size> listSize = p.getSupportedPreviewSizes();
Camera.Size mPreviewSize = listSize.get(2);
Log.v(TAG, "use: width = " + mPreviewSize.width
+ " height = " + mPreviewSize.height);
p.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
p.setPreviewFormat(PixelFormat.YCbCr_420_SP);
mServiceCamera.setParameters(p);
mServiceCamera.setDisplayOrientation(90);
try {
mServiceCamera.setPreviewDisplay(mSurfaceHolder);
mServiceCamera.startPreview();
}
catch (IOException e) {
Log.e(TAG, e.getMessage());
e.printStackTrace();
}
mServiceCamera.unlock();
mMediaRecorder = new MediaRecorder();
mMediaRecorder.setCamera(mServiceCamera);
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_480P));
mMediaRecorder.setOutputFile("/somePath/" + fileName + ".mp4");
mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
mMediaRecorder.prepare();
mMediaRecorder.start();
return true;
} catch (IllegalStateException e) {
Log.d(TAG, e.getMessage());
e.printStackTrace();
return false;
} catch (IOException e) {
Log.d(TAG, e.getMessage());
e.printStackTrace();
return false;
}
}
public void stopRecording() {
mRecordingStatus = false;
try {
mServiceCamera.reconnect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mMediaRecorder.stop();
mMediaRecorder.reset();
mServiceCamera.stopPreview();
mMediaRecorder.release();
mServiceCamera.release();
mServiceCamera = null;
}
public static class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
System.out.println("Accident!!");
accidentStatus = true;
}
}
I think the problem is that the SurfaceView is destroyed when Activity goes in onPause() state and as I have used a static SurfaceView it is nullified. And so the CameraRecorderService(IntentService) does not get the value from Activity because it runs on a separate thread.
But how do I solve this problem? As I said, the application works fine in foreground but the main purpose of my application is to run in background. Any suggestions are welcome!
Related
So basically, i got a list view that shows a list of bands.
When i press on one of the bands in the list, it creates a band profile page via an adapter.
In this layout, i got a play button, which starts a MediaPlayer service and play a song.
So far, so good.
The problem is, im trying to update the seekBar, located in the band profile page (created by the adapter mentioned above), from the MediaPlayer service Java file, but i'm unable to reach it.
i'm trying to use :
LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.activity_singleband, null, false);
seekBar = (SeekBar) layout.findViewById(R.id.seekBarBand);
but it seems that it uses another "instance" of the view, and not the one i'm currently viewing.
Here is the full MusicService Java file:
public class MusicService extends Service {
SeekBar seekBar;
Handler handler;
Runnable runnable;
NotificationManager nMN;
TextView BandName;
String songLink;
String songName;
String bandName;
String SongPlaying="";
String BandPlaying="";
public static MediaPlayer mp;
public static Boolean mpState = true;
public static Boolean mpPause = false;
public static Boolean isPlaying = false;
#Override
public void onCreate() {
super.onCreate();
}
// Play Cycle for seekBar
public void PlayCycle(){
seekBar.setProgress(mp.getCurrentPosition());
if (mp.isPlaying()){
runnable = new Runnable() {
#Override
public void run() {
PlayCycle();
}
};
handler.postDelayed(runnable, 1000);
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
songLink = intent.getStringExtra("SongLink");
songName = intent.getStringExtra("SongName");
bandName = intent.getStringExtra("BandName");
LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.activity_singleband, null, false);
seekBar = (SeekBar) layout.findViewById(R.id.seekBarBand);
BandName = (TextView) layout.findViewById(R.id.singleBandNameView);
BandName.setText("Bla Bla");
System.out.print("Band Name : "+ BandName.getText() );
handler = new Handler();
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean input) {
if (input){
mp.seekTo(progress);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
if (isPlaying && !songName.equals(SongPlaying) && !BandPlaying.equals(bandName)) {
Stop();
mp = new MediaPlayer();
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
Uri songUri = Uri.parse(songLink);
mp.setDataSource(getApplicationContext(), songUri);
SongPlaying = songName;
Toast.makeText(getApplicationContext(), "Please wait", Toast.LENGTH_SHORT).show();
isPlaying=true;
new prepare().execute();
}
catch (IOException e) {
e.printStackTrace();
}
}
else if (!isPlaying && !songName.equals(SongPlaying) && !BandPlaying.equals(bandName) ) {
mp = new MediaPlayer();
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
Uri songUri = Uri.parse(songLink);
mp.setDataSource(getApplicationContext(), songUri);
SongPlaying = songName;
Toast.makeText(getApplicationContext(), "Please wait", Toast.LENGTH_SHORT).show();
new prepare().execute();
isPlaying=true;
}
catch (IOException e) {
e.printStackTrace();
}
}
else {
Toast.makeText(getApplicationContext(), "Song is Already Playing", Toast.LENGTH_SHORT).show();
}
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
mp.release();
handler.removeCallbacks(runnable);
}
/// Permanent Notification
private void showNotification() {
nMN = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification n = new Notification.Builder(this)
.setContentTitle(songName)
.setContentText(bandName)
.setSmallIcon(R.drawable.ic_bandcamp)
//.addAction(R.drawable.ic_play_arrow_black_24dp, "button1", new pause())
.setOngoing(true)
.build();
nMN.notify(1, n);
}
/// PREPARE SONG TO PLAY //
public class prepare extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.prepare();
PlayCycle();
seekBar.setMax(mp.getDuration());
mp.start();
SongPlaying = songName;
isPlaying=true;
showNotification(); // show notification
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
Toast.makeText(getApplicationContext(),"Song Finished", Toast.LENGTH_LONG).show();
new stop().execute();
}
});
} catch (IOException io){ io.printStackTrace(); }
return null; }}
/// STOP PLAYING SONG //
public static void Stop()
{
mp.reset();
mp.release();
isPlaying=false;
}
public static class stop extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.reset();
mp.release();
isPlaying=false;
} catch (Exception io){ io.printStackTrace(); }
return null; }}
/// PAUSE PLAYING SONG//
public static class pause extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.pause(); }
catch (Exception io){ io.printStackTrace(); }
return null; }}
public static class start extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.start(); }
catch (Exception io){ io.printStackTrace(); }
return null; }}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
Would love to have some suggestions.
Thanks in advance.
Okey Firstly I suggest you to read more about Android Service classes. Secondly Do NOT place you visual components like a SeekBar or TextView in the Service because it doesn't make any sense. Service is just a background task and do not know about the UI. Since it's executed in the background you don't even need AsyncTask inside this so thirdly remove ALL AsyncTasks in your service and run their task like they don't need to be executed in the background.
So all what you have to do is create a Fragment/Activity for the UI, then bind your Fragment/Acitivity with your Service to communicate and update UI components.
The easiest way is make the Service a Singleton like it's done here:
public class MusicService extends Service {
private static MusicService instance = null;
public static MusicService getInstance() {
return instance;
}
Handler handler;
Runnable runnable;
NotificationManager nMN;
String songLink;
String songName;
String bandName;
String SongPlaying="";
String BandPlaying="";
public static MediaPlayer mp;
public static Boolean mpState = true;
public static Boolean mpPause = false;
public static Boolean isPlaying = false;
#Override
public void onCreate() {
instance = this;
super.onCreate();
}
// Play Cycle for seekBar
public void PlayCycle(){
seekBar.setProgress(mp.getCurrentPosition());
if (mp.isPlaying()){
runnable = new Runnable() {
#Override
public void run() {
PlayCycle();
}
};
handler.postDelayed(runnable, 1000);
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
songLink = intent.getStringExtra("SongLink");
songName = intent.getStringExtra("SongName");
bandName = intent.getStringExtra("BandName");
handler = new Handler();
if (isPlaying && !songName.equals(SongPlaying) && !BandPlaying.equals(bandName)) {
Stop();
mp = new MediaPlayer();
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
Uri songUri = Uri.parse(songLink);
mp.setDataSource(getApplicationContext(), songUri);
SongPlaying = songName;
Toast.makeText(getApplicationContext(), "Please wait", Toast.LENGTH_SHORT).show();
isPlaying=true;
new prepare().execute();
}
catch (IOException e) {
e.printStackTrace();
}
}
else if (!isPlaying && !songName.equals(SongPlaying) && !BandPlaying.equals(bandName) ) {
mp = new MediaPlayer();
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
Uri songUri = Uri.parse(songLink);
mp.setDataSource(getApplicationContext(), songUri);
SongPlaying = songName;
Toast.makeText(getApplicationContext(), "Please wait", Toast.LENGTH_SHORT).show();
new prepare().execute();
isPlaying=true;
}
catch (IOException e) {
e.printStackTrace();
}
}
else {
Toast.makeText(getApplicationContext(), "Song is Already Playing", Toast.LENGTH_SHORT).show();
}
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
mp.release();
handler.removeCallbacks(runnable);
}
/// Permanent Notification
private void showNotification() {
nMN = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification n = new Notification.Builder(this)
.setContentTitle(songName)
.setContentText(bandName)
.setSmallIcon(R.drawable.ic_bandcamp)
//.addAction(R.drawable.ic_play_arrow_black_24dp, "button1", new pause())
.setOngoing(true)
.build();
nMN.notify(1, n);
}
/// PREPARE SONG TO PLAY //
public class prepare extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.prepare();
PlayCycle();
seekBar.setMax(mp.getDuration());
mp.start();
SongPlaying = songName;
isPlaying=true;
showNotification(); // show notification
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
Toast.makeText(getApplicationContext(),"Song Finished", Toast.LENGTH_LONG).show();
new stop().execute();
}
});
} catch (IOException io){ io.printStackTrace(); }
return null; }}
/// STOP PLAYING SONG //
public static void Stop()
{
mp.reset();
mp.release();
isPlaying=false;
}
public static class stop extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.reset();
mp.release();
isPlaying=false;
} catch (Exception io){ io.printStackTrace(); }
return null; }}
/// PAUSE PLAYING SONG//
public static class pause extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.pause(); }
catch (Exception io){ io.printStackTrace(); }
return null; }}
public static class start extends AsyncTask {
#Override
protected Object doInBackground(Object[] objects) {
try {
mp.start(); }
catch (Exception io){ io.printStackTrace(); }
return null; }}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
// throw new UnsupportedOperationException("Not yet implemented");
return null;
}
The basic idea is if no face detected,it should return to Main(reset Activity) but after returning back to main ,getCameraInstance(0) returns null.
If i want to back to Main ,it will call the onPause() and release the cam and after main re-created the backCamera is already relased and it should problem-free create a new instance of back camera.Am i wrong ?
Thanks
ps :i get java.lang.RuntimeException: Fail to connect to camera service and that means i didnt release the camera correctly but i cant find my error.
Here is my code
public class MainActivity extends Activity{
....
private Camera mCameraBack=null;
private CameraPreviewBack mPreviewBack;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(mCameraBack==null){
mCameraBack=getCameraInstance(0);
}
try{
mPreviewBack=new CameraPreviewBack(this,mCameraBack );
previewBack=(FrameLayout)findViewById(R.id.camera_preview_back);
previewBack.addView(mPreviewBack);
}catch(Exception ex){
Log.d(TAG,"surfaceCreate");
ex.printStackTrace();
}
public void capture(View view){
mPreviewBack.takePicture();
...
}
#Override
protected void onPause() {
Log.d(TAG, "onPause");
super.onPause();
if (mCameraBack != null) {
mCameraBack.stopPreview();
mCameraBack.release();
mCameraBack = null;
}
if (mPreviewBack != null) {
previewBack.removeView(mPreviewBack);
mPreviewBack = null;
}
}
#Override
public void onResume() {
Log.d(TAG, "onResume");
super.onResume();
if (mCameraBack == null) {
mCameraBack = getCameraInstance(0);
}
if (mPreviewBack == null) {
mPreviewBack=new CameraPreviewBack(this,mCameraBack );
previewBack.addView(mPreviewBack);
}
}
public static Camera getCameraInstance(int cameraId){
Camera c = null;
try {
c = Camera.open(cameraId);
}
catch (Exception e){
}
return c; // returns null if camera is unavailable
}
}
and
public class CameraPreviewBack extends SurfaceView implements SurfaceHolder.Callback {
...
public CameraPreviewBack(Context context,Camera camera) {
super(context);
mCamera=camera;
this.context=context;
mHolder=getHolder();
mHolder.addCallback(this);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Log.d(TAG, "Surface created");
try {
if(mCamera!=null){
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
}
} catch (IOException e) {
Log.d(TAG, "mHolder failure");
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
Log.d(TAG, "Surface changed");
configureCamRotation();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
public void takePicture(){
..
task.execute();
try {
task.get();
} catch (InterruptedException e) {
} catch (ExecutionException e) {
}
task.cancel(isFinished);
}
private PictureCallback getPictureCallback() {
PictureCallback mPicture = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera){
....
if(!detected){
Intent intentMain=new Intent(context, MainActivity.class);
intentMain.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intentMain);
}
...
}
private class TakePictureTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
mCamera.takePicture(null, null, getPictureCallback());
try {
Thread.sleep(2000); // 3 second preview
} catch (InterruptedException e) {
}
return null;
}
}
newer swallow exceptions
like here:
catch (Exception e){
}
Print this exception Log.d(TAG, "Error when init camera", e); and maybe you will get:
Runtime Exception http://developer.android.com/reference/android/hardware/Camera.html#open(int)
releasing camera onDestroy and changing Intents in onPostExecute method of Asynctask solved my problem
I successfully implemented AdMob interstitial ads into my application but the only problem is that they are not clickable.
Here is my AdMob.java class:
import android.app.Activity;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.InterstitialAd;
public class Admob extends AdListener{
private Activity ctx;
//Admob
private InterstitialAd interstitial;
private AdRequest adRequest;
private String unitId;
public static boolean fromAdmob = false;
public Admob(Activity ctx, String unitId)
{
this.ctx = ctx;
this.unitId = unitId;
try{
this.interstitial = new InterstitialAd(this.ctx);
interstitial.setAdUnitId(unitId);
interstitial.setAdListener(this);
adRequest = new AdRequest.Builder().build();
}
catch (Exception e){
e.printStackTrace();
}
}
public void loadAd()
{
try{
interstitial.loadAd(adRequest);
}
catch (Exception e){
e.printStackTrace();
}
}
#Override
public void onAdLoaded() {
try{
fromAdmob = true;
interstitial.show();
}
catch (Exception e){
e.printStackTrace();
}
}
#Override
public void onAdFailedToLoad(int errorCode)
{
}
#Override
public void onAdClosed() {
fromAdmob = false;
}
}
And the MainPageActivity.java file:
public class MainPageActivity extends Activity implements IGameListener, IEventDelegate{
//Tags
private static final String TAG = "bubblesMainPage";
//Context
public static Activity instance;
//Media
private MediaPlayer mp;
//Ads
private Admob admob;
//TapSDK
private Tap tap;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_page_activity);
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
tr
Tap.establish(this);
tap = new Tap(this, "xxxx");
}
catch (Exception e){
e.printStackTrace();
}
//Admob
this.admob = new Admob(this, "ADMOB_ID");
//Context
instance = this;
//Media
mp = MediaPlayer.create(getApplicationContext(), R.raw.background_music);
mp.setLooping(true);
//Set activity properties
try {
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
} catch (Exception e) {
e.printStackTrace();
}
//Intents & Listeners
final Intent gamePlayActivity = new Intent(this, BubbleShooterActivity.class);
BubbleShooterActivity.listener = this;
//Animations
Animation scaleUpAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.scale_up);
findViewById(R.id.play_button).startAnimation(scaleUpAnim);
//UI Events
findViewById(R.id.play_button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new Thread(new Runnable() {
#Override
public void run() {
try {
startActivity(gamePlayActivity);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
});
findViewById(R.id.more_games_button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
tap.showMoreGamesWithViewControllerAndAppID((IEventDelegate)instance);
}
});
}
/*
Game methods - start
*/
#Override
public void onGameStart() {
Log.i(TAG, "onGameStart");
}
#Override
public void onLevelFailed() {
//Level failed window
try{
Intent i = new Intent(this, LostActivity.class);
startActivity(i);
}
catch (Exception e){
e.printStackTrace();
}
Log.i(TAG, "onLevelFailed");
}
#Override
public void onLevelUp() {
Log.i(TAG, "onLevelUp");
if(admob != null){
runOnUiThread(new Runnable() {
#Override
public void run() {
try{
admob.loadAd();
}
catch (Exception e){
e.printStackTrace();
}
try{
Intent i = new Intent(instance, WonActivity.class);
startActivity(i);
}
catch (Exception e){
e.printStackTrace();
}
}
});
}
}
#Override
public void onGameEnd() {
Log.i(TAG, "onGameEnd");
}
/*
TapSDK methods - start
*/
#Override
public void viewDidAppear() {
Log.i(TAG, "TapSDK: viewDidAppear");
}
#Override
public void viewDidDisappear() {
Log.i(TAG, "TapSDK: viewDidDisappear");
}
#Override
public void viewConnectFail(String s) {
Log.i(TAG, "TapSDK: viewConnectFail " + s);
}
#Override
public void conversion() {
Log.i(TAG, "TapSDK: conversion");
}
#Override
public void onOfferComplete(int score) {
Log.i(TAG, "TapSDK: onOfferComplete " + score);
}
/*
Activity methods start
*/
#Override
public void onBackPressed() {
Log.i(TAG, "onBackPressed");
try{
Intent exitScreen = new Intent(this, ExitActivity.class);
startActivity(exitScreen);
}
catch (Exception e){
e.printStackTrace();
Log.e(TAG, "onBackPressed: " + e.getMessage());
}
}
#Override
protected void onStart() {
super.onStart();
// Monitor launch times and interval from installation
RateThisApp.onStart(this);
// If the criteria is satisfied, "Rate this app" dialog will be shown
RateThisApp.showRateDialogIfNeeded(this);
}
#Override
protected void onResume()
{
super.onResume();
if(tap != null)
tap.onResume();
mp.seekTo(0);
mp.start();
}
#Override
protected void onPause()
{
if(tap != null)
tap.onPause();
mp.pause();
super.onPause();
}
#Override
protected void onDestroy()
{
mp.stop();
mp = null;
if(tap != null)
tap.onDestroy();
super.onDestroy();
}
}
I couldn't figure out how to show the interstitial ad when the user levels up and presses the play button (which will lead him to the next level). The interstitial ad show up when he presses the home button which will take him to the MainActivity screen.
Can someone help me to make the ads clickable? Also, any suggestions on how to add the interstitial ad after each completed level? When someone has finished a level and presses the play button, an interstitial ad should appear and when that is closed he should be able to play the next level.
I really appreciate your help!
A number of issues:
Don't call interstitial.show() from onAdLoaded. Call show() when the user presses the play button for your next level (but only if an ad is available).
Get rid of public static Activity instance; Never hold a static reference to an Activity. You will just leak context everywhere.
Instead of calling admob.loadAd() from onLevelUp() call it as soon as you create your Activity (it takes times to load an ad from the network). NB you don't need to call it from runOnUIThread()
Call admob.loadAd() again after each time you show the ad to the user.
I am trying to develop an application in Android with the following feature: record video and audio seamlessly, even if the user has another application in the foreground. A common scenario for it would be: the user opens the app, starts recording, then opens a navigation app or receives a call. I want my app to keep recording.
I have put together some code, mainly inspired by this tutorial, which will be quoted below. I have encountered two problems, however:
1. When I press the "home" key, video recording freezes, but sound is fine
2. When I navigate back to the app, the preview is black
My questions are:
Is my goal possible on Android?
What am I doing wrong?
My code:
public class GlobalState extends Application {
private boolean recording = false;
private boolean loggingEnabled = true;
private Camera serviceCamera = null;
private CameraPreview cameraPreview = null;
#Override
public void onCreate() {
try {
serviceCamera = Camera.open();
} catch (Exception e) {
}
super.onCreate();
}
public boolean isRecording() {
return recording;
}
public boolean isLoggingEnabled() {
return loggingEnabled;
}
public void setRecording(boolean recording) {
this.recording = recording;
}
public void setCamera(Camera serviceCamera) {
this.serviceCamera = serviceCamera;
}
public Camera getCamera() {
return serviceCamera;
}
public void setCameraPreview(CameraPreview cameraPreview) {
this.cameraPreview = cameraPreview;
}
public CameraPreview getCameraPreview() {
return this.cameraPreview;
}
}
public class CameraPreview extends SurfaceView implements
SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private static final String TAG = "CameraPreview";
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (mHolder.getSurface() == null) {
return;
}
try {
mCamera.stopPreview();
} catch (Exception e) {
}
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e) {
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
public class MainActivity extends Activity {
public String TAG = "DE-MainActivity";
ImageView mRecordView;
ImageView mMenuButtonView;
LinearLayout mMenuView;
TextView mVideosTextView;
TextView mSettingsTextView;
private Camera mCamera;
private CameraPreview mPreview;
GlobalState mAppState = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAppState = (GlobalState) getApplicationContext();
if (mAppState.isLoggingEnabled()) {
Log.v(TAG, "Activity: onCreate");
}
mCamera = mAppState.getCamera();
if (mAppState.getCameraPreview() == null) {
mPreview = new CameraPreview(this, mCamera);
mAppState.setCameraPreview(mPreview);
}
FrameLayout preview = (FrameLayout) findViewById(R.id.fl_camera);
preview.addView(mPreview);
mMenuView = (LinearLayout) findViewById(R.id.ll_menu_list);
mVideosTextView = (TextView) findViewById(R.id.tv_menu_item_videos);
mSettingsTextView = (TextView) findViewById(R.id.tv_menu_item_settings);
mRecordView = (ImageView) findViewById(R.id.iv_record);
mRecordView.setImageResource(R.drawable.btn_not_recording);
mRecordView.setAlpha((float) 0.5);
mRecordView.bringToFront();
mRecordView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!mAppState.isRecording()) {
mRecordView.setImageResource(R.drawable.btn_recording);
mRecordView.setAlpha((float) 0.3);
startService(new Intent(MainActivity.this,
RecorderService.class));
} else {
mRecordView.setImageResource(R.drawable.btn_not_recording);
mRecordView.setAlpha((float) 0.5);
stopService(new Intent(MainActivity.this,
RecorderService.class));
}
}
});
mMenuButtonView = (ImageView) findViewById(R.id.iv_menu);
mMenuButtonView.setImageResource(R.drawable.btn_menu);
mMenuButtonView.setAlpha((float) 0.5);
mMenuButtonView.bringToFront();
mMenuButtonView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mMenuView.getVisibility() == View.VISIBLE) {
mMenuView.setVisibility(View.INVISIBLE);
} else {
mMenuView.setVisibility(View.VISIBLE);
}
}
});
mSettingsTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mAppState.isLoggingEnabled())
Log.v(TAG, "settings clicked!");
}
});
mVideosTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mAppState.isLoggingEnabled())
Log.v(TAG, "videos clicked!");
}
});
}
#Override
protected void onDestroy() {
if (mAppState.isLoggingEnabled())
Log.v(TAG, "APPLICATION EXIT!");
if (mCamera != null) {
mCamera.release(); // release the camera for other applications
mCamera = null;
}
super.onDestroy();
}
public boolean onCreateOptionsMenu(Menu menu) {
if (mMenuView.getVisibility() == View.VISIBLE) {
mMenuView.setVisibility(View.INVISIBLE);
} else {
mMenuView.setVisibility(View.VISIBLE);
}
return false;
}
}
public class RecorderService extends Service {
private static final String TAG = "RecorderService";
private static Camera mServiceCamera;
private MediaRecorder mMediaRecorder;
private GlobalState mAppState;
public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;
#Override
public void onCreate() {
mAppState = (GlobalState) getApplicationContext();
mServiceCamera = mAppState.getCamera();
if (mAppState.isLoggingEnabled())
Log.v(TAG, "onCreate");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if (!mAppState.isRecording()) {
if (prepareVideoRecorder()) {
mMediaRecorder.start();
mAppState.setRecording(true);
} else {
releaseMediaRecorder();
}
}
return 5;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
if (mAppState.isLoggingEnabled())
Log.v(TAG, "onDestroy");
// stop recording and release camera
mMediaRecorder.stop(); // stop the recording
releaseMediaRecorder(); // release the MediaRecorder object
mServiceCamera.lock(); // take camera access back from MediaRecorder
mAppState.setRecording(false);
super.onDestroy();
}
private void releaseMediaRecorder() {
if (mMediaRecorder != null) {
mMediaRecorder.reset(); // clear recorder configuration
mMediaRecorder.release(); // release the recorder object
mMediaRecorder = null;
}
}
/** Create a file Uri for saving an image or video */
private static Uri getOutputMediaFileUri(int type) {
return Uri.fromFile(getOutputMediaFile(type));
}
/** Create a File for saving an image or video */
private static File getOutputMediaFile(int type) {
File mediaStorageDir = new File(
Environment.getExternalStorageDirectory(), "DashEyeApp");
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d("MyCameraApp", "failed to create directory");
return null;
}
}
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
.format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator
+ "IMG_" + timeStamp + ".jpg");
} else if (type == MEDIA_TYPE_VIDEO) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator
+ "VID_" + timeStamp + ".mp4");
} else {
return null;
}
return mediaFile;
}
private boolean prepareVideoRecorder() {
mMediaRecorder = new MediaRecorder();
mServiceCamera.unlock();
mMediaRecorder.setCamera(mServiceCamera);
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setProfile(CamcorderProfile
.get(CamcorderProfile.QUALITY_HIGH));
mMediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO)
.toString());
// Step 5: Set the preview output
// mMediaRecorder.setPreviewDisplay(mAppState.getCameraPreview().getHolder().getSurface());
try {
mMediaRecorder.prepare();
} catch (IllegalStateException e) {
Log.d(TAG,
"IllegalStateException preparing MediaRecorder: "
+ e.getMessage());
releaseMediaRecorder();
return false;
} catch (IOException e) {
Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage());
releaseMediaRecorder();
return false;
}
return true;
}
}
I am sorry for the wall of text and I greatly appreciate any help!
my solution is fine, try it:
Service android:
import java.util.Calendar;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.view.Gravity;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
public class BackgroundVideoRecorder extends Service implements
SurfaceHolder.Callback {
private WindowManager windowManager;
private SurfaceView surfaceView;
private Camera camera = null;
private MediaRecorder mediaRecorder = null;
int contTime = 0, duracaoGravacao = 30; //interval in seconds to record video
private class thread implements Runnable {
public void run() {
contTime++;
if (contTime >= duracaoGravacao) {
StopService();
}
tick_Handler.postDelayed(tick_thread, 1000);
}
}
Handler tick_Handler;
thread tick_thread;
Preferences pref;
#Override
public void onCreate() {
windowManager = (WindowManager) this
.getSystemService(Context.WINDOW_SERVICE);
surfaceView = new SurfaceView(this);
LayoutParams layoutParams = new WindowManager.LayoutParams(1, 1,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
windowManager.addView(surfaceView, layoutParams);
surfaceView.getHolder().addCallback(this);
tick_Handler = new Handler();
tick_thread = new thread();
VIDEO_RECORDER_FOLDER = new _Path().getPathVideo();
}
#Override
public void onStart(Intent intent, int startId) {
tick_Handler.post(tick_thread);
}
// Method called right after Surface created (initializing and starting
// MediaRecorder)
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
boolean found = false;
int i = 0;
try {
for (i = 0; i < Camera.getNumberOfCameras(); i++) {
Camera.CameraInfo newInfo = new Camera.CameraInfo();
Camera.getCameraInfo(i, newInfo);
if (newInfo.facing == CameraInfo.CAMERA_FACING_FRONT) {
found = true;
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
if (found) {
camera = Camera.open(i);
} else {
camera = Camera.open();
}
Calendar lCDateTime = Calendar.getInstance();
String t = String.valueOf(lCDateTime.getTimeInMillis());
nomeArquivo = "hire_me_now_" + t + ".mp4";
nomeArquivo = nomeArquivo.replace(" ", "_").replace(":", "_")
.replace("-", "_");
String caminhoArquivo = VIDEO_RECORDER_FOLDER + "/" + nomeArquivo;
mediaRecorder = new MediaRecorder();
camera.unlock();
mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface());
mediaRecorder.setCamera(camera);
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mediaRecorder.setProfile(CamcorderProfile
.get(CamcorderProfile.QUALITY_QVGA));
mediaRecorder.setVideoFrameRate(15);
mediaRecorder.setOutputFile(caminhoArquivo);
try {
mediaRecorder.prepare();
} catch (Exception e) {
e.printStackTrace();
}
mediaRecorder.start();
}
// Stop recording and remove SurfaceView
#Override
public void onDestroy() {
mediaRecorder.stop();
mediaRecorder.reset();
mediaRecorder.release();
camera.lock();
camera.release();
windowManager.removeView(surfaceView);
}
protected void StopService() {
try {
this.stopSelf();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format,
int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
It turns out that Android doesn't like that the preview gets destroyed (when, for example, the user hits the "Home" button) so it cuts out video recording.
The workaround to this is using WindowManager to set an overlay and, when the user hits the "Home" button, resize it to 1x1. I found the solution here. Many thanks to cman!
it can be done but from API level 23 you will need ask for camera permissions, you can refer to this answer https://stackoverflow.com/a/49919386/4604234
I have a simple app.On Button click from the Main activity,I call another activity which just shows a preview from the front Camera.I observe that it takes around 4 seconds for the second activity to launch.I think its because of the time it takes to render the camera preview element in the second activity.This is way too long and I am looking for a way to reduce the rendering time of the second activity.Here is my code:
Main.java:
public class Main extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btnLaunchCamera = (Button) findViewById(R.id.launchCamera);
btnLaunchCamera.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(Main.this, Cam.class));
}
});
}
}
Cam.java
public class Cam extends Activity {
private static int cameraId = 0;
private Camera camera;
//Adding for camera preview
public static FrameLayout preview;
public static CameraPreview mPreview;
Context context;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e("Cam","Inside onCreate");
setContentView(R.layout.cam);
context = getApplicationContext();
// Do we have a camera?
try {
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
Toast.makeText(this, "No camera on this device", Toast.LENGTH_LONG).show();
} else {
cameraId = AppFunctions.findFrontFacingCamera();
if (cameraId < 0) {
Toast.makeText(this, "No front facing camera found.", Toast.LENGTH_LONG).show();
} else {
try {
camera = Camera.open(cameraId);
Log.d("Cam","camera id" + cameraId);
} catch (Exception e) {
e.printStackTrace();
}
try {
mPreview = new CameraPreview(this, camera);
preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (Exception e3) {
e3.printStackTrace();
}
}
#Override
protected void onPause() {
super.onPause();
Log.e("Cam","Inside onPause");
try {
if (camera != null) {
camera.release();
camera = null;
preview.removeView(mPreview);
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
protected void onResume() {
super.onResume();
Log.e("Cam","Inside onResume");
try {
if(camera ==null) {
Log.d("Cam","in resume,camera is null");
try {
camera = android.hardware.Camera.open(cameraId);
mPreview = new CameraPreview(this, camera);
preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.e("Cam","Inside onDestroy");
if (camera != null) {
try {
camera.release();
camera = null;
preview.removeView(mPreview);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
cam.xml
<FrameLayout
android:id="#+id/camera_preview"
android:layout_width="453dp"
android:layout_height="400dp"
android:layout_marginLeft="250dp"
android:layout_marginTop="30dp"
android:padding="5dp" >
</FrameLayout>
CameraPreview.java
/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
final static String TAG = "CameraPreview";
public CameraPreview(Context context, Camera camera) {
super(context);
try {
mCamera = camera;
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
} catch (Exception e) {
e.printStackTrace();
}
}
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(holder);
mCamera.setDisplayOrientation(0);
mCamera.startPreview();
} catch (Exception e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG,"Inside SurfaceDestroyed");
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
Please assist me on how I can reduce the rendering time for the second activity.Any help would be appreciated.Many thanks !