Android Custom Camera Preview

Android Custom Camera Preview

The Android Custom Camera Preview plugin provides simple functions to easily show a live camera preview and optionally overlay it with a centered image for custom frame or crosshair graphics, and/or text for instructions/feedback to the user. The plugin also provides a function for taking photos while the preview is running. Photos are saved to the device cache directory and the filename is returned for custom handling.

Currently, only the back-facing camera is supported. If you need front-facing camera support please get in touch. We intend to update this plugin with camera switching support, among other things, and may prioritise doing so if there's enough demand for it.

This plugin is available to Android users only. iOS developers can instead fill simple display objects with the camera source fill effect.

Get Android Custom Camera Preview now from the Corona Labs Marketplace.

Integration

First, get the Android Custom Camera Preview plugin from the Corona Marketplace.

Next, update the plugins section of your projects build.settings file to include Android Custom Camera Preview:


	--
	-- Plugins section
	--
	plugins =
	{
		["plugin.androidCustomCameraPreview"] =
		{
			publisherId = "uk.co.qweb"
		},
	},
						

Finally, load the Android Custom Camera Preview library from within your game code and request camera access permissions from the user:


	local cameraView = require "plugin.androidCustomCameraPreview"

	local hasAccessToCamera, hasCamera = media.hasSource( media.Camera )
	if(hasAccessToCamera == false) then
		if(hasCamera == true and native.canShowPopup( "requestAppPermission" )) then
			native.showPopup( "requestAppPermission", { appPermission="Camera" } )
		end

		display.newText( { text = "After authorising camera permissions, restart this app.", x = display.contentCenterX, y = display.contentCenterY, width = 200, height = 0, font = native.systemFont, fontSize = 20, align = "center" } )
	end
						

That's it! You can now use any of the below functions within your Corona app.

Functions

start()

Starts a full screen preview of the back-facing camera. The preview overlays all Corona graphics.


	local cameraView = require "plugin.androidCustomCameraPreview"

	local hasAccessToCamera, hasCamera = media.hasSource( media.Camera )
	if(hasAccessToCamera == false) then
		if(hasCamera == true and native.canShowPopup( "requestAppPermission" )) then
			native.showPopup( "requestAppPermission", { appPermission="Camera" } )
		end

		display.newText( { text = "After authorising camera permissions, restart this app.", x = display.contentCenterX, y = display.contentCenterY, width = 200, height = 0, font = native.systemFont, fontSize = 20, align = "center" } )
	else
		cameraView.start()
	end
						

stop()

Stops the preview and returns to your Corona display.


	local cameraView = require "plugin.androidCustomCameraPreview"

	local hasAccessToCamera, hasCamera = media.hasSource( media.Camera )
	if(hasAccessToCamera == false) then
		if(hasCamera == true and native.canShowPopup( "requestAppPermission" )) then
			native.showPopup( "requestAppPermission", { appPermission="Camera" } )
		end

		display.newText( { text = "After authorising camera permissions, restart this app.", x = display.contentCenterX, y = display.contentCenterY, width = 200, height = 0, font = native.systemFont, fontSize = 20, align = "center" } )
	else
		local mainDisplayGroup = display.newGroup()
		local button

		cameraView.start()

		local function handleTaps(event)
			cameraView.stop()
			display.remove(button)
		end

		-- Fill the screen with a button to capture taps while the camera is displayed. Can't actually see this for the camera but it'll still receive events.
		button = display.newRect( mainDisplayGroup, display.contentCenterX, display.contentCenterY, display.contentWidth + ((display.screenOriginX / -1) * 2), display.contentHeight + ((display.screenOriginY / -1) * 2) )
		button:setFillColor(0, 0, 0)
		button:addEventListener('tap', handleTaps)
	end
						

status()

Receive the Android Custom Camera Preview current status. Return value will be either "waiting" or "running".


	local cameraView = require "plugin.androidCustomCameraPreview"

	local hasAccessToCamera, hasCamera = media.hasSource( media.Camera )
	if(hasAccessToCamera == false) then
		if(hasCamera == true and native.canShowPopup( "requestAppPermission" )) then
			native.showPopup( "requestAppPermission", { appPermission="Camera" } )
		end

		display.newText( { text = "After authorising camera permissions, restart this app.", x = display.contentCenterX, y = display.contentCenterY, width = 200, height = 0, font = native.systemFont, fontSize = 20, align = "center" } )
	else
		local mainDisplayGroup = display.newGroup()
		local button

		print("Status before starting: " .. cameraView.status())

		cameraView.start()

		print("Status after starting: " .. cameraView.status())

		local function handleTaps(event)
			cameraView.stop()
			display.remove(button)

			print("Status after stopping: " .. cameraView.status())
		end

		-- Fill the screen with a button to capture taps while the camera is displayed. Can't actually see this for the camera but it'll still receive events.
		button = display.newRect( mainDisplayGroup, display.contentCenterX, display.contentCenterY, display.contentWidth + ((display.screenOriginX / -1) * 2), display.contentHeight + ((display.screenOriginY / -1) * 2) )
		button:setFillColor(0, 0, 0)
		button:addEventListener('tap', handleTaps)
							

text(string position, string message)

Sets a message to appear either at position "top" or "bottom" of the camera preview.

Messages are shown in white with a subtle black outline shadow for optimal visibility regardless of what the camera is showing. Text displayed at the top outputs at a maximum font size of 30pt, and text displayed at the bottom outputs with a maximum font size of 20pt. On devices running Android Oreo (Android 8.0, SDK 26) or later, text size is adjusted for best-fit. On devices running earlier versions the text is output at maximum size.


	local cameraView = require "plugin.androidCustomCameraPreview"

	local hasAccessToCamera, hasCamera = media.hasSource( media.Camera )
	if(hasAccessToCamera == false) then
		if(hasCamera == true and native.canShowPopup( "requestAppPermission" )) then
			native.showPopup( "requestAppPermission", { appPermission="Camera" } )
		end

		display.newText( { text = "After authorising camera permissions, restart this app.", x = display.contentCenterX, y = display.contentCenterY, width = 200, height = 0, font = native.systemFont, fontSize = 20, align = "center" } )
	else
		local mainDisplayGroup = display.newGroup()
		local button

		cameraView.start()
		cameraView.text("top", "Android Custom Camera Preview for Corona SDK")
		cameraView.text("bottom", "Hello World!")

		local function handleTaps(event)
			cameraView.stop()
			display.remove(button)
		end

		-- Fill the screen with a button to capture taps while the camera is displayed. Can't actually see this for the camera but it'll still receive events.
		button = display.newRect( mainDisplayGroup, display.contentCenterX, display.contentCenterY, display.contentWidth + ((display.screenOriginX / -1) * 2), display.contentHeight + ((display.screenOriginY / -1) * 2) )
		button:setFillColor(0, 0, 0)
		button:addEventListener('tap', handleTaps)
	end
						

image(string filename)

Loads an image from the device cache directory and centers to the camera preview. Images are scaled to fit, so for best results go for a high resolution square canvas.

Images must be moved to the cache directory first and as per the Corona documentation, should therefore be named as a .txt file to prevent the build process from packaging them into your binary.


	local cameraView = require "plugin.androidCustomCameraPreview"

	local hasAccessToCamera, hasCamera = media.hasSource( media.Camera )
	if(hasAccessToCamera == false) then
		if(hasCamera == true and native.canShowPopup( "requestAppPermission" )) then
			native.showPopup( "requestAppPermission", { appPermission="Camera" } )
		end

		display.newText( { text = "After authorising camera permissions, restart this app.", x = display.contentCenterX, y = display.contentCenterY, width = 200, height = 0, font = native.systemFont, fontSize = 20, align = "center" } )
	else
		local mainDisplayGroup = display.newGroup()
		local button

		-- Moves crosshair.png.txt to the cache directory + renames to crosshair.png, so that we can tell cameraPreview to open it
		local rfh = io.open( system.pathForFile("crosshair.png.txt"), "rb" )
		local wfh = io.open( system.pathForFile("crosshair.png", system.CachesDirectory), "wb" )
		local data = rfh:read( "*a" )
		wfh:write( data )
		rfh:close()
		wfh:close()

		cameraView.start()
		cameraView.image(system.pathForFile("crosshair.png", system.CachesDirectory))

		local function handleTaps(event)
			cameraView.stop()
			display.remove(button)
		end

		-- Fill the screen with a button to capture taps while the camera is displayed. Can't actually see this for the camera but it'll still receive events.
		button = display.newRect( mainDisplayGroup, display.contentCenterX, display.contentCenterY, display.contentWidth + ((display.screenOriginX / -1) * 2), display.contentHeight + ((display.screenOriginY / -1) * 2) )
		button:setFillColor(0, 0, 0)
		button:addEventListener('tap', handleTaps)
	end
						

photo()

Captures a photo from the Camera Preview. Camera Preview must first be started.

Returns the filename of the resulting photo saved to the caches directory, but you must wait for the file to be fully written first. Refer to the photoStatus() function for this.

Some devices will freeze the preview image while the photo is being written, and the stop() function may be delayed until after the write completes.


	local cameraView = require "plugin.androidCustomCameraPreview"

	local hasAccessToCamera, hasCamera = media.hasSource( media.Camera )
	if(hasAccessToCamera == false) then
		if(hasCamera == true and native.canShowPopup( "requestAppPermission" )) then
			native.showPopup( "requestAppPermission", { appPermission="Camera" } )
		end

		display.newText( { text = "After authorising camera permissions, restart this app.", x = display.contentCenterX, y = display.contentCenterY, width = 200, height = 0, font = native.systemFont, fontSize = 20, align = "center" } )
	else
		local mainDisplayGroup = display.newGroup()
		local button

		cameraView.start()

		local function handleTaps(event)
			local pic = cameraView.photo()
			local file = system.pathForFile(pic, system.CachesDirectory)

			if(file ~= nil) then
				-- Wait until the photo file has finished being written
				while(cameraView.photoStatus() ~= "saved") and (cameraView.photoStatus() ~= "error") do
				end

				-- Show the resulting capture and scale to fit
				local capturedImage = display.newImage(mainDisplayGroup, pic, system.CachesDirectory, display.contentCenterX, display.contentCenterY)
				local scale = (display.contentWidth / 2) / capturedImage.width
				capturedImage:scale(scale, scale)
			end

			cameraView.stop()
			display.remove(button)
		end

		-- Fill the screen with a button to capture taps while the camera is displayed. Can't actually see this for the camera but it'll still receive events.
		button = display.newRect( mainDisplayGroup, display.contentCenterX, display.contentCenterY, display.contentWidth + ((display.screenOriginX / -1) * 2), display.contentHeight + ((display.screenOriginY / -1) * 2) )
		button:setFillColor(0, 0, 0)
		button:addEventListener('tap', handleTaps)
	end
						

photoStatus()

Receive the Android Custom Camera Preview current photo-writing status. Return value will be either "waiting", "writing", "saved", or "error".

When calling photo(), Android Custom Camera Preview will return the filename that the photo will be written to immediately, but it can take a few seconds for the file write to complete, depending on the camera resolution and the file access write speed. You should therefore wait for photoStatus() to return "saved" before attempting to use this file.


	local cameraView = require "plugin.androidCustomCameraPreview"

	local hasAccessToCamera, hasCamera = media.hasSource( media.Camera )
	if(hasAccessToCamera == false) then
		if(hasCamera == true and native.canShowPopup( "requestAppPermission" )) then
			native.showPopup( "requestAppPermission", { appPermission="Camera" } )
		end

		display.newText( { text = "After authorising camera permissions, restart this app.", x = display.contentCenterX, y = display.contentCenterY, width = 200, height = 0, font = native.systemFont, fontSize = 20, align = "center" } )
	else
		local mainDisplayGroup = display.newGroup()
		local button

		cameraView.start()

		local function handleTaps(event)
			local pic = cameraView.photo()
			local file = system.pathForFile(pic, system.CachesDirectory)

			if(file ~= nil) then
				-- Wait until the photo file has finished being written
				while(cameraView.photoStatus() ~= "saved") and (cameraView.photoStatus() ~= "error") do
				end

				-- Show the resulting capture and scale to fit
				local capturedImage = display.newImage(mainDisplayGroup, pic, system.CachesDirectory, display.contentCenterX, display.contentCenterY)
				local scale = (display.contentWidth / 2) / capturedImage.width
				capturedImage:scale(scale, scale)
			end

			cameraView.stop()
			display.remove(button)
		end

		-- Fill the screen with a button to capture taps while the camera is displayed. Can't actually see this for the camera but it'll still receive events.
		button = display.newRect( mainDisplayGroup, display.contentCenterX, display.contentCenterY, display.contentWidth + ((display.screenOriginX / -1) * 2), display.contentHeight + ((display.screenOriginY / -1) * 2) )
		button:setFillColor(0, 0, 0)
		button:addEventListener('tap', handleTaps)
	end