Appsee Support

Controlling Session Lifetime

A session in Appsee ends when your app is sent to the background. There are use cases where you might want to prevent the session from ending prematurely in order to continue it after an external input by the user.

Here are a few examples of those use-cases:

1. When your app opens a camera app to take a picture.
2. When your app prompts a permission dialog (Android 6.0 Marshmallow and up).
3. When your app opens a web-based login screen.

In the example code below you can see the proper way to prevent the Appsee session from ending due to one of the scenarios above.

To read more about the session lifetime methods, please see the links below:
- For Android: https://www.appsee.com/docs/android/api?section=lifetime
- For iOS: https://www.appsee.com/docs/ios/api?section=lifetime

Note: the code contains a timeout mechanism (MAX_APPSEE_SESSION_TIMEOUT) that ensures that Appsee's session will end after some time in case your app never returns to the foreground. To prevent infinite Appsee sessions you must use a reasonable timeout depending on the use-case of the app.
DO NOT remove the timeout mechanism, as it might cause Appsee to continue working in the background indefinitely.

Link to code
Here is an example code:

package com.appseelifecycle;

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.appsee.Appsee;
import com.appsee.AppseeListener;
import com.appsee.AppseeScreenDetectedInfo;
import com.appsee.AppseeSessionEndedInfo;
import com.appsee.AppseeSessionEndingInfo;
import com.appsee.AppseeSessionStartedInfo;
import com.appsee.AppseeSessionStartingInfo;

import java.io.FileOutputStream;

public class MainActivity extends AppCompatActivity
{
	private static final String TAG = "AppseeDemo";
	private static final int TAKE_PICTURE_REQUEST_CODE = 98;
	private static final int PERMISSION_REQUEST_CODE = 99;
	private static AppseeListener appseeListener = null;

	// Indicates if app is not foreground but should keep appsee session alive (until the timeout or app becomes foreground again)
	private static boolean appseeSessionKeepAlive = false;

	// Handler that will call Appsee.finishSession() upon timeout
	private static Handler handler;

	// Sets how much time appsee session continues after app is no longer in foreground
	private static final int MAX_APPSEE_SESSION_TIMEOUT = 5 * 1000; // 5 seconds

	// Runnable that forces appsee to finish the session now
	private static final Runnable appseeForceFinishSession = new Runnable()
	{
		@Override
		public void run()
		{
			// Must allow the session to end now after timeout!
			setAppseeSessionKeepAlive(false);

			// Finishing session due to too much time outside the app
			Appsee.finishSession(true, true);
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		findViewById(R.id.btnTakePhoto).setOnClickListener(new View.OnClickListener()
		{
			@Override
			public void onClick(View view)
			{
				startCamera();
			}
		});

		startAppsee();
	}

	private void startAppsee()
	{
		if (appseeListener == null)
		{
			appseeListener = new AppseeListener()
			{
				@Override
				public void onAppseeSessionEnding(AppseeSessionEndingInfo appseeSessionEndingInfo)
				{
					appseeSessionEndingInfo.setShouldEndSession(!appseeSessionKeepAlive);
				}

				@Override
				public void onAppseeSessionStarting(AppseeSessionStartingInfo appseeSessionStartingInfo) {}

				@Override
				public void onAppseeSessionStarted(AppseeSessionStartedInfo appseeSessionStartedInfo) {}

				@Override
				public void onAppseeSessionEnded(AppseeSessionEndedInfo appseeSessionEndedInfo) {}

				@Override
				public void onAppseeScreenDetected(AppseeScreenDetectedInfo appseeScreenDetectedInfo) {}
			};

			Appsee.addAppseeListener(appseeListener);
		}

		HandlerThread handlerThread = new HandlerThread("AppseeDelayedSessionClosingThread");
		handlerThread.start();
		handler = new Handler(handlerThread.getLooper());

		Appsee.start("API KEY");
	}

	private void startCamera()
	{
		// Must ask for permission on Android M and up!
		askPermission();

		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
		{
			Log.w(TAG, "No permission!");
			return;
		}

		// before potentially leaving the app, flagging for appsee to avoid ending session
		setAppseeSessionKeepAlive(true);
		startActivityForResult(new Intent(MediaStore.ACTION_IMAGE_CAPTURE), TAKE_PICTURE_REQUEST_CODE);
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data)
	{
		// Back in the app, now appsee can control the session lifecycle again
		setAppseeSessionKeepAlive(false);

		if (requestCode == TAKE_PICTURE_REQUEST_CODE && resultCode == Activity.RESULT_OK)
		{
			if (data.getExtras().containsKey("data"))
			{
				Bitmap bitmap = (Bitmap)data.getExtras().get("data");

				if (bitmap != null)
				{
					try
					{
						FileOutputStream fos = new FileOutputStream("/sdcard/test.jpg");
						bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
						fos.close();
					}
					catch (Exception e)
					{
						Log.e(TAG, "Error saving image, Exception" + e);
					}
				}
			}
		}
	}

	private void askPermission()
	{
		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;

		if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
		{
			if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE))
			{
				Toast.makeText(this, "Must need permission to save the file", Toast.LENGTH_LONG).show();
			}

			// before potentially leaving the app, flagging for appsee to avoid ending session
			setAppseeSessionKeepAlive(true);
			requestPermissions(new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE }, PERMISSION_REQUEST_CODE);
		}
	}

	@Override
	public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)
	{
		// Back in the app, now appsee can control the session lifecycle again
		setAppseeSessionKeepAlive(false);

		switch (requestCode)
		{
			case PERMISSION_REQUEST_CODE:
			{
				// If request is cancelled, the result arrays are empty.
				if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
				{
					startCamera();
				}
				else
				{
				}
			}
		}
	}

	private static void setAppseeSessionKeepAlive(boolean appseeSessionKeepAlive)
	{
		MainActivity.appseeSessionKeepAlive = appseeSessionKeepAlive;

		if (appseeSessionKeepAlive)
		{
			handler.postDelayed(appseeForceFinishSession, MAX_APPSEE_SESSION_TIMEOUT);
		}
		else
		{
			handler.removeCallbacks(appseeForceFinishSession);
		}
	}
}

Did you find this article helpful?