diff --git a/CAMERA2_INTEGRATION.md b/CAMERA2_INTEGRATION.md new file mode 100644 index 00000000..b1ab2d5a --- /dev/null +++ b/CAMERA2_INTEGRATION.md @@ -0,0 +1,165 @@ +# Camera2 API Integration + +## Overview + +This document describes the Camera2 API integration that has been added to the android-gpuimage-plus library. The implementation provides a flexible camera backend system that supports both the legacy Camera API and the modern Camera2 API. + +## Features + +- **Backward Compatibility**: Existing code continues to work with the legacy Camera API by default +- **Camera2 Support**: Full Camera2 API support for devices with API level 21+ +- **Runtime Switching**: Users can choose which camera backend to use through UI controls +- **Automatic Fallback**: Graceful fallback to legacy API when Camera2 is not available +- **Persistent Settings**: User camera backend preferences are saved and restored + +## Architecture + +### Core Components + +1. **CameraBackend** - Abstract base class defining the camera interface +2. **CameraBackendLegacy** - Implementation wrapping the legacy Camera API +3. **CameraBackend2** - Implementation using the Camera2 API +4. **CameraBackendFactory** - Factory class for creating camera backends +5. **CameraInstance** - Modified singleton that now delegates to backends + +### Backend Selection + +The camera backend can be selected using: + +```java +// Set Camera2 backend +CameraInstance.setCameraBackendType(CameraBackendFactory.CameraBackendType.CAMERA2); + +// Set legacy backend +CameraInstance.setCameraBackendType(CameraBackendFactory.CameraBackendType.LEGACY); + +// Automatic selection (Camera2 if available, otherwise legacy) +CameraInstance.setCameraBackendType(CameraBackendFactory.CameraBackendType.AUTO); +``` + +## Usage + +### For Library Users + +The library remains fully backward compatible. No changes are required for existing code. + +To enable Camera2: + +```java +// Initialize with context (required for Camera2) +CameraInstance.getInstance().initializeWithContext(context); + +// Enable Camera2 backend +CameraInstance.setCameraBackendType(CameraBackendFactory.CameraBackendType.CAMERA2); + +// Use camera normally +CameraInstance.getInstance().tryOpenCamera(callback); +``` + +### For Demo Application + +The demo application now includes a configuration panel in MainActivity: + +1. **Checkbox**: "Use Camera2 API" - toggles between Camera backends +2. **Status Display**: Shows current backend status and device compatibility +3. **Info Button**: Displays detailed runtime information + +Settings are automatically saved and restored between app sessions. + +## Implementation Details + +### CameraBackend Interface + +The `CameraBackend` abstract class defines a unified interface for camera operations: + +- `tryOpenCamera()` - Open camera with specified facing +- `stopCamera()` - Close camera and cleanup +- `startPreview()` / `stopPreview()` - Control preview +- `setPictureSize()` / `setFocusMode()` - Configuration +- `focusAtPoint()` - Touch-to-focus functionality + +### Legacy Compatibility + +The `CameraBackendLegacy` class wraps the existing Camera API implementation, ensuring: + +- All existing functionality is preserved +- No breaking changes to the API +- Same behavior as before the refactoring + +### Camera2 Implementation + +The `CameraBackend2` class provides: + +- Modern Camera2 API usage with CameraDevice and CameraCaptureSession +- Background thread handling for camera operations +- Proper lifecycle management +- Permission checking +- Size selection and configuration + +### Context Initialization + +Camera2 requires a Context for CameraManager access. The library now supports context initialization: + +```java +// Initialize once, typically in Application or Activity +CameraInstance.getInstance().initializeWithContext(context); +``` + +This is automatically handled in CameraGLSurfaceView constructor. + +## Error Handling + +The implementation includes comprehensive error handling: + +- **Permission Errors**: Camera2 checks for camera permissions +- **Device Compatibility**: Automatic fallback when Camera2 is not supported +- **Camera Access Errors**: Proper cleanup on camera open failures +- **Lifecycle Errors**: Safe handling of Activity/Context lifecycle + +## Testing + +Use `CameraBackendTest` class for validation: + +```java +// Run comprehensive backend tests +CameraBackendTest.testCameraBackends(context); + +// Get runtime information +String info = CameraBackendTest.getRuntimeInfo(context); +``` + +## Benefits + +1. **Future-Proof**: Ready for modern Android camera capabilities +2. **Better Performance**: Camera2 API provides better control and performance +3. **Enhanced Features**: Access to Camera2-specific features like manual controls +4. **Maintained Compatibility**: Existing applications continue to work unchanged +5. **User Choice**: Developers and users can choose the best backend for their needs + +## Requirements + +- **Minimum SDK**: API 21 (Android 5.0) for Camera2 features +- **Permissions**: CAMERA permission required (same as before) +- **Context**: Application context needed for Camera2 initialization + +## Migration Guide + +### For Existing Applications + +No changes required - applications continue to work with legacy Camera API by default. + +### To Enable Camera2 + +1. Ensure `initializeWithContext()` is called (automatic in CameraGLSurfaceView) +2. Set Camera2 backend type +3. Test on target devices +4. Consider providing user choice in settings + +## Future Enhancements + +The architecture supports future enhancements: + +- Camera2 advanced features (manual controls, RAW capture, etc.) +- CameraX backend implementation +- Device-specific optimizations +- Feature detection and capabilities reporting \ No newline at end of file diff --git a/UI_CHANGES.md b/UI_CHANGES.md new file mode 100644 index 00000000..c41765ad --- /dev/null +++ b/UI_CHANGES.md @@ -0,0 +1,49 @@ +# UI Changes Visualization + +## MainActivity UI Enhancement + +The MainActivity now includes a Camera Configuration panel at the top: + +``` +┌─ MainActivity ──────────────────────────────┐ +│ │ +│ Camera Configuration │ +│ ┌──────────────────────────────────────────┐ │ +│ │ □ Use Camera2 API (requires API 21+) │ │ +│ │ │ │ +│ │ Camera2 availability: ✓ Supported │ │ +│ │ (API XX) │ │ +│ │ │ │ +│ │ [Show Camera Backend Info] │ │ +│ └──────────────────────────────────────────┘ │ +│ │ +│ Basic Image Filter Demo │ +│ Advanced Image Filter Demo │ +│ Image Deform Demo │ +│ Camera Filter Demo │ +│ Multi Input Demo │ +│ Simple Player Demo │ +│ Video Player Demo │ +│ Face Tracking Demo │ +│ Image Demo WithMatrix │ +│ Test Cases │ +│ │ +└──────────────────────────────────────────────┘ +``` + +## User Interaction Flow + +1. **Checkbox Interaction**: Users can toggle between Camera APIs +2. **Status Display**: Real-time feedback on Camera2 availability +3. **Persistent Settings**: Preferences saved between app sessions +4. **Info Button**: Displays detailed backend information +5. **Toast Messages**: Feedback when settings change + +## Technical Implementation + +- Settings are stored in SharedPreferences +- Camera backend is automatically applied +- Graceful fallback for unsupported devices +- Comprehensive logging for debugging + +This implementation provides a user-friendly way to test and compare both camera backends while maintaining full backward compatibility. \ No newline at end of file diff --git a/cgeDemo/src/main/java/org/wysaid/cgeDemo/CameraDemoActivity.java b/cgeDemo/src/main/java/org/wysaid/cgeDemo/CameraDemoActivity.java index f7ba6494..7eeb1697 100755 --- a/cgeDemo/src/main/java/org/wysaid/cgeDemo/CameraDemoActivity.java +++ b/cgeDemo/src/main/java/org/wysaid/cgeDemo/CameraDemoActivity.java @@ -124,6 +124,12 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_camera_demo); PermissionUtil.verifyStoragePermissions(this); + // Log which camera backend is being used + Log.i(LOG_TAG, "=== CameraDemoActivity ==="); + Log.i(LOG_TAG, "Camera backend configuration: " + CameraInstance.getCameraBackendInfo()); + Log.i(LOG_TAG, "Camera2 supported: " + CameraInstance.isCamera2Supported()); + Log.i(LOG_TAG, "Selected backend type: " + CameraInstance.getCameraBackendType()); + // lastVideoPathFileName = FileUtil.getPathInPackage(CameraDemoActivity.this, true) + "/lastVideoPath.txt"; Button takePicBtn = (Button) findViewById(R.id.takePicBtn); Button takeShotBtn = (Button) findViewById(R.id.takeShotBtn); diff --git a/cgeDemo/src/main/java/org/wysaid/cgeDemo/MainActivity.java b/cgeDemo/src/main/java/org/wysaid/cgeDemo/MainActivity.java index 299b2df6..486b33be 100644 --- a/cgeDemo/src/main/java/org/wysaid/cgeDemo/MainActivity.java +++ b/cgeDemo/src/main/java/org/wysaid/cgeDemo/MainActivity.java @@ -2,6 +2,7 @@ import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.res.AssetManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -12,13 +13,18 @@ import android.view.MenuItem; import android.view.View; import android.widget.Button; +import android.widget.CheckBox; import android.widget.LinearLayout; +import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import java.io.IOException; import java.io.InputStream; +import org.wysaid.camera.CameraBackendFactory; +import org.wysaid.camera.CameraBackendTest; +import org.wysaid.camera.CameraInstance; import org.wysaid.common.Common; import org.wysaid.myUtils.MsgUtil; import org.wysaid.myUtils.PermissionUtil; @@ -28,6 +34,11 @@ public class MainActivity extends AppCompatActivity { public static final String LOG_TAG = "wysaid"; + private static final String PREFS_NAME = "CameraSettings"; + private static final String CAMERA2_ENABLED_KEY = "camera2_enabled"; + + private CheckBox mCamera2CheckBox; + private TextView mCamera2StatusText; public static final String EFFECT_CONFIGS[] = { "", @@ -242,6 +253,8 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + setupCameraConfiguration(); + LinearLayout mLayout = (LinearLayout) findViewById(R.id.buttonLayout); for (DemoClassDescription demo : mDemos) { @@ -256,6 +269,77 @@ protected void onCreate(Bundle savedInstanceState) { PermissionUtil.verifyStoragePermissions(this); } + private void setupCameraConfiguration() { + mCamera2CheckBox = findViewById(R.id.camera2CheckBox); + mCamera2StatusText = findViewById(R.id.camera2StatusText); + Button showInfoButton = findViewById(R.id.showInfoButton); + + // Check Camera2 availability + boolean isCamera2Supported = CameraInstance.isCamera2Supported(); + String statusMessage = "Camera2 availability: " + + (isCamera2Supported ? "✓ Supported (API " + android.os.Build.VERSION.SDK_INT + ")" : + "✗ Not supported (API " + android.os.Build.VERSION.SDK_INT + " < 21)"); + mCamera2StatusText.setText(statusMessage); + + if (!isCamera2Supported) { + mCamera2CheckBox.setEnabled(false); + mCamera2CheckBox.setText("Use Camera2 API (not available on this device)"); + } + + // Load saved preference + SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE); + boolean camera2Enabled = prefs.getBoolean(CAMERA2_ENABLED_KEY, false); + mCamera2CheckBox.setChecked(camera2Enabled && isCamera2Supported); + + // Apply saved setting + applyCameraSetting(camera2Enabled && isCamera2Supported); + + // Set up checkbox listener + mCamera2CheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> { + applyCameraSetting(isChecked); + saveCameraSetting(isChecked); + + // Show a toast message about the change + String message = isChecked ? + "Camera2 API enabled. Restart camera activities to take effect." : + "Legacy Camera API enabled. Restart camera activities to take effect."; + MsgUtil.toastMsg(MainActivity.this, message); + }); + + // Set up info button + showInfoButton.setOnClickListener(v -> { + String info = CameraBackendTest.getRuntimeInfo(MainActivity.this); + Log.i(LOG_TAG, "Camera Backend Info:\n" + info); + MsgUtil.toastMsg(MainActivity.this, "Camera backend info logged - check logcat"); + + // Also run the test + CameraBackendTest.testCameraBackends(MainActivity.this); + }); + } + + private void applyCameraSetting(boolean useCamera2) { + CameraBackendFactory.CameraBackendType backendType = useCamera2 ? + CameraBackendFactory.CameraBackendType.CAMERA2 : + CameraBackendFactory.CameraBackendType.LEGACY; + + CameraInstance.setCameraBackendType(backendType); + + String statusText = useCamera2 ? + "✓ Using Camera2 API backend" : + "Using Legacy Camera API backend"; + Log.i(LOG_TAG, statusText); + + // Update status text with more details + String detailedStatus = statusText + " | Backend info: " + CameraInstance.getCameraBackendInfo(); + mCamera2StatusText.setText(detailedStatus); + } + + private void saveCameraSetting(boolean useCamera2) { + SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE); + prefs.edit().putBoolean(CAMERA2_ENABLED_KEY, useCamera2).apply(); + Log.i(LOG_TAG, "Camera setting saved: " + (useCamera2 ? "Camera2" : "Legacy")); + } + // @Override // public void onDestroy() { // super.onDestroy(); diff --git a/cgeDemo/src/main/res/layout/activity_main.xml b/cgeDemo/src/main/res/layout/activity_main.xml index 9a7a4933..6e3c1697 100644 --- a/cgeDemo/src/main/res/layout/activity_main.xml +++ b/cgeDemo/src/main/res/layout/activity_main.xml @@ -8,11 +8,56 @@ android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity"> + + + + + + + + +