package io.evercam.androidapp.video;
import android.app.ActionBar;
import android.app.ActionBar.OnNavigationListener;
import android.app.Activity;
import android.app.TaskStackBuilder;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.support.v4.app.NavUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.logentries.android.AndroidLogger;
import com.squareup.picasso.Picasso;
import org.freedesktop.gstreamer.GStreamer;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.RejectedExecutionException;
import io.evercam.Camera;
import io.evercam.androidapp.CamerasActivity;
import io.evercam.androidapp.EvercamPlayApplication;
import io.evercam.androidapp.FeedbackActivity;
import io.evercam.androidapp.MainActivity;
import io.evercam.androidapp.ParentActivity;
import io.evercam.androidapp.R;
import io.evercam.androidapp.ViewCameraActivity;
import io.evercam.androidapp.authentication.EvercamAccount;
import io.evercam.androidapp.custom.CameraListAdapter;
import io.evercam.androidapp.custom.CustomToast;
import io.evercam.androidapp.custom.CustomedDialog;
import io.evercam.androidapp.custom.ProgressView;
import io.evercam.androidapp.dal.DbCamera;
import io.evercam.androidapp.dto.AppData;
import io.evercam.androidapp.dto.AppUser;
import io.evercam.androidapp.dto.EvercamCamera;
import io.evercam.androidapp.feedback.KeenHelper;
import io.evercam.androidapp.feedback.ShortcutFeedbackItem;
import io.evercam.androidapp.feedback.StreamFeedbackItem;
import io.evercam.androidapp.recordings.RecordingWebActivity;
import io.evercam.androidapp.tasks.CaptureSnapshotRunnable;
import io.evercam.androidapp.tasks.DeleteCameraTask;
import io.evercam.androidapp.utils.Commons;
import io.evercam.androidapp.utils.Constants;
import io.evercam.androidapp.utils.EnumConstants.DeleteType;
import io.evercam.androidapp.utils.PrefsManager;
import io.evercam.androidapp.utils.PropertyReader;
import io.evercam.androidapp.video.SnapshotManager.FileType;
import io.keen.client.java.KeenClient;
public class VideoActivity extends ParentActivity implements SurfaceHolder.Callback
{
public static EvercamCamera evercamCamera;
private final static String TAG = "VideoActivity";
private String liveViewCameraId = "";
private boolean showImagesVideo = false;
/**
* UI elements
*/
private SurfaceView surfaceView;
private SurfaceHolder surfaceHolder;
private ProgressView progressView = null;
private TextView offlineTextView;
private TextView timeCountTextView;
private RelativeLayout imageViewLayout;
private ImageView imageView;
private ImageView mediaPlayerView;
private ImageView snapshotMenuView;
private Animation fadeInAnimation = null;
private long downloadStartCount = 0;
private long downloadEndCount = 0;
private BrowseJpgTask browseJpgTask;
private boolean isProgressShowing = true;
static boolean enableLogs = true;
private final int MIN_SLEEP_INTERVAL = 200; // interval between two requests of
// images
private final int ADJUSTMENT_INTERVAL = 10; // how much milli seconds to increment
// or decrement on image failure or
// success
private int sleepInterval = MIN_SLEEP_INTERVAL + 290; // starting image
// interval
private boolean startDownloading = false; // start making requests soon
// after the image is received
// first time. Until first image
// is not received, do not make
// requests
private static long latestStartImageTime = 0; // time of the latest request
// that has been made
private int successiveFailureCount = 0; // how much successive image
// requests have failed
private Boolean isShowingFailureMessage = false;
private Boolean optionsActivityStarted = false;
public static String startingCameraID;
private int defaultCameraIndex;
private boolean paused = false;
private boolean isPlayingJpg = false;// If true, stop trying video
// URL for reconnecting.
private boolean isJpgSuccessful = false; //Whether or not the JPG view ever
//got successfully played
private boolean end = false; // whether to end this activity or not
private boolean editStarted = false;
private boolean feedbackStarted = false;
private boolean recordingsStarted = false;
private Handler timerHandler = new Handler();
private Thread timerThread;
private Runnable timerRunnable;
private TimeCounter timeCounter;
private Date startTime;
private AndroidLogger logger;
private KeenClient client;
private String username = "";
/**
* Gstreamer
*/
private long native_app_data;
private native void nativeRequestSample(String format); // supported values are png and jpeg
private native void nativeSetUri(String uri, int connectionTimeout);
private native void nativeInit(); // Initialize native code, build pipeline, etc
private native void nativeFinalize(); // Destroy pipeline and shutdown native code
private native void nativePlay(); // Set pipeline to PLAYING
private native void nativePause(); // Set pipeline to PAUSED
private static native boolean nativeClassInit(); // Initialize native class: cache Method IDs for callbacks
private native void nativeSurfaceInit(Object surface);
private native void nativeSurfaceFinalize();
private long native_custom_data; // Native code will use this to keep private data
private final int TCP_TIMEOUT = 10 * 1000000; // 10 seconds in micro seconds
static
{
System.loadLibrary("gstreamer_android");
System.loadLibrary("evercam");
nativeClassInit();
}
@Override
public void onCreate(Bundle savedInstanceState)
{
try
{
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
initAnalyticsObjects();
readShortcutCameraId();
if(!liveViewCameraId.isEmpty())
{
startingCameraID = liveViewCameraId;
liveViewCameraId = "";
}
launchSleepTimer();
setDisplayOriention();
if(this.getActionBar() != null)
{
this.getActionBar().setDisplayHomeAsUpEnabled(true);
}
/**
* Init Gstreamer
*/
try
{
GStreamer.init(this);
} catch (Exception e)
{
Log.e(TAG, e.getLocalizedMessage());
EvercamPlayApplication.sendCaughtException(this, e);
finish();
return;
}
nativeInit();
setContentView(R.layout.video_activity_layout);
initialPageElements();
checkIsShortcutCameraExists();
startPlay();
}
catch(OutOfMemoryError e)
{
Log.e(TAG, e.toString() + "-::OOM::-" + Log.getStackTraceString(e));
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
// Here actually no matter what result is returned, all restart video
// play, but keep the verbose code for future extension.
if(requestCode == Constants.REQUEST_CODE_PATCH_CAMERA)
{
// Restart video playing no matter the patch is success or not.
if(resultCode == Constants.RESULT_TRUE)
{
startPlay();
}
else
{
startPlay();
}
}
else
// If back from view camera or feedback
{
startPlay();
}
}
@Override
public void onResume()
{
try
{
super.onResume();
this.paused = false;
editStarted = false;
feedbackStarted = false;
recordingsStarted = false;
if(optionsActivityStarted)
{
optionsActivityStarted = false;
showProgressView();
startDownloading = false;
this.paused = false;
this.end = false;
this.isShowingFailureMessage = false;
latestStartImageTime = SystemClock.uptimeMillis();
if(browseJpgTask == null)
{
// ignore if image thread is null
}
else if(browseJpgTask.getStatus() != AsyncTask.Status.RUNNING)
{
browseJpgTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
else if(browseJpgTask.getStatus() == AsyncTask.Status.FINISHED)
{
createBrowseJpgTask();
}
}
}
catch(OutOfMemoryError e)
{
Log.e(TAG, e.toString() + "-::OOM::-" + Log.getStackTraceString(e));
}
catch(Exception e)
{
Log.e(TAG, e.toString());
sendToMint(e);
}
}
// When activity gets focused again
@Override
public void onRestart()
{
try
{
super.onRestart();
paused = false;
end = false;
editStarted = false;
feedbackStarted = false;
recordingsStarted = false;
if(optionsActivityStarted)
{
setCameraForPlaying(evercamCamera);
createPlayer(evercamCamera);
}
}
catch(OutOfMemoryError e)
{
Log.e(TAG, e.toString() + "-::OOM::-" + Log.getStackTraceString(e));
}
}
@Override
public void onPause()
{
super.onPause();
if(!optionsActivityStarted)
{
this.paused = true;
}
}
@Override
public void onStop()
{
super.onStop();
releasePlayer();
end = true;
if(!optionsActivityStarted)
{
if(browseJpgTask != null)
{
this.paused = true;
}
// Do not finish if user get into edit camera screen, feedback screen, or recording
if(!editStarted && !feedbackStarted && !recordingsStarted)
{
this.finish();
}
}
if(timeCounter != null)
{
timeCounter.stop();
timeCounter = null;
}
}
@Override
protected void onDestroy()
{
nativeFinalize();
super.onDestroy();
}
@Override
public boolean dispatchTouchEvent(MotionEvent event)
{
// TODO: Reset the timer to keep screen awake
launchSleepTimer();
return super.dispatchTouchEvent(event);
}
private void checkIsShortcutCameraExists()
{
//It will refill global camera list in isUserLogged()
if(MainActivity.isUserLogged(this))
{
username = AppData.defaultUser.getUsername();
if(AppData.evercamCameraList.size() > 0)
{
boolean cameraIsAccessible = false;
for(EvercamCamera camera : AppData.evercamCameraList)
{
if(camera.getCameraId().equals(startingCameraID))
{
cameraIsAccessible = true;
break;
}
}
if(!cameraIsAccessible)
{
EvercamAccount evercamAccount = new EvercamAccount(this);
AppUser matchedUser = null;
ArrayList<AppUser> userList = evercamAccount.retrieveUserList();
for(AppUser appUser : userList)
{
if(!appUser.getUsername().equals(username))
{
ArrayList<EvercamCamera> cameraList = new DbCamera(this).getCamerasByOwner(appUser.getUsername(), 500);
for(EvercamCamera camera : cameraList)
{
if(camera.getCameraId().equals(startingCameraID))
{
matchedUser = appUser;
break;
}
}
}
}
if(matchedUser != null)
{
CustomToast.showSuperToastShort(this, getString(R
.string.msg_switch_account) + " - " + matchedUser.getUsername());
evercamAccount.updateDefaultUser(matchedUser.getEmail());
checkIsShortcutCameraExists();
}
else
{
CustomToast.showSuperToastShort(this, getString(R
.string.msg_can_not_access_camera) + " - " + username);
new ShortcutFeedbackItem(this, AppData.defaultUser.getUsername(), startingCameraID,
ShortcutFeedbackItem.ACTION_TYPE_USE, ShortcutFeedbackItem.RESULT_TYPE_FAILED)
.sendToKeenIo(client);
navigateBackToCameraList();
}
}
else
{
new ShortcutFeedbackItem(this, AppData.defaultUser.getUsername(), startingCameraID,
ShortcutFeedbackItem.ACTION_TYPE_USE, ShortcutFeedbackItem.RESULT_TYPE_SUCCESS)
.sendToKeenIo(client);;
}
}
}
else
{
//If no account signed in
startActivity(new Intent(this, MainActivity.class));
finish();
}
}
private void launchSleepTimer()
{
try
{
if(timerThread != null)
{
timerThread = null;
timerHandler.removeCallbacks(timerRunnable);
}
final int sleepTime = getSleepTime();
if(sleepTime != 0)
{
timerRunnable = new Runnable()
{
@Override
public void run()
{
VideoActivity.this.getWindow().clearFlags(WindowManager.LayoutParams
.FLAG_KEEP_SCREEN_ON);
}
};
timerThread = new Thread()
{
@Override
public void run()
{
timerHandler.postDelayed(timerRunnable, sleepTime);
}
};
timerThread.start();
}
}
catch(Exception e)
{
// Catch this exception and send by Google Analytics
// This should not influence user using the app
EvercamPlayApplication.sendCaughtException(this, e);
}
}
private void initAnalyticsObjects()
{
String logentriesToken = getPropertyReader()
.getPropertyStr(PropertyReader.KEY_LOGENTRIES_TOKEN);
if(!logentriesToken.isEmpty())
{
logger = AndroidLogger.getLogger(getApplicationContext(), logentriesToken, false);
}
client = KeenHelper.getClient(this);
}
private int getSleepTime()
{
final String VALUE_NEVER = "0";
String valueString = PrefsManager.getSleepTimeValue(this);
if(!valueString.equals(VALUE_NEVER))
{
return Integer.valueOf(valueString) * 1000;
}
else
{
return 0;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.video_menu, menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu)
{
MenuItem viewItem = menu.findItem(R.id.video_menu_view_camera);
MenuItem shortcutItem = menu.findItem(R.id.video_menu_create_shortcut);
if(evercamCamera != null)
{
//Hide 'Edit' option for shared camera
if(evercamCamera.canEdit())
{
viewItem.setVisible(true);
}
else
{
viewItem.setVisible(true);
}
if(evercamCamera.isOffline())
{
shortcutItem.setVisible(false);
}
else
{
shortcutItem.setVisible(true);
}
}
else
{
Log.e(TAG, "EvercamCamera is null");
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
int itemId = item.getItemId();
try
{
if(itemId == R.id.video_menu_delete_camera)
{
CustomedDialog.getConfirmRemoveDialog(VideoActivity.this, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface warningDialog, int which)
{
if(evercamCamera.canDelete())
{
new DeleteCameraTask(evercamCamera.getCameraId(), VideoActivity.this, DeleteType.DELETE_OWNED).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
else
{
new DeleteCameraTask(evercamCamera.getCameraId(), VideoActivity.this, DeleteType.DELETE_SHARE).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
}, R.string.msg_confirm_remove_camera).show();
}
else if(itemId == R.id.video_menu_view_camera)
{
editStarted = true;
Intent viewIntent = new Intent(VideoActivity.this, ViewCameraActivity.class);
startActivityForResult(viewIntent, Constants.REQUEST_CODE_VIEW_CAMERA);
}
else if(itemId == android.R.id.home)
{
navigateBackToCameraList();
}
else if(itemId == R.id.video_menu_feedback)
{
feedbackStarted = true;
Intent feedbackIntent = new Intent(VideoActivity.this, FeedbackActivity.class);
feedbackIntent.putExtra(Constants.BUNDLE_KEY_CAMERA_ID, evercamCamera.getCameraId());
startActivityForResult(feedbackIntent, Constants.REQUEST_CODE_FEEDBACK);
}
else if(itemId == R.id.video_menu_view_snapshots)
{
SnapshotManager.showSnapshotsInGalleryForCamera(this, evercamCamera.getCameraId());
}
else if(itemId == R.id.video_menu_create_shortcut)
{
Bitmap bitmap = getBitmapFromImageView(imageView);
HomeShortcut.create(getApplicationContext(), evercamCamera, bitmap);
CustomToast.showSuperToastShort(this, R.string.msg_shortcut_created);
EvercamPlayApplication.sendEventAnalytics(this, R.string.category_shortcut, R
.string.action_shortcut_create, R.string.label_shortcut_create);
new ShortcutFeedbackItem(this, AppData.defaultUser.getUsername(), evercamCamera.getCameraId(),
ShortcutFeedbackItem.ACTION_TYPE_CREATE, ShortcutFeedbackItem.RESULT_TYPE_SUCCESS)
.sendToKeenIo(client);
getMixpanel().sendEvent(R.string.mixpanel_event_create_shortcut, new JSONObject()
.put("Camera ID", evercamCamera.getCameraId()));
}
else if(itemId == R.id.video_menu_view_recordings)
{
recordingsStarted = true;
Intent recordingIntent = new Intent(this, RecordingWebActivity.class);
recordingIntent.putExtra(Constants.BUNDLE_KEY_CAMERA_ID, evercamCamera.getCameraId());
startActivity(recordingIntent);
}
}
catch(OutOfMemoryError e)
{
Log.e(TAG, e.toString() + "-::OOM::-" + Log.getStackTraceString(e));
}
catch(Exception e)
{
Log.e(TAG, e.toString() + "::" + Log.getStackTraceString(e));
sendToMint(e);
}
return true;
}
@Override
public void onBackPressed()
{
navigateBackToCameraList();
}
private void navigateBackToCameraList()
{
if(CamerasActivity.activity == null)
{
if (android.os.Build.VERSION.SDK_INT >= 16)
{
Intent upIntent = NavUtils.getParentActivityIntent(this);
TaskStackBuilder.create(this)
.addNextIntentWithParentStack(upIntent)
.startActivities();
}
}
finish();
}
private void readShortcutCameraId()
{
Intent liveViewIntent = this.getIntent();
if(liveViewIntent != null && liveViewIntent.getExtras() != null)
{
EvercamPlayApplication.sendEventAnalytics(this, R.string.category_shortcut,
R.string.action_shortcut_use, R.string.label_shortcut_use);
try
{
if(evercamCamera != null)
{
getMixpanel().sendEvent(R.string.mixpanel_event_use_shortcut, new JSONObject().put("Camera ID", evercamCamera.getCameraId()));
}
}
catch(JSONException e)
{
e.printStackTrace();
}
liveViewCameraId = liveViewIntent.getExtras().getString(HomeShortcut.KEY_CAMERA_ID, "");
}
}
private void startPlay()
{
sendToLogentries(logger, "User: " + username + " is viewing camera: " + startingCameraID);
paused = false;
end = false;
checkNetworkStatus();
loadCamerasToActionBar();
}
public static boolean startPlayingVideoForCamera(Activity activity, String cameraId)
{
startingCameraID = cameraId;
Intent intent = new Intent(activity, VideoActivity.class);
activity.startActivityForResult(intent, Constants.REQUEST_CODE_DELETE_CAMERA);
return false;
}
private void setCameraForPlaying(EvercamCamera evercamCamera)
{
try
{
VideoActivity.evercamCamera = evercamCamera;
showImagesVideo = false;
downloadStartCount = 0;
downloadEndCount = 0;
isProgressShowing = false;
startDownloading = false;
latestStartImageTime = 0;
successiveFailureCount = 0;
isShowingFailureMessage = false;
optionsActivityStarted = false;
mediaPlayerView.setVisibility(View.GONE);
snapshotMenuView.setVisibility(View.GONE);
paused = false;
end = false;
surfaceView.setVisibility(View.GONE);
imageView.setVisibility(View.VISIBLE);
showProgressView();
loadImageFromCache(VideoActivity.evercamCamera);
if(!evercamCamera.isOffline())
{
startDownloading = true;
}
showProgressView();
}
catch(Exception e)
{
Log.e(TAG, e.toString() + "::" + Log.getStackTraceString(e));
sendToMint(e);
EvercamPlayApplication.sendCaughtException(this, e);
CustomedDialog.showUnexpectedErrorDialog(VideoActivity.this);
}
}
// Loads image from cache. First image gets loaded correctly and hence we
// can start making requests concurrently as well
public void loadImageFromCache(EvercamCamera camera)
{
imageView.setImageDrawable(null);
// Bitmap cacheBitmap = EvercamFile.loadBitmapForCamera(this, cameraId);
// imageView.setImageBitmap(cacheBitmap);
if(camera.hasThumbnailUrl())
{
Picasso.with(this).load(camera.getThumbnailUrl()).into(imageView);
}
else
{
Log.e(TAG, camera.toString());
}
}
private void startMediaPlayerAnimation()
{
if(fadeInAnimation != null)
{
fadeInAnimation.cancel();
fadeInAnimation.reset();
snapshotMenuView.clearAnimation();
mediaPlayerView.clearAnimation();
}
fadeInAnimation = AnimationUtils.loadAnimation(VideoActivity.this, R.anim.fadein);
fadeInAnimation.setAnimationListener(new Animation.AnimationListener()
{
@Override
public void onAnimationStart(Animation animation)
{
// TODO Auto-generated method stub
}
@Override
public void onAnimationRepeat(Animation animation)
{
// TODO Auto-generated method stub
}
@Override
public void onAnimationEnd(Animation animation)
{
if(!paused)
{
mediaPlayerView.setVisibility(View.GONE);
snapshotMenuView.setVisibility(View.GONE);
}
else
{
mediaPlayerView.setVisibility(View.VISIBLE);
if(surfaceView.getVisibility() != View.VISIBLE)
{
snapshotMenuView.setVisibility(View.VISIBLE);
}
}
int orientation = VideoActivity.this.getResources().getConfiguration().orientation;
if(!paused && orientation == Configuration.ORIENTATION_LANDSCAPE)
{
VideoActivity.this.getActionBar().hide();
}
}
});
mediaPlayerView.startAnimation(fadeInAnimation);
snapshotMenuView.startAnimation(fadeInAnimation);
}
/**
* **********
* Surface
* ***********
*/
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder)
{
Log.d("GStreamer", "Surface created: " + surfaceHolder.getSurface());
nativeSurfaceInit(surfaceHolder.getSurface());
}
@Override
public void surfaceChanged(SurfaceHolder surfaceholder, int format, int width, int height)
{
Log.d("GStreamer", "Surface changed to format " + format + " width " + width + " height " + height);
onMediaSizeChanged(width, height);
nativeSurfaceInit(surfaceholder.getSurface());
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceholder)
{
Log.d("GStreamer", "Surface destroyed");
nativeSurfaceFinalize();
}
/**
* **********
* Player
* ***********
*/
private String createUri(EvercamCamera camera)
{
String uri = "rtsp://" + camera.getUsername() + ":" + camera.getPassword() + "@"
+ camera.getExternalRtspUrl().replaceFirst("rtsp://","");
return uri;
}
private void createPlayer(EvercamCamera camera)
{
startTime = new Date();
if(camera.hasRtspUrl())
{
Log.e(TAG, "uri " + createUri(camera));
nativeSetUri(createUri(camera), TCP_TIMEOUT);
play(camera);
}
else
{
//If no RTSP URL exists, start JPG view straight away
showImagesVideo = true;
createBrowseJpgTask();
}
}
private void play(EvercamCamera camera)
{
nativePlay();
}
private void releasePlayer()
{
nativePause();
}
private void restartPlay()
{
nativePlay();
}
private void pausePlayer()
{
nativePause();
}
// when screen gets rotated
@Override
public void onConfigurationChanged(Configuration newConfig)
{
try
{
super.onConfigurationChanged(newConfig);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
int orientation = newConfig.orientation;
if(orientation == Configuration.ORIENTATION_PORTRAIT)
{
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
this.getActionBar().show();
}
else
{
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
if(!paused && !end && !isProgressShowing) this.getActionBar().hide();
else this.getActionBar().show();
}
this.invalidateOptionsMenu();
}
catch(Exception e)
{
EvercamPlayApplication.sendCaughtException(this, e);
sendToMint(e);
}
}
private void showMediaFailureDialog()
{
CustomedDialog.getCanNotPlayDialog(this, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
VideoActivity.this.getActionBar().show();
paused = true;
isShowingFailureMessage = false;
dialog.dismiss();
hideProgressView();
// timeCounter.stop();
}
}).show();
isShowingFailureMessage = true;
showImagesVideo = false;
}
private void setDisplayOriention()
{
/** Force landscape if it's enabled in settings */
boolean forceLandscape = PrefsManager.isForceLandscape(this);
if(forceLandscape)
{
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
int orientation = this.getResources().getConfiguration().orientation;
if(orientation == Configuration.ORIENTATION_PORTRAIT)
{
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
}
else
{
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
private void checkNetworkStatus()
{
if(!Commons.isOnline(this))
{
CustomedDialog.getNoInternetDialog(this, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
paused = true;
dialog.dismiss();
hideProgressView();
}
}).show();
}
}
private void initialPageElements()
{
imageViewLayout = (RelativeLayout) this.findViewById(R.id.camera_view_layout);
imageView = (ImageView) this.findViewById(R.id.img_camera1);
mediaPlayerView = (ImageView) this.findViewById(R.id.ivmediaplayer1);
snapshotMenuView = (ImageView) this.findViewById(R.id.player_savesnapshot);
surfaceView = (SurfaceView) findViewById(R.id.surface_view);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
progressView = ((ProgressView) imageViewLayout.findViewById(R.id.ivprogressspinner1));
progressView.setMinimumWidth(mediaPlayerView.getWidth());
progressView.setMinimumHeight(mediaPlayerView.getHeight());
progressView.canvasColor = Color.TRANSPARENT;
isProgressShowing = true;
progressView.setVisibility(View.VISIBLE);
offlineTextView = (TextView) findViewById(R.id.offline_text_view);
timeCountTextView = (TextView) findViewById(R.id.time_text_view);
/** The click listener for pause/play button */
mediaPlayerView.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
if(end)
{
Toast.makeText(VideoActivity.this, R.string.msg_try_again,
Toast.LENGTH_SHORT).show();
return;
}
if(isProgressShowing) return;
if(paused) // video is currently paused. Now we need to
// resume it.
{
timeCountTextView.setVisibility(View.VISIBLE);
showProgressView();
mediaPlayerView.setImageBitmap(null);
mediaPlayerView.setVisibility(View.VISIBLE);
snapshotMenuView.setVisibility(View.VISIBLE);
mediaPlayerView.setImageResource(android.R.drawable.ic_media_pause);
startMediaPlayerAnimation();
//If playing url is not null, resume rtsp stream
if(evercamCamera != null && !evercamCamera.getExternalRtspUrl().isEmpty())
{
restartPlay();
}
//Otherwise restart jpg view
else
{
//Don't need to do anything because image thread is listening
}
paused = false;
}
else
// video is currently playing. Now we need to pause video
{
timeCountTextView.setVisibility(View.GONE);
mediaPlayerView.clearAnimation();
snapshotMenuView.clearAnimation();
if(fadeInAnimation != null && fadeInAnimation.hasStarted() &&
!fadeInAnimation.hasEnded())
{
fadeInAnimation.cancel();
fadeInAnimation.reset();
}
mediaPlayerView.setVisibility(View.VISIBLE);
snapshotMenuView.setVisibility(View.VISIBLE);
mediaPlayerView.setImageBitmap(null);
mediaPlayerView.setImageResource(android.R.drawable.ic_media_play);
pausePlayer();
paused = true; // mark the images as paused. Do not stop
// threads, but do not show the images
// showing up
}
}
});
/**
* The click listener of camera live view layout, including both RTSP and JPG view
* Once clicked, if camera view is playing, show the pause menu, otherwise do nothing.
*/
imageViewLayout.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
if(end)
{
Toast.makeText(VideoActivity.this, R.string.msg_try_again,
Toast.LENGTH_SHORT).show();
return;
}
if(isProgressShowing) return;
if(!paused && !end) // video is currently playing. Show pause button
{
if(mediaPlayerView.getVisibility() == View.VISIBLE)
{
mediaPlayerView.setVisibility(View.GONE);
snapshotMenuView.setVisibility(View.GONE);
mediaPlayerView.clearAnimation();
snapshotMenuView.clearAnimation();
fadeInAnimation.reset();
}
else
{
VideoActivity.this.getActionBar().show();
mediaPlayerView.setImageResource(android.R.drawable.ic_media_pause);
mediaPlayerView.setVisibility(View.VISIBLE);
snapshotMenuView.setVisibility(View.VISIBLE);
startMediaPlayerAnimation();
}
}
}
});
snapshotMenuView.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
//Hide pause/snapshot menu if the live view is not paused
if(!paused)
{
mediaPlayerView.setVisibility(View.GONE);
snapshotMenuView.setVisibility(View.GONE);
mediaPlayerView.clearAnimation();
snapshotMenuView.clearAnimation();
fadeInAnimation.reset();
}
if(imageView.getVisibility() == View.VISIBLE)
{
Bitmap bitmap = getBitmapFromImageView(imageView);
processSnapshot(bitmap, FileType.JPG);
}
else if(surfaceView.getVisibility() == View.VISIBLE)
{
nativeRequestSample("jpeg");
}
}
});
}
private Bitmap getBitmapFromImageView(ImageView imageView)
{
final Bitmap bitmap;
if(imageView.getDrawable() instanceof BitmapDrawable)
{
bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
}
else
{
Drawable drawable = imageView.getDrawable();
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.draw(canvas);
}
return bitmap;
}
// Hide progress view
void hideProgressView()
{
imageViewLayout.findViewById(R.id.ivprogressspinner1).setVisibility(View.GONE);
isProgressShowing = false;
}
void showProgressView()
{
progressView.canvasColor = Color.TRANSPARENT;
progressView.setVisibility(View.VISIBLE);
isProgressShowing = true;
}
private void createBrowseJpgTask()
{
browseJpgTask = new BrowseJpgTask();
browseJpgTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void startTimeCounter()
{
if(timeCounter == null)
{
String timezone = "Etc/UTC";
if(evercamCamera != null)
{
timezone = evercamCamera.getTimezone();
}
timeCounter = new TimeCounter(this, timezone);
}
if(!timeCounter.isStarted())
{
timeCounter.start();
}
}
private void processSnapshot(Bitmap btm, SnapshotManager.FileType type)
{
final Bitmap bitmap = btm;
final SnapshotManager.FileType fileType = type;
if (bitmap != null)
{
CustomedDialog.getConfirmSnapshotDialog(VideoActivity.this, bitmap,
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
String path = SnapshotManager.createFilePath
(evercamCamera.getCameraId(), fileType);
new Thread(new CaptureSnapshotRunnable(VideoActivity
.this, path, bitmap)).start();
}
}).show();
}
}
// Handle stream loaded
private void onVideoLoaded()
{
Log.d(TAG, "onVideoLoaded()");
runOnUiThread(new Runnable()
{
public void run()
{
//View gets played, show time count, and start buffering
isPlayingJpg = false;
hideProgressView();
surfaceView.setVisibility(View.VISIBLE);
imageView.setVisibility(View.GONE);
startTimeCounter();
//And send to Google Analytics
EvercamPlayApplication.sendEventAnalytics(VideoActivity.this,
R.string.category_streaming_rtsp,
R.string.action_streaming_rtsp_success,
R.string.label_streaming_rtsp_success);
StreamFeedbackItem successItem = new StreamFeedbackItem(VideoActivity
.this, AppData.defaultUser.getUsername(), true);
successItem.setCameraId(evercamCamera.getCameraId());
successItem.setUrl(createUri(evercamCamera));
successItem.setType(StreamFeedbackItem.TYPE_RTSP);
if(startTime != null)
{
float timeDifferenceFloat = Commons.calculateTimeDifferenceFrom
(startTime);
Log.d(TAG, "Time difference: " + timeDifferenceFloat + " seconds");
successItem.setLoadTime(timeDifferenceFloat);
startTime = null;
}
sendToLogentries(logger, successItem.toJson());
successItem.sendToKeenIo(client);
}
});
}
// Handle stream loading failed
private void onVideoLoadFailed()
{
Log.d(TAG, "onVideoLoadFailed()");
runOnUiThread(new Runnable()
{
public void run()
{
EvercamPlayApplication.sendEventAnalytics(VideoActivity.this,
R.string.category_streaming_rtsp,
R.string.action_streaming_rtsp_failed,
R.string.label_streaming_rtsp_failed);
StreamFeedbackItem failedItem = new StreamFeedbackItem
(VideoActivity.this, AppData.defaultUser.getUsername(),
false);
failedItem.setCameraId(evercamCamera.getCameraId());
failedItem.setUrl(createUri(evercamCamera));
failedItem.setType(StreamFeedbackItem.TYPE_RTSP);
sendToLogentries(logger, failedItem.toJson());
failedItem.sendToKeenIo(client);
isPlayingJpg = true;
CustomToast.showInBottom(VideoActivity.this, R.string.msg_switch_to_jpg);
showImagesVideo = true;
createBrowseJpgTask();
}
});
}
private void onSampleRequestSuccess(byte[] data, int size)
{
final byte [] imageData = data;
final int imageSize = size;
runOnUiThread(new Runnable() {
public void run() {
final Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0, imageSize);
processSnapshot(bitmap, FileType.JPG);
}
});
}
private void onSampleRequestFailed()
{
runOnUiThread(new Runnable() {
public void run() {
Log.d(TAG, "onSampleRequestFailed");
CustomToast.showInCenterLong(VideoActivity.this, "Requesting snapshot failed");
}
});
}
public class BrowseJpgTask extends AsyncTask<String, String, String>
{
@Override
protected String doInBackground(String... params)
{
while(!end && !isCancelled() && showImagesVideo)
{
try
{
// wait for starting
while(!startDownloading)
{
Thread.sleep(500);
}
if(!paused) // if application is paused, do not send the
// requests. Rather wait for the play
// command
{
DownloadImageTask downloadImageTask = new DownloadImageTask(evercamCamera
.getCameraId());
if(downloadStartCount - downloadEndCount < 9)
{
downloadImageTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
if(downloadStartCount - downloadEndCount > 9 && sleepInterval < 2000)
{
sleepInterval += ADJUSTMENT_INTERVAL;
Log.d(TAG, "Sleep interval adjusted to: " + sleepInterval);
}
else if(sleepInterval >= MIN_SLEEP_INTERVAL)
{
sleepInterval -= ADJUSTMENT_INTERVAL;
Log.d(TAG, "Sleep interval adjusted to: " + sleepInterval);
}
}
}
catch(RejectedExecutionException ree)
{
Log.e(TAG, ree.toString() + "-::REE::-" + Log.getStackTraceString(ree));
}
catch(OutOfMemoryError e)
{
Log.e(TAG, e.toString() + "-::OOM::-" + Log.getStackTraceString(e));
}
catch(Exception ex)
{
downloadStartCount--;
Log.e(TAG, ex.toString() + "-::::-" + Log.getStackTraceString(ex));
sendToMint(ex);
}
try
{
Thread.currentThread();
Thread.sleep(sleepInterval, 50);
}
catch(Exception e)
{
EvercamPlayApplication.sendCaughtException(VideoActivity.this, e);
Log.e(TAG, e.toString() + "-::::-" + Log.getStackTraceString(e));
}
}
return null;
}
}
private class DownloadImageTask extends AsyncTask<Void, Void, Drawable>
{
private long myStartImageTime;
private String successUrl = "";//Only used for data collection
private String cameraId = "";
public DownloadImageTask(String cameraId)
{
this.cameraId = cameraId;
}
@Override
protected Drawable doInBackground(Void... params)
{
if(!showImagesVideo)
{
return null;
}
Drawable response = null;
if(!paused && !end)
{
try
{
myStartImageTime = SystemClock.uptimeMillis();
downloadStartCount++;
Camera camera = Camera.getById(cameraId, false);
InputStream stream = camera.getSnapshotFromEvercam();
response = Drawable.createFromStream(stream, "src");
if(response != null)
{
successiveFailureCount = 0;
}
}
catch(Exception e)
{
Log.e(TAG, "Request snapshot from Evercam error: " + e.toString());
successiveFailureCount++;
}
catch(OutOfMemoryError e)
{
Log.e(TAG, e.toString() + "-::OOM::-" + Log.getStackTraceString(e));
successiveFailureCount++;
}
finally
{
downloadEndCount++;
}
}
else
{
Log.d(TAG, "Paused or ended");
}
return response;
}
@Override
protected void onPostExecute(Drawable result)
{
try
{
if(!showImagesVideo) return;
Log.d(TAG, "Failure count:" + successiveFailureCount);
if(!paused && !end)
{
if(result != null)
{
Log.d(TAG, "result not null");
if(result.getIntrinsicWidth() > 0 && result.getIntrinsicHeight() > 0)
{
if(myStartImageTime >= latestStartImageTime)
{
latestStartImageTime = myStartImageTime;
if(mediaPlayerView.getVisibility() != View.VISIBLE &&
VideoActivity.this.getResources().getConfiguration()
.orientation == Configuration.ORIENTATION_LANDSCAPE)
VideoActivity.this.getActionBar().hide();
if(showImagesVideo && cameraId.equals(evercamCamera.getCameraId()))
{
//Only update JPG when the image belongs to the current camera
imageView.setImageDrawable(result);
}
else if(!cameraId.equals(evercamCamera.getCameraId()))
{
Log.e(TAG, "Image received but not to show");
}
hideProgressView();
//Image received, start time counter, need more tests
startTimeCounter();
if(!isJpgSuccessful)
{
//Successfully played JPG view, send Google Analytics event
isJpgSuccessful = true;
EvercamPlayApplication.sendEventAnalytics(VideoActivity.this,
R.string.category_streaming_jpg,
R.string.action_streaming_jpg_success,
R.string.label_streaming_jpg_success);
StreamFeedbackItem successItem = new StreamFeedbackItem
(VideoActivity.this, AppData.defaultUser.getUsername
(), true);
successItem.setCameraId(evercamCamera.getCameraId());
successItem.setUrl(successUrl);
successItem.setType(StreamFeedbackItem.TYPE_JPG);
sendToLogentries(logger, successItem.toJson());
successItem.sendToKeenIo(client);
}
else
{
//Log.d(TAG, "Jpg success but already reported");
}
}
else
{
if(enableLogs) Log.i(TAG, "downloaded image discarded. ");
}
}
}
else
{
Log.d(TAG, "result is null");
if(successiveFailureCount > 10 && !isShowingFailureMessage)
{
Log.d(TAG, "successiveFailureCount > 5 && !isShowingFailureMessage");
if(myStartImageTime >= latestStartImageTime)
{
Log.d(TAG, "myStartImageTime >= latestStartImageTime");
showMediaFailureDialog();
browseJpgTask.cancel(true);
//Failed to play JPG view, send Google Analytics event
EvercamPlayApplication.sendEventAnalytics(VideoActivity.this,
R.string.category_streaming_jpg,
R.string.action_streaming_jpg_failed,
R.string.label_streaming_jpg_failed);
//Send Feedback
StreamFeedbackItem failedItem = new StreamFeedbackItem
(VideoActivity.this, AppData.defaultUser.getUsername(),
false);
failedItem.setCameraId(evercamCamera.getCameraId());
failedItem.setUrl(evercamCamera.getExternalSnapshotUrl());
failedItem.setType(StreamFeedbackItem.TYPE_JPG);
sendToLogentries(logger, failedItem.toJson());
failedItem.sendToKeenIo(client);
}
}
}
}
else
{
Log.d(TAG, "paused or ended");
}
}
catch(OutOfMemoryError e)
{
if(enableLogs) Log.e(TAG, e.toString() + "-::OOM::-" + Log.getStackTraceString(e));
}
catch(Exception e)
{
if(enableLogs) Log.e(TAG, e.toString());
sendToMint(e);
}
startDownloading = true;
}
}
private String[] getCameraNameArray(ArrayList<EvercamCamera> cameraList)
{
ArrayList<String> cameraNames = new ArrayList<>();
for(int count = 0; count < cameraList.size(); count++)
{
EvercamCamera camera = cameraList.get(count);
cameraNames.add(camera.getName());
if(cameraList.get(count).getCameraId().equals(startingCameraID))
{
defaultCameraIndex = cameraNames.size() - 1;
}
}
String[] cameraNameArray = new String[cameraNames.size()];
cameraNames.toArray(cameraNameArray);
return cameraNameArray;
}
private void loadCamerasToActionBar()
{
String[] cameraNames;
final ArrayList<EvercamCamera> onlineCameraList = new ArrayList<>();
final ArrayList<EvercamCamera> cameraList;
//If is not showing offline cameras, the offline cameras should be excluded from list
if(PrefsManager.showOfflineCameras(VideoActivity.this))
{
cameraList = AppData.evercamCameraList;
}
else
{
for(EvercamCamera evercamCamera : AppData.evercamCameraList)
{
if(!evercamCamera.isOffline())
{
onlineCameraList.add(evercamCamera);
}
}
cameraList = onlineCameraList;
}
cameraNames = getCameraNameArray(cameraList);
CameraListAdapter adapter = new CameraListAdapter(VideoActivity.this,
R.layout.live_view_spinner, R.id.spinner_camera_name_text, cameraNames, cameraList);
VideoActivity.this.getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
OnNavigationListener navigationListener = new OnNavigationListener()
{
@Override
public boolean onNavigationItemSelected(int itemPosition, long itemId)
{
//Stop time counter when another camera selected
if(timeCounter != null)
{
timeCounter.stop();
timeCounter = null;
}
if(browseJpgTask != null && browseJpgTask.getStatus() != AsyncTask.Status.RUNNING)
{
browseJpgTask.cancel(true);
}
browseJpgTask = null;
showImagesVideo = false;
evercamCamera = cameraList.get(itemPosition);
if(evercamCamera.isOffline())
{
// If camera is offline, show offline msg and stop video
// playing.
offlineTextView.setVisibility(View.VISIBLE);
progressView.setVisibility(View.GONE);
// Hide video elements if switch to an offline camera.
surfaceView.setVisibility(View.GONE);
imageView.setVisibility(View.GONE);
}
else
{
offlineTextView.setVisibility(View.GONE);
setCameraForPlaying(cameraList.get(itemPosition));
createPlayer(evercamCamera);
}
return false;
}
};
getActionBar().setListNavigationCallbacks(adapter, navigationListener);
getActionBar().setSelectedNavigationItem(defaultCameraIndex);
}
private void onMediaSizeChanged (int width, int height) {
Log.i ("GStreamer", "Media size changed to " + width + "x" + height);
final GStreamerSurfaceView gstreamerSurfaceView = (GStreamerSurfaceView) this.findViewById(R.id.surface_view);
gstreamerSurfaceView.media_width = width;
gstreamerSurfaceView.media_height = height;
runOnUiThread(new Runnable() {
public void run() {
gstreamerSurfaceView.requestLayout();
}
});
}
}