Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(android): use cameraX #12395

Merged
merged 59 commits into from May 3, 2023
Merged

feat(android): use cameraX #12395

merged 59 commits into from May 3, 2023

Conversation

m1ga
Copy link
Contributor

@m1ga m1ga commented Jan 18, 2021

JIRA: https://jira-archive.titaniumsdk.com/AC-6676
Fixes #13525

Optional Description:

Will use CameraX on Android when using a camera with an overlay

const win = Ti.UI.createWindow({layout: "vertical", backgroundColor: "#fff"});

const buttons = [
	"open camera (old camera)",
	"open camera",
	"open camera (no autoHide)",
	"open camera (flash on)",
	"open camera (flash auto)",
	"open camera (video)",
	"open camera (video, low bit rate)",
	"open camera (video, low resolution)",
	"open camera (video, max duration)",
	"open camera (small output image)",
];
const results = Ti.UI.createView({height: Ti.UI.SIZE, width: Ti.UI.FILL, layout: "horizontal"});
const img = Ti.UI.createImageView({width: "45%", height: Ti.UI.SIZE, autorotate: true});
const videoPlayer = Titanium.Media.createVideoPlayer({height: 200, width: "45%", mediaControlStyle: Titanium.Media.VIDEO_CONTROL_DEFAULT, scalingMode: Titanium.Media.VIDEO_SCALING_RESIZE_ASPECT});
var isRecording = false;
var videoMaximumDuration = 0;
var isBack = true;

results.add([img, videoPlayer])

for (var i = 0; i < buttons.length; ++i) {
	var btn = Ti.UI.createButton({title: buttons[i], idx: i + 1, height: 40, borderRadius:0, width: 300});
	btn.addEventListener("click", openCamera);
	win.add(btn);
}
win.add(results);

var cameraSizes = Ti.Media.cameraOutputSizes;
if (cameraSizes != null) {
	for (var i=0; i<cameraSizes.length;++i){
		console.log(cameraSizes[i].cameraType);
		console.log(cameraSizes[i].values);
	}
}


function openCamera(e) {
	var sourceId = e.source.idx;
	var isVideo = (sourceId == "6" || sourceId == "7" || sourceId == "8" || sourceId == "9");
	var overlay = Ti.UI.createView({height: Ti.UI.FILL, height: Ti.UI.FILL});
	var isTorch = isFlash = isZoom = false;
	var btnText = (isVideo) ? "Record video" : "Take picture";
	var useCameraX = (sourceId != "1");
	var btn = Ti.UI.createButton({title: btnText, bottom: 10});
	var btn2 = Ti.UI.createButton({title: "Switch cam", bottom: 50});
	var btn3 = Ti.UI.createButton({title: "torch", bottom: 90, left: 10});
	var btn4 = Ti.UI.createButton({title: "flash (off)", bottom: 90, right: 10});
	var btn5 = Ti.UI.createButton({title: "zoom", bottom: 90});
	overlay.add([btn, btn2, btn3, btn4,btn5]);

	btn.addEventListener("click", function() {
		if (isVideo) {
			if (!isRecording) {
				Ti.Media.startVideoCapture()
				isRecording = true;
				btn.title = "Stop";
			} else {
				Ti.Media.stopVideoCapture()
				isRecording = false;
				btn.title = btnText;
			}
		} else {
			Ti.Media.takePicture()
		}
	})
	btn2.addEventListener("click", function() {
		if (isBack) {
			Ti.Media.switchCamera(Titanium.Media.CAMERA_FRONT)
		} else {
			Ti.Media.switchCamera(Titanium.Media.CAMERA_REAR)
		}
		isBack = !isBack;
	})
	btn3.addEventListener("click", function() {
		isTorch = !isTorch;
		Ti.Media.torch = isTorch;
	})
	btn4.addEventListener("click", function() {
		if (isFlash) {
			Ti.Media.cameraFlashMode = Titanium.Media.CAMERA_FLASH_OFF
			btn4.title = "flash (off)";
		} else {
			Ti.Media.cameraFlashMode = Titanium.Media.CAMERA_FLASH_ON
			btn4.title = "flash (on)";
		}
		isFlash = !isFlash;
	})
	btn5.addEventListener("click", function() {
		if (isZoom) {
			Ti.Media.zoomLevel = 1;
		} else {
			Ti.Media.zoomLevel = Ti.Media.maxZoomLevel - 0.5;
		}
		isZoom = !isZoom;
	})

	var flashMode = Titanium.Media.CAMERA_FLASH_OFF;
	if (sourceId == "4") {
		isFlash = true;
		flashMode = Titanium.Media.CAMERA_FLASH_ON;
		btn4.title = "flash (on)";
	} else if (sourceId == "5") {
		flashMode = Titanium.Media.CAMERA_FLASH_AUTO;
	}

	var autohide = true;
	if (sourceId == "3") {
		autohide = false;
	}

	var mediaType = Titanium.Media.MEDIA_TYPE_IMAGE;
	if (sourceId == "6") {
		mediaType = Titanium.Media.MEDIA_TYPE_VIDEO;
	}

	var bitRate = -1;
	var frameRate = -1;
	if (sourceId == "7") {
		mediaType = Titanium.Media.MEDIA_TYPE_VIDEO;
		bitRate = 120;
		frameRate = 2 * 1024 * 1024;
	}

	var maxResolution = null;
	if (sourceId == "8") {
		mediaType = Titanium.Media.MEDIA_TYPE_VIDEO;
		maxResolution = {
			width: 300,
			height: 200
		}
	}

	if (sourceId == "9") {
		mediaType = Titanium.Media.MEDIA_TYPE_VIDEO;
		videoMaximumDuration = 2000;
	}

	var imgWidth = -1;
	var imgHeight = -1;
	if (sourceId == "10") {
		// lower the target image resolution
		imgWidth = 240;
		imgHeight = 320;
	}

	btn2.visible = btn3.visible = btn4.visible = btn5.visible = useCameraX;

	Ti.Media.requestCameraPermissions(function(e) {
		if (e.success) {
			Ti.Media.showCamera({
				autohide: autohide,
				useCameraX: useCameraX,
				cameraFlashMode: flashMode,
				overlay: overlay,
				frameRate: frameRate,
				bitRate: bitRate,
				mediaTypes: [mediaType],
				saveToPhotoGallery: true,
				maxResolution: maxResolution,
				videoMaximumDuration: videoMaximumDuration,
				targetImageWidth: imgWidth,
				targetImageHeight: imgHeight,
				success: function(e) {

					if (e.mediaType == "public.movie") {
						var f = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, "tmp.mp4");
						f.write(e.media);
						videoPlayer.url = f.nativePath;
						videoPlayer.play();
					} else {
						img.image = e.media;
					}
					console.log("type", e.mediaType);
					console.log("size", e.media.size);
					console.log("width", e.media.width, "height", e.media.height);
				}
			});
		}
	});
}

win.open();

Features:

  • open camera
  • close camera
  • add overlay
  • switch front/back
  • take picture
  • video support
  • mediatype
  • new image size parameters: targetImageWidth and targetImageHeight
  • new Ti.Media.cameraOutputSizes to get the camera output sizes
  • video quality
  • video max duration
  • flash
  • torch
  • pinch to zoom (photo & video)
  • minZoomLevel, maxZoomLevel and zoomLevel to set the zoom in code
  • check file destination / saveToGallery
  • clean up code
  • open callback with preview size return values
  • aspectRatio with Ti.Media.ASPECT_RATIO_16_9 and Ti.Media.ASPECT_RATIO_4_3
  • scalingMode with Ti.Media.IMAGE_SCALING_ASPECT_FIT and Ti.Media.IMAGE_SCALING_ASPECT_FILL
  • verticalAlign with Ti.Media.VERTICAL_ALIGN_BOTTOM, Ti.Media.VERTICAL_ALIGN_TOP, Ti.Media.VERTICAL_ALIGN_CENTER
  • add useCameraX parameter

Tested:

  • Pixel 4, Android 13
  • Pixel 7, Android 13
  • Samsung A20, Android 11
Ti.Media.showCamera({
	success: onCameraSuccess,
	overlay: view_overlay,
	aspectRatio: Ti.Media.ASPECT_RATIO_16_9,
	scalingMode: Ti.Media.IMAGE_SCALING_ASPECT_FIT,
	open: function(e) {
		console.log(e);
	}
});

16:9 image with ASPECT_FILL
image

16:9 image with ASPECT_FIT
Screenshot_20230319-161953

@build
Copy link
Contributor

build commented Jan 18, 2021

Fails
🚫 Test reports missing for Android 5.0, Android Main, CLI, iPad, iPhone, MacOS. This indicates that a build failed or the test app crashed
🚫

🔬 There are library changes, but no changes to the unit tests. That's OK as long as you're refactoring existing code, but will require an admin to merge this PR. Please see README.md#unit-tests for docs on unit testing.

Warnings
⚠️

Commit 05cc839edb945fb0ce0b46b1e31ce177f882841f has a message "merge" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit 3502f498dbec6625d28474b29edae95267694c59 has a message "max video duration" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit 2899cea19fa9a1769614ae640e690806198eba4e has a message "version update" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit 4ba122e18142fc494372db09c2ad1b9e52e8fc32 has a message "version update, permission check" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit fb833e758401cb0448f97a10c7c51eaf01296bfa has a message "update" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit 9e50759946640e9f9946559c0fbfec556f2f47cb has a message "remove old files" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit 9021cadc3265ee2240080aa792bd6709365db44e has a message "update library version" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit 3427c8e92c51b760dd67b6fab0b0fa8126d5d6ab has a message "bit rate, max resolution, bug fixes" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit cc0e5d221f5e52ca2352d31be011985b4f0cedb7 has a message "change video camera" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit be9b5eaee6003cca90c342b30db31395ca3c109b has a message "video start" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit cdea992513877c121a659ad06fcdf59c8a3f1a46 has a message "save image" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit ba2d6380b1c9d23f4cd97c39c2d18f8e0b8d2278 has a message "flash" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit e0f549500527c738fe267abde5ab593cbee49070 has a message "android back, switch lens" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit 89a9a5e6ed3481eb5e0b22bea43a51670e5637b0 has a message "close camera" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit 79e21969e43798d674122447a8491d0bd3525544 has a message "camerax overlay" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

🔍 Can't find junit reports at ./junit.*.xml, skipping generating JUnit Report.

⚠️ This PR has milestone set to 10.0.0, but the version defined in package.json is 10.2.0 Please either: - Update the milestone on the PR - Update the version in package.json - Hold the PR to be merged later after a release and version bump on this branch
Messages
📖

🚨 This PR has one or more commits with warnings/errors for commit messages not matching our configuration. You may want to squash merge this PR and edit the message to match our conventions, or ask the original developer to modify their history.

📖 🎉 Another contribution from our awesome community member, m1ga! Thanks again for helping us make Titanium SDK better. 👍
📖 👍 Hey!, You deleted more code than you added. That's awesome!

Generated by 🚫 dangerJS against cfe1f05

@build build requested a review from a team February 8, 2021 10:38
@build build added the docs label Feb 8, 2021
@build build requested a review from a team April 26, 2021 17:10
@m1ga
Copy link
Contributor Author

m1ga commented Oct 8, 2022

added Ti.Media.torch = true/false to switch on the light when the camera is open. That will light up the whole time and not like the flash only when you take the image

@m1ga
Copy link
Contributor Author

m1ga commented Nov 25, 2022

Added output image parameters:

var cameraSizes = Ti.Media.cameraOutputSizes;
for (var i=0; i<cameraSizes.length;++i){
	console.log(cameraSizes[i].cameraType);
	console.log(cameraSizes[i].values);
}

to get the potential sizes.

And you can set them with:

targetImageWidth: 800,
targetImageHeight: 600,

@m1ga
Copy link
Contributor Author

m1ga commented Mar 19, 2023

some new additions that makes working with overlays a lot easier:

  • open callback that contains the size of the preview (e.g. if you use 4:3 it will be wider than your screen)
  • aspectRatio with two constants Ti.Media.ASPECT_RATIO_16_9 and Ti.Media.ASPECT_RATIO_4_3
  • scalingMode with Ti.Media.IMAGE_SCALING_ASPECT_FIT and Ti.Media.IMAGE_SCALING_ASPECT_FILL

imageScreenshot_20230319-161953

having the preview only be FIT (full width) makes it easier to work with overlays:

20230319_162945

@m1ga
Copy link
Contributor Author

m1ga commented Mar 19, 2023

  • verticalAlign with Ti.Media.VERTICAL_ALIGN_BOTTOM (top, bottom, center)

Screenshot_20230319-163636 Screenshot_20230319-163647

@m1ga
Copy link
Contributor Author

m1ga commented May 1, 2023

added the old files back. The new cameraX part is used when you set useCameraX: true opening the camera.
That way we can merge this quicker as the default is still the old way and you can opt-in to the new way

@hansemannn
Copy link
Collaborator

Let's take this in and give it some testing during the next 1-2 releases. If it runs smooth, we can make it default after that 🙏

Copy link
Collaborator

@hansemannn hansemannn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some more feedback - mostly questions to be sure! One general thing: As the code is pretty separated now, wouldn't it make sense to wrap it up inside a module to be able to quickly iterate on it, then include it to the SDK once stable? We did the same with ti.wkwebview back then.

android/titanium/build.gradle Outdated Show resolved Hide resolved
apidoc/Titanium/Media/Media.yml Outdated Show resolved Hide resolved
platforms: [android]
since: "12.1.0"

- name: VERTICAL_ALIGN_TOP
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefix this constants to be more distinctive regarding other ones?

} else if (videoRecordEvent instanceof VideoRecordEvent.Pause) {
// Handle the case where the active recording is paused
} else if (videoRecordEvent instanceof VideoRecordEvent.Resume) {
// Handles the case where the active recording is resumed
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More events useful?

}
if (camera != null) {
camera = null;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any more strong references, e.g. callbackContext and overlayProxy? It would be good to run the profiler for any memory leaks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I'll check that. The current TiCameraActivity doesn't do more so if that works fine it will be the same in TiCameraXActivity.

@m1ga
Copy link
Contributor Author

m1ga commented May 3, 2023

Thanks for the feedback! Already adjusted most of it!

to wrap it up inside a module to be able to quickly iterate on it, then include it to the SDK once stable? We did the same with ti.wkwebview back then.

I did so much work here already that I don't want to rip it out and create a module. I think more people test it if we just say "enable useCameraX" and don't have to add a module. It would be different too (no Ti.Media... namespace) so people would need to change code after it's merged into the SDK.

My idea would be:
have people test it in the RC/beta phase to see that it is working as a replacement (using the current features, just with the flag enabled) and then we know it is working without a difference.

If the new features have to be changed it will be in a minor bugfix release as they are "new features" that won't break existing apps. There wasn't any demand for those features so they aren't time critical (and they are Android only).

@hansemannn
Copy link
Collaborator

Totally fine! Let's merge it and await the feedback!

Copy link
Collaborator

@hansemannn hansemannn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing work, really excited to use this in production apps soon!

@hansemannn hansemannn merged commit e68d33a into tidev:master May 3, 2023
6 checks passed
@m1ga
Copy link
Contributor Author

m1ga commented May 3, 2023

awesome! Thanks!

@m1ga m1ga deleted the cameraX branch May 3, 2023 20:51
narbs pushed a commit to narbs/titanium_mobile that referenced this pull request Jun 7, 2023
* camerax overlay

* close camera

* android back, switch lens

* flash

* save image

* video start

* change video camera

* bit rate, max resolution, bug fixes

* update library version

* remove old files

* version update, permission check

* version update

* max video duration

* update video part

* get video from gallery, add recording callback

* image: save to gallery

* error msg, clean up recordings

* cleanup

* pinch to zoom

* revert Android studio styling

* docs: remove empty description

* update camerax version

* update

* min/maxZoomLevel, zoomLevel

* final version

* update

* move try/catch

* torch, set flash mode

* remove duplicated code

* read camera sizes and be able to set them

* raise cameraX library

* aspectRatio, open callback, scalingMode

* verticalAlign

* add old camera files again

* better defaults

* minor update to camerax version

* update files
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Android: CameraX
4 participants