scoplan.camera 1.2.5 → 1.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ ---
2
+ alwaysApply: true
3
+ always_on: true
4
+ trigger: always_on
5
+ applyTo: "**"
6
+ description: Snyk Security At Inception
7
+ ---
8
+
9
+ # Project security best practices
10
+
11
+ - Always run snyk_code_scan tool for new first party code that is generated in a Snyk-supported language.
12
+ - If any security issues are found based on newly introduced or modified code or dependencies, attempt to fix the issues using the results context from Snyk.
13
+ - Rescan the code after fixing the issues to ensure that the issues were fixed and that there are no newly introduced issues.
14
+ - Repeat this process until no new issues are found.
@@ -0,0 +1,3 @@
1
+ {
2
+ "snyk.advanced.autoSelectOrganization": true
3
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scoplan.camera",
3
- "version": "1.2.5",
3
+ "version": "1.2.10",
4
4
  "description": "",
5
5
  "cordova": {
6
6
  "id": "scoplan.camera",
package/plugin.xml CHANGED
@@ -65,7 +65,7 @@
65
65
  <framework custom="true" src="src/android/gradle/scoplanCamera.gradle" type="gradleReference" />
66
66
  <framework src="com.google.android.material:material:1.5.0-alpha02" />
67
67
  <framework src="com.squareup.picasso:picasso:2.8" />
68
- <framework src="io.sentry:sentry-android:7.2.0" />
68
+ <framework src="io.sentry:sentry-android:8.31.0" />
69
69
  <framework src="androidx.constraintlayout:constraintlayout:2.1.4" />
70
70
  <lib-file src="src/android/libs/ds-photo-editor-sdk-v9.aar" />
71
71
  </platform>
@@ -2,6 +2,12 @@ package scoplan.camera;
2
2
 
3
3
  import android.content.Context;
4
4
  import android.os.Bundle;
5
+ import android.view.View;
6
+
7
+ import androidx.core.graphics.Insets;
8
+ import androidx.core.view.ViewCompat;
9
+ import androidx.core.view.WindowCompat;
10
+ import androidx.core.view.WindowInsetsCompat;
5
11
 
6
12
  import com.dsphotoeditor.sdk.activity.DsPhotoEditorCropActivity;
7
13
 
@@ -13,5 +19,16 @@ public class CCropActivity extends DsPhotoEditorCropActivity {
13
19
  int themeId = context.getResources().getIdentifier("AppTheme.NoActionBar", "style", context.getPackageName());
14
20
  setTheme(themeId);
15
21
  super.onCreate(savedInstanceState);
22
+
23
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
24
+
25
+ View rootView = findViewById(com.dsphotoeditor.sdk.R.id.ds_photo_editor_crop_root_layout);
26
+ if (rootView != null) {
27
+ ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, windowInsets) -> {
28
+ Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
29
+ v.setPadding(insets.left, insets.top, insets.right, insets.bottom);
30
+ return WindowInsetsCompat.CONSUMED;
31
+ });
32
+ }
16
33
  }
17
34
  }
@@ -2,6 +2,13 @@ package scoplan.camera;
2
2
 
3
3
  import android.content.Context;
4
4
  import android.os.Bundle;
5
+ import android.view.View;
6
+
7
+ import androidx.core.graphics.Insets;
8
+ import androidx.core.view.ViewCompat;
9
+ import androidx.core.view.WindowCompat;
10
+ import androidx.core.view.WindowInsetsCompat;
11
+
5
12
  import com.dsphotoeditor.sdk.activity.DsPhotoEditorDrawActivity;
6
13
 
7
14
  public class CDrawActivity extends DsPhotoEditorDrawActivity {
@@ -12,5 +19,16 @@ public class CDrawActivity extends DsPhotoEditorDrawActivity {
12
19
  int themeId = context.getResources().getIdentifier("AppTheme.NoActionBar", "style", context.getPackageName());
13
20
  setTheme(themeId);
14
21
  super.onCreate(savedInstanceState);
22
+
23
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
24
+
25
+ View rootView = findViewById(com.dsphotoeditor.sdk.R.id.ds_photo_editor_draw_root_layout);
26
+ if (rootView != null) {
27
+ ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, windowInsets) -> {
28
+ Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
29
+ v.setPadding(insets.left, insets.top, insets.right, insets.bottom);
30
+ return WindowInsetsCompat.CONSUMED;
31
+ });
32
+ }
15
33
  }
16
34
  }
@@ -2,6 +2,13 @@ package scoplan.camera;
2
2
 
3
3
  import android.content.Context;
4
4
  import android.os.Bundle;
5
+ import android.view.View;
6
+
7
+ import androidx.core.graphics.Insets;
8
+ import androidx.core.view.ViewCompat;
9
+ import androidx.core.view.WindowCompat;
10
+ import androidx.core.view.WindowInsetsCompat;
11
+
5
12
  import com.dsphotoeditor.sdk.activity.DsPhotoEditorTextActivity;
6
13
 
7
14
  public class CTextActivity extends DsPhotoEditorTextActivity {
@@ -12,6 +19,17 @@ public class CTextActivity extends DsPhotoEditorTextActivity {
12
19
  int themeId = context.getResources().getIdentifier("AppTheme.NoActionBar", "style", context.getPackageName());
13
20
  setTheme(themeId);
14
21
  super.onCreate(savedInstanceState);
22
+
23
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
24
+
25
+ View rootView = findViewById(com.dsphotoeditor.sdk.R.id.ds_photo_editor_text_sticker_root_layout);
26
+ if (rootView != null) {
27
+ ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, windowInsets) -> {
28
+ Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
29
+ v.setPadding(insets.left, insets.top, insets.right, insets.bottom);
30
+ return WindowInsetsCompat.CONSUMED;
31
+ });
32
+ }
15
33
  }
16
34
  }
17
35
 
@@ -31,6 +31,7 @@ import androidx.activity.result.ActivityResult;
31
31
  import androidx.activity.result.ActivityResultLauncher;
32
32
  import androidx.activity.result.contract.ActivityResultContracts;
33
33
  import androidx.annotation.NonNull;
34
+ import androidx.annotation.Nullable;
34
35
  import androidx.core.app.ActivityCompat;
35
36
  import androidx.core.view.WindowCompat;
36
37
  import androidx.core.view.WindowInsetsControllerCompat;
@@ -41,6 +42,7 @@ import android.os.Handler;
41
42
  import android.os.HandlerThread;
42
43
  import android.util.Log;
43
44
  import android.util.Size;
45
+ import android.view.ContextThemeWrapper;
44
46
  import android.view.LayoutInflater;
45
47
  import android.view.OrientationEventListener;
46
48
  import android.view.Surface;
@@ -66,6 +68,7 @@ import java.util.Arrays;
66
68
  import java.util.Date;
67
69
  import java.util.List;
68
70
 
71
+ import capacitor.cordova.android.plugins.R;
69
72
  import io.sentry.Sentry;
70
73
 
71
74
  public class CameraFragment extends Fragment implements scoplan.camera.OnImageCaptureListener, View.OnClickListener {
@@ -87,7 +90,7 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
87
90
  private OrientationEventListener orientationEventListener;
88
91
  private CameraCaptureSession cameraCaptureSessions;
89
92
  private CaptureRequest.Builder captureRequestBuilder;
90
- private String cameraId;
93
+ private String cameraId = null;
91
94
  private int CAMERA_REQUEST_PERMISSION = 5000;
92
95
  private Handler mBackgroundHandler;
93
96
  private HandlerThread mBackgroundThread;
@@ -101,17 +104,41 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
101
104
  private int currentOrientation = -1;
102
105
  private boolean flashOn = false;
103
106
  private boolean cameraIsOpen = false;
104
-
107
+ private boolean previewReady = false;
105
108
  private SurfaceHolder mSurfaceHolder;
106
109
 
107
110
  private boolean surfaceAvailable = false;
111
+ private boolean surfaceSizeConfigured = false;
112
+ private Size optimalPreviewSize = null;
108
113
  private LinearLayout cameraFrameLayout;
114
+ private boolean callbackAdded = false;
115
+
116
+ @Override
117
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
118
+ super.onViewCreated(view, savedInstanceState);
119
+ if (!callbackAdded) {
120
+ mSurfaceHolder.addCallback(surfaceHolderCallBack);
121
+ callbackAdded = true;
122
+ }
123
+
124
+ // Apply window insets to root view to handle system bars
125
+ androidx.core.view.ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> {
126
+ androidx.core.graphics.Insets insets = windowInsets.getInsets(androidx.core.view.WindowInsetsCompat.Type.systemBars());
127
+
128
+ // Apply padding to root view
129
+ v.setPadding(insets.left, insets.top, insets.right, insets.bottom);
130
+
131
+ return androidx.core.view.WindowInsetsCompat.CONSUMED;
132
+ });
133
+ }
109
134
 
110
135
  private final SurfaceHolder.Callback surfaceHolderCallBack = new SurfaceHolder.Callback() {
111
136
  @Override
112
137
  public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
113
138
  surfaceAvailable = true;
114
- openCamera();
139
+ if(!cameraIsOpen) {
140
+ openCamera();
141
+ }
115
142
  }
116
143
 
117
144
  @Override
@@ -121,8 +148,8 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
121
148
 
122
149
  @Override
123
150
  public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
124
- //TODO close
125
151
  surfaceAvailable = false;
152
+ surfaceSizeConfigured = false;
126
153
  release();
127
154
  }
128
155
  };
@@ -132,19 +159,24 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
132
159
  public void onOpened(@NonNull CameraDevice camera) {
133
160
  cameraDevice = camera;
134
161
  cameraIsOpen = true;
135
- createCameraPreview();
162
+ // Configure surface size first; surfaceChanged will trigger createCameraPreview
163
+ configureSurfaceSize();
136
164
  }
137
165
 
138
166
  @Override
139
- public void onDisconnected(@NonNull CameraDevice cameraDevice) {
140
- cameraDevice.close();
167
+ public void onDisconnected(@NonNull CameraDevice camera) {
141
168
  cameraIsOpen = false;
169
+ previewReady = false;
170
+ surfaceSizeConfigured = false;
142
171
  release();
143
172
  }
144
173
 
145
174
  @Override
146
- public void onError(@NonNull CameraDevice cameraDevice, int i) {
147
- cameraDevice.close();
175
+ public void onError(@NonNull CameraDevice camera, int error) {
176
+ Log.e(SCOPLAN_TAG, "CameraDevice.StateCallback onError: " + error);
177
+ cameraIsOpen = false;
178
+ previewReady = false;
179
+ surfaceSizeConfigured = false;
148
180
  release();
149
181
  }
150
182
  };
@@ -198,13 +230,29 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
198
230
  }
199
231
  };
200
232
 
201
- this.fakeR = new FakeR(getContext());
233
+ this.fakeR = new FakeR(requireActivity());
234
+ }
235
+
236
+ private int findStyleResId(Context context, String styleName) {
237
+ // Convert dotted names (AppTheme.NoActionBar) to compiled names (AppTheme_NoActionBar)
238
+ String compiledName = styleName.replace('.', '_');
239
+
240
+ int resId = context.getResources().getIdentifier(
241
+ compiledName,
242
+ "style",
243
+ context.getPackageName()
244
+ );
245
+
246
+ return resId;
202
247
  }
203
248
 
204
249
  @Override
205
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
250
+ public View onCreateView(LayoutInflater outerInflater, ViewGroup container,
206
251
  Bundle savedInstanceState) {
207
252
  // Inflate the layout for this fragment
253
+ int themeId = getActivity().getResources().getIdentifier("AppTheme.fullscreen", "style", getActivity().getPackageName());
254
+ ContextThemeWrapper themeContext = new ContextThemeWrapper(requireActivity(), themeId);
255
+ LayoutInflater inflater = outerInflater.cloneInContext(themeContext);
208
256
  View view = inflater.inflate(this.fakeR.getLayout("fragment_camera"), container, false);
209
257
  camButton = view.findViewById(this.fakeR.getId("button_capture"));
210
258
  zoomBar = view.findViewById(this.fakeR.getId("camera_zoom"));
@@ -217,7 +265,6 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
217
265
  drawOn = view.findViewById(this.fakeR.getId("draw_on"));
218
266
  assert surfaceView != null;
219
267
  mSurfaceHolder = surfaceView.getHolder();
220
- mSurfaceHolder.addCallback(surfaceHolderCallBack);
221
268
  souche = view.findViewById(this.fakeR.getId("image_souche"));
222
269
  cameraTopBar = view.findViewById(this.fakeR.getId("cameraTopBar"));
223
270
  cancelBtn = view.findViewById(this.fakeR.getId("cancel"));
@@ -233,51 +280,113 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
233
280
  validationButton.setOnClickListener(this);
234
281
  flashBtn.setOnClickListener(this);
235
282
  this.defineViewVisibility();
283
+
236
284
  return view;
237
285
  }
238
286
 
287
+ private void configureSurfaceSize() {
288
+ if (cameraId == null || getActivity() == null) {
289
+ // Can't configure yet, fall back to direct preview
290
+ surfaceSizeConfigured = true;
291
+ createCameraPreview();
292
+ return;
293
+ }
294
+ try {
295
+ CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
296
+ StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
297
+ if (map != null) {
298
+ Size[] outputSizes = map.getOutputSizes(SurfaceTexture.class);
299
+ optimalPreviewSize = selectedPreviewSize(outputSizes);
300
+ if (optimalPreviewSize != null && cameraFrameLayout.getWidth() > 0) {
301
+ Size displaySize = calculateSurfaceSize(optimalPreviewSize);
302
+ getActivity().runOnUiThread(() -> {
303
+ if (!isAdded()) return;
304
+ ViewGroup.LayoutParams layoutParams = surfaceView.getLayoutParams();
305
+ boolean sizeChanged = layoutParams.width != displaySize.getWidth()
306
+ || layoutParams.height != displaySize.getHeight();
307
+ layoutParams.width = displaySize.getWidth();
308
+ layoutParams.height = displaySize.getHeight();
309
+ surfaceView.setLayoutParams(layoutParams);
310
+ // Set buffer to camera native resolution
311
+ mSurfaceHolder.setFixedSize(optimalPreviewSize.getWidth(), optimalPreviewSize.getHeight());
312
+ surfaceSizeConfigured = true;
313
+ // If size didn't change, surfaceChanged won't fire, so call createCameraPreview directly
314
+ if (!sizeChanged) {
315
+ createCameraPreview();
316
+ }
317
+ // Otherwise surfaceChanged will be called → createCameraPreview
318
+ });
319
+ return;
320
+ }
321
+ }
322
+ } catch (Exception e) {
323
+ Log.e(SCOPLAN_TAG, "Error configuring surface size", e);
324
+ }
325
+ // Fallback: proceed directly
326
+ surfaceSizeConfigured = true;
327
+ createCameraPreview();
328
+ }
329
+
239
330
  private void createCameraPreview() {
240
- if(cameraDevice == null || !surfaceAvailable)
331
+ if(cameraDevice == null || !surfaceAvailable || previewReady || !surfaceSizeConfigured)
241
332
  return;
242
333
  if(!cameraIsOpen) {
243
334
  this.openCamera();
335
+ return;
244
336
  }
337
+ previewReady = true;
245
338
  try{
339
+ // Close any existing session before creating a new one
340
+ closeSession();
341
+
246
342
  Surface surface = mSurfaceHolder.getSurface();
247
343
  captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
248
344
  captureRequestBuilder.addTarget(surface);
249
345
  cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
250
346
  @Override
251
347
  public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
252
- if(cameraDevice == null)
348
+ if(cameraDevice == null) {
349
+ previewReady = false;
253
350
  return;
351
+ }
254
352
  cameraCaptureSessions = cameraCaptureSession;
255
353
  updatePreview();
256
354
  }
257
355
 
258
356
  @Override
259
357
  public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
260
- Sentry.captureMessage(cameraCaptureSession.toString());
358
+ Log.e(SCOPLAN_TAG, "onConfigureFailed for preview session");
359
+ Sentry.captureMessage("onConfigureFailed: " + cameraCaptureSession.toString());
360
+ previewReady = false;
361
+ failedCapture();
261
362
  }
262
- },null);
363
+ }, mBackgroundHandler);
263
364
  } catch (Exception e) {
365
+ Log.e(SCOPLAN_TAG, "Error creating camera preview", e);
264
366
  Sentry.captureException(e);
367
+ previewReady = false;
265
368
  this.failedCapture();
266
369
  }
267
370
  }
268
371
 
269
372
  private void openCamera() {
270
373
  try{
374
+ if (cameraId != null ) {
375
+ return;
376
+ }
271
377
  for (String id : manager.getCameraIdList()) {
272
378
  CameraCharacteristics cameraCharacteristics =
273
379
  manager.getCameraCharacteristics(id);
274
- if (cameraCharacteristics.get(cameraCharacteristics.LENS_FACING) ==
380
+ if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) ==
275
381
  CameraCharacteristics.LENS_FACING_BACK) {
276
382
  cameraId = id;
277
383
  break;
278
384
  }
279
385
  }
280
- cameraId = manager.getCameraIdList()[0];
386
+ // Fallback to first camera only if no back camera was found
387
+ if (cameraId == null) {
388
+ cameraId = manager.getCameraIdList()[0];
389
+ }
281
390
  CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
282
391
  StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
283
392
  assert map != null;
@@ -310,28 +419,9 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
310
419
  }
311
420
 
312
421
  private void updatePreview() {
313
- if(cameraDevice == null)
314
- Log.e(SCOPLAN_TAG, "Error camera");
315
- captureRequestBuilder.set(CaptureRequest.CONTROL_MODE,CaptureRequest.CONTROL_MODE_AUTO);
316
- try{
317
- CameraCharacteristics cameraCharacteristics =
318
- manager.getCameraCharacteristics(cameraId);
319
- StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
320
- if(map != null) {
321
- Size[] outputSizes = map.getOutputSizes(SurfaceTexture.class);
322
- Size preferredSize = selectedPreviewSize(outputSizes);
323
- if(preferredSize != null) {
324
- ViewGroup.LayoutParams layoutParams = surfaceView.getLayoutParams();
325
- Size surfaceSize = calculateSurfaceSize(preferredSize);
326
- getActivity().runOnUiThread(() -> {
327
- layoutParams.width = surfaceSize.getWidth();
328
- layoutParams.height = surfaceSize.getHeight();
329
- mSurfaceHolder.setFixedSize(surfaceSize.getWidth(), surfaceSize.getHeight());
330
- surfaceView.setLayoutParams(layoutParams);
331
- });
332
- }
333
- }
334
- cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(),null, mBackgroundHandler);
422
+ captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
423
+ try {
424
+ cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, mBackgroundHandler);
335
425
  this.cameraSeekBarListener = new scoplan.camera.CameraSeekBarListener(cameraId, manager, zoomBar, captureRequestBuilder, cameraCaptureSessions, mBackgroundHandler);
336
426
  } catch (Exception e) {
337
427
  Sentry.captureException(e);
@@ -341,23 +431,54 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
341
431
 
342
432
  private Size calculateSurfaceSize(Size selectedSize) {
343
433
  int parentWidth = this.cameraFrameLayout.getWidth();
344
- Size correctSize = new Size(selectedSize.getHeight(), selectedSize.getWidth()); // We are in portrait mode
434
+ int parentHeight = this.cameraFrameLayout.getHeight();
435
+
436
+ // Camera sensor is landscape, but we display in portrait → swap W/H
437
+ float previewAspectRatio = (float) selectedSize.getHeight() / selectedSize.getWidth();
345
438
 
346
- float aspectRatio = (float) correctSize.getWidth() / correctSize.getHeight();
439
+ // Center-crop: fill the entire parent, crop overflow (no distortion)
440
+ int surfaceWidth, surfaceHeight;
441
+ int fitWidth = parentWidth;
442
+ int fitHeight = (int) (parentWidth / previewAspectRatio);
443
+
444
+ if (fitHeight >= parentHeight) {
445
+ // Fit by width already fills the height — use it
446
+ surfaceWidth = fitWidth;
447
+ surfaceHeight = fitHeight;
448
+ } else {
449
+ // Fit by height and let width overflow
450
+ surfaceHeight = parentHeight;
451
+ surfaceWidth = (int) (parentHeight * previewAspectRatio);
452
+ }
347
453
 
348
- int height = (int) (parentWidth / aspectRatio);
349
- return new Size(parentWidth, height);
454
+ return new Size(surfaceWidth, surfaceHeight);
350
455
  }
351
456
 
352
457
  private Size selectedPreviewSize(Size[] outputSizes) {
353
- float targetAspectRatio = 1f; // For example, 16:9 aspect ratio
354
- // Choose a suitable size from outputSizes based on the aspect ratio
458
+ // Target 4:3 aspect ratio (landscape sensor: width > height, so 4/3 ≈ 1.333)
459
+ float targetAspectRatio = 4f / 3f;
355
460
  Size selectedSize = null;
461
+ int bestArea = 0;
462
+
356
463
  for (Size size : outputSizes) {
357
464
  float aspectRatio = (float) size.getWidth() / size.getHeight();
358
- if (Math.abs(aspectRatio - targetAspectRatio) < 0.1) {
359
- selectedSize = size;
360
- break;
465
+ if (Math.abs(aspectRatio - targetAspectRatio) < 0.05) {
466
+ // Among matching ratios, pick the largest for best quality
467
+ int area = size.getWidth() * size.getHeight();
468
+ if (area > bestArea) {
469
+ bestArea = area;
470
+ selectedSize = size;
471
+ }
472
+ }
473
+ }
474
+
475
+ // Fallback: if no 4:3, pick the largest size available
476
+ if (selectedSize == null && outputSizes.length > 0) {
477
+ selectedSize = outputSizes[0];
478
+ for (Size size : outputSizes) {
479
+ if (size.getWidth() * size.getHeight() > selectedSize.getWidth() * selectedSize.getHeight()) {
480
+ selectedSize = size;
481
+ }
361
482
  }
362
483
  }
363
484
  return selectedSize;
@@ -367,23 +488,22 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
367
488
  public void onResume() {
368
489
  super.onResume();
369
490
  startBackgroundThread();
370
- if(surfaceAvailable)
491
+ if(surfaceAvailable && !cameraIsOpen) {
371
492
  openCamera();
372
- else {
373
- mSurfaceHolder.addCallback(surfaceHolderCallBack);
374
493
  }
375
494
  this.orientationEventListener.enable();
376
495
 
377
496
  Window window = requireActivity().getWindow();
378
497
 
498
+ // Enable edge-to-edge mode but keep system bars visible
499
+ WindowCompat.setDecorFitsSystemWindows(window, false);
500
+
379
501
  WindowInsetsControllerCompat controller =
380
502
  WindowCompat.getInsetsController(window, window.getDecorView());
381
503
 
382
504
  if (controller != null) {
383
- controller.hide(WindowInsets.Type.statusBars());
384
- controller.setSystemBarsBehavior(
385
- WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
386
- );
505
+ // Show status and navigation bars
506
+ controller.show(WindowInsets.Type.systemBars());
387
507
  }
388
508
  }
389
509
 
@@ -393,11 +513,15 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
393
513
  super.onPause();
394
514
  this.orientationEventListener.disable();
395
515
  Window window = requireActivity().getWindow();
516
+
517
+ // Restore normal window mode when leaving camera
518
+ WindowCompat.setDecorFitsSystemWindows(window, true);
519
+
396
520
  WindowInsetsControllerCompat controller =
397
521
  WindowCompat.getInsetsController(window, window.getDecorView());
398
522
 
399
523
  if (controller != null) {
400
- controller.show(WindowInsets.Type.statusBars());
524
+ controller.show(WindowInsets.Type.systemBars());
401
525
  }
402
526
  release();
403
527
  }
@@ -408,23 +532,45 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
408
532
  release();
409
533
  }
410
534
 
535
+ private void closeSession() {
536
+ if(cameraCaptureSessions != null) {
537
+ try {
538
+ cameraCaptureSessions.close();
539
+ } catch (Exception e) {
540
+ Log.e(SCOPLAN_TAG, "Error closing capture session", e);
541
+ }
542
+ cameraCaptureSessions = null;
543
+ }
544
+ }
545
+
411
546
  private void release() {
547
+ closeSession();
412
548
  if(cameraDevice != null) {
413
- cameraDevice.close();
549
+ try {
550
+ cameraDevice.close();
551
+ } catch (Exception e) {
552
+ Log.e(SCOPLAN_TAG, "Error closing camera device", e);
553
+ }
414
554
  cameraIsOpen = false;
555
+ previewReady = false;
556
+ surfaceSizeConfigured = false;
415
557
  cameraDevice = null;
558
+ cameraId = null;
416
559
  }
417
560
  }
418
561
 
419
562
  private void stopBackgroundThread() {
563
+ if (mBackgroundThread == null) {
564
+ return;
565
+ }
420
566
  mBackgroundThread.quitSafely();
421
567
  try{
422
568
  mBackgroundThread.join();
423
- mBackgroundThread= null;
424
- mBackgroundHandler = null;
425
569
  } catch (InterruptedException e) {
426
- e.printStackTrace();
570
+ Log.e(SCOPLAN_TAG, "Error stopping background thread", e);
427
571
  }
572
+ mBackgroundThread = null;
573
+ mBackgroundHandler = null;
428
574
  }
429
575
 
430
576
  private void startBackgroundThread() {
@@ -442,15 +588,18 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
442
588
  }
443
589
 
444
590
  private void takePicture() {
591
+ if(getContext() == null) {
592
+ return;
593
+ }
445
594
  if(this.pictureCount >= this.photoLimit){
446
595
  Toast toast = Toast.makeText(this.getContext(), "La prise de photos est limitée à " + this.photoLimit + " par envoi", Toast.LENGTH_SHORT);
447
596
  toast.show();
448
597
  return;
449
598
  }
450
- if(this.mBackgroundThread == null || !this.mBackgroundThread.isInterrupted() || !this.mBackgroundThread.isAlive()) {
599
+ if(this.mBackgroundThread == null || !this.mBackgroundThread.isAlive()) {
451
600
  this.startBackgroundThread();
452
601
  }
453
- if(cameraDevice == null) {
602
+ if(cameraDevice == null || cameraId == null) {
454
603
  return;
455
604
  }
456
605
  this.pictureCount++;
@@ -459,9 +608,12 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
459
608
  try {
460
609
  CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
461
610
  Size[] jpegSizes = null;
462
- if(characteristics != null)
463
- jpegSizes = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
464
- .getOutputSizes(ImageFormat.JPEG);
611
+ if(characteristics != null) {
612
+ StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
613
+ if (map != null) {
614
+ jpegSizes = map.getOutputSizes(ImageFormat.JPEG);
615
+ }
616
+ }
465
617
  //Capture image with custom size
466
618
  int width = 640;
467
619
  int height = 480;
@@ -470,7 +622,7 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
470
622
  width = jpegSizes[0].getWidth();
471
623
  height = jpegSizes[0].getHeight();
472
624
  }
473
- final ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG,1);
625
+ final ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
474
626
  List<Surface> outputSurface = new ArrayList<>(2);
475
627
  outputSurface.add(reader.getSurface());
476
628
  outputSurface.add(mSurfaceHolder.getSurface());
@@ -480,7 +632,7 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
480
632
  captureBuilder.set(CaptureRequest.FLASH_MODE, flashOn ? CaptureRequest.FLASH_MODE_TORCH : CaptureRequest.FLASH_MODE_OFF);
481
633
  String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
482
634
  String fileName = "IMG_"+ timeStamp + ".jpg";
483
- File file = new File( this.getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName);
635
+ File file = new File(this.getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName);
484
636
  ImageReader.OnImageAvailableListener readerListener = new scoplan.camera.ImageCameraAvailableListener(file, reader, this, currentOrientation);
485
637
 
486
638
  reader.setOnImageAvailableListener(readerListener, mBackgroundHandler);
@@ -488,14 +640,18 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
488
640
  @Override
489
641
  public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
490
642
  super.onCaptureCompleted(session, request, result);
643
+ reader.close();
644
+ previewReady = false;
491
645
  createCameraPreview();
492
646
  }
493
647
 
494
648
  @Override
495
649
  public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
496
650
  super.onCaptureFailed(session, request, failure);
651
+ reader.close();
497
652
  Sentry.captureMessage("Failed capture _" + failure.getReason());
498
653
  Log.e(SCOPLAN_TAG, "Error FAILED Capture " + failure.getReason());
654
+ pictureCount--;
499
655
  failedCapture();
500
656
  }
501
657
  };
@@ -508,43 +664,59 @@ public class CameraFragment extends Fragment implements scoplan.camera.OnImageCa
508
664
  } catch (Exception e) {
509
665
  Log.e(SCOPLAN_TAG, "Error - " + e.getMessage());
510
666
  Sentry.captureException(e);
667
+ reader.close();
668
+ pictureCount--;
511
669
  failedCapture();
512
670
  }
513
671
  }
514
672
 
515
673
  @Override
516
674
  public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
517
- Log.e(SCOPLAN_TAG, "Error FAILED Configuration ");
518
- Sentry.captureMessage("Failed configure cameraCaptureSession");
675
+ Log.e(SCOPLAN_TAG, "Error FAILED Configuration for capture session");
676
+ Sentry.captureMessage("Failed configure capture session");
677
+ reader.close();
678
+ pictureCount--;
519
679
  failedCapture();
520
680
  }
521
681
  }, mBackgroundHandler);
522
682
 
523
683
  } catch (Exception e) {
684
+ Log.e(SCOPLAN_TAG, "Error in takePicture", e);
524
685
  Sentry.captureException(e);
525
- throw new RuntimeException(e);
686
+ pictureCount--;
687
+ failedCapture();
526
688
  }
527
689
  }
528
690
 
529
691
  @Override
530
692
  public void onImageCapture(File file, Bitmap bitmap) {
531
693
  pictures.add(file.getAbsolutePath());
532
- getActivity().runOnUiThread(() -> {
533
- souche.setImageBitmap(bitmap);
534
- defineViewVisibility();
535
- });
694
+ if (getActivity() != null && isAdded()) {
695
+ getActivity().runOnUiThread(() -> {
696
+ if (isAdded()) {
697
+ souche.setImageBitmap(bitmap);
698
+ defineViewVisibility();
699
+ }
700
+ });
701
+ }
536
702
  }
537
703
 
538
704
  @Override
539
705
  public void onImageBuildFailed(Exception e) {
540
706
  Sentry.captureException(e);
707
+ pictureCount--;
541
708
  failedCapture();
542
709
  }
543
710
 
544
711
  public void failedCapture() {
545
- Toast toast = Toast.makeText(getContext(), "Oups! une erreur pendant la capture. Veuillez rééssayer.", Toast.LENGTH_LONG);
546
- toast.show();
712
+ if (getActivity() == null || !isAdded()) {
713
+ return;
714
+ }
547
715
  getActivity().runOnUiThread(() -> {
716
+ if (!isAdded() || getContext() == null) {
717
+ return;
718
+ }
719
+ Toast.makeText(getContext(), "Oups! une erreur pendant la capture. Veuillez rééssayer.", Toast.LENGTH_LONG).show();
548
720
  defineViewVisibility();
549
721
  });
550
722
  }
@@ -29,33 +29,43 @@ public class ImageCameraAvailableListener implements ImageReader.OnImageAvailabl
29
29
  Image image = null;
30
30
  try{
31
31
  image = reader.acquireLatestImage();
32
+ if (image == null) {
33
+ this.imageCaptureListener.onImageBuildFailed(new IOException("Failed to acquire image"));
34
+ return;
35
+ }
32
36
  ByteBuffer buffer = image.getPlanes()[0].getBuffer();
33
37
  byte[] bytes = new byte[buffer.capacity()];
34
38
  buffer.get(bytes);
35
39
  this.imageCaptureListener.onImageCapture(file, save(bytes));
36
40
  }
37
- catch (IOException e)
41
+ catch (Exception e)
38
42
  {
39
43
  e.printStackTrace();
40
44
  this.imageCaptureListener.onImageBuildFailed(e);
41
45
  }
42
46
  finally {
43
- {
44
- if(image != null)
45
- image.close();
46
- }
47
+ if(image != null)
48
+ image.close();
47
49
  }
48
50
  }
49
51
 
50
52
  private Bitmap save(byte[] bytes) throws IOException {
51
53
  OutputStream outputStream = null;
54
+ Bitmap bmp = null;
52
55
  try{
53
56
  outputStream = new FileOutputStream(file);
54
- Bitmap bmp = BitmapFactory.decodeByteArray(bytes,0, bytes.length);
57
+ bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
58
+ if (bmp == null) {
59
+ throw new IOException("Failed to decode image bytes");
60
+ }
55
61
  Matrix matrix = new Matrix();
56
62
  matrix.setRotate(this.rotation);
57
63
  Bitmap rotatedBitmap = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
58
64
  rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 65, outputStream);
65
+ // Recycle original bitmap only if rotation created a new one
66
+ if (rotatedBitmap != bmp) {
67
+ bmp.recycle();
68
+ }
59
69
  return rotatedBitmap;
60
70
  } finally {
61
71
  if(outputStream != null)
@@ -13,6 +13,11 @@ import android.widget.FrameLayout;
13
13
  import android.widget.RelativeLayout;
14
14
  import android.widget.Toast;
15
15
 
16
+ import androidx.core.graphics.Insets;
17
+ import androidx.core.view.ViewCompat;
18
+ import androidx.core.view.WindowCompat;
19
+ import androidx.core.view.WindowInsetsCompat;
20
+
16
21
  import com.dsphotoeditor.sdk.activity.DsPhotoEditorActivity;
17
22
 
18
23
  import java.io.File;
@@ -28,6 +33,10 @@ public class PhotoEditorActivity extends DsPhotoEditorActivity {
28
33
  int themeId = context.getResources().getIdentifier("AppTheme.NoActionBar", "style", context.getPackageName());
29
34
  setTheme(themeId);
30
35
  super.onCreate(savedInstanceState);
36
+
37
+ // Enable edge-to-edge and handle system bars
38
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
39
+
31
40
  this.fakeR = new FakeR(this);
32
41
  this.e = this.getIntent().getData();
33
42
  if (this.e == null) {
@@ -36,6 +45,16 @@ public class PhotoEditorActivity extends DsPhotoEditorActivity {
36
45
  } else {
37
46
  this.createBottomBar(savedInstanceState);
38
47
  }
48
+
49
+ // Apply window insets to root layout
50
+ View rootView = findViewById(fakeR.getId("ds_photo_editor_root_layout"));
51
+ if (rootView != null) {
52
+ ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, windowInsets) -> {
53
+ Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
54
+ v.setPadding(insets.left, insets.top, insets.right, insets.bottom);
55
+ return WindowInsetsCompat.CONSUMED;
56
+ });
57
+ }
39
58
  }
40
59
 
41
60
  @SuppressLint("ResourceType")
@@ -1,6 +1,10 @@
1
1
  package scoplan.camera;
2
2
 
3
3
  import androidx.core.content.ContextCompat;
4
+ import androidx.core.graphics.Insets;
5
+ import androidx.core.view.ViewCompat;
6
+ import androidx.core.view.WindowCompat;
7
+ import androidx.core.view.WindowInsetsCompat;
4
8
 
5
9
  import android.app.AlertDialog;
6
10
  import android.content.Context;
@@ -53,6 +57,18 @@ public class PhotoEditorMesureCustomActivity extends DsPhotoEditorTextActivity i
53
57
  super.onCreate(var1);
54
58
  this.fakeR = new FakeR(this);
55
59
  this.setContentView(com.dsphotoeditor.sdk.R.layout.activity_ds_photo_editor_sticker_text);
60
+
61
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
62
+
63
+ View rootView = findViewById(com.dsphotoeditor.sdk.R.id.ds_photo_editor_text_sticker_root_layout);
64
+ if (rootView != null) {
65
+ ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, windowInsets) -> {
66
+ Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
67
+ v.setPadding(insets.left, insets.top, insets.right, insets.bottom);
68
+ return WindowInsetsCompat.CONSUMED;
69
+ });
70
+ }
71
+
56
72
  if (original != null && !original.isRecycled()) {
57
73
  this.a();
58
74
  this.b();
@@ -1,5 +1,6 @@
1
1
  package scoplan.camera;
2
2
 
3
+ import android.content.Context;
3
4
  import android.content.pm.ActivityInfo;
4
5
  import android.widget.FrameLayout;
5
6
 
@@ -38,20 +39,21 @@ public class ScoplanCamera extends CordovaPlugin implements CameraEventListener
38
39
 
39
40
  private void takePictures(int limit, int currentCount) {
40
41
  cordova.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
41
- CameraEventListener cameraEventListener = this;
42
+ scoplan.camera.CameraEventListener cameraEventListener = this;
42
43
  cordova.getActivity().runOnUiThread(new Runnable() {
43
44
  @Override
44
45
  public void run() {
45
46
  containerView = (FrameLayout)cordova.getActivity().findViewById(containerViewId);
46
47
  if(containerView == null){
47
- containerView = new FrameLayout(cordova.getActivity().getApplicationContext());
48
+ Context context = cordova.getActivity();
49
+ containerView = new FrameLayout(context);
48
50
  containerView.setId(containerViewId);
49
51
  FrameLayout.LayoutParams containerLayoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
50
52
  cordova.getActivity().addContentView(containerView, containerLayoutParams);
51
53
  //add the fragment to the container
52
54
  }
53
55
  containerView.setClickable(true);
54
- cameraFragment = new CameraFragment();
56
+ cameraFragment = new scoplan.camera.CameraFragment();
55
57
  if(currentCount > 0){
56
58
  cameraFragment.setCurrentCount(currentCount);
57
59
  }
@@ -2,7 +2,7 @@
2
2
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
3
  android:layout_width="match_parent"
4
4
  android:layout_height="match_parent"
5
- android:background="@drawable/shape_rectangle">
5
+ android:background="#000000">
6
6
  <LinearLayout
7
7
  android:id="@+id/camera_frame_layout"
8
8
  android:layout_width="match_parent"
@@ -79,6 +79,11 @@
79
79
  [imagePreview setImage:nil];
80
80
  UIView* view = [self.cameraUI.cameraOverlayView viewWithTag:11];
81
81
  [view setHidden:YES];
82
+ // No more photos: button back to "X"
83
+ UIButton* cancelBtn = (UIButton *)[self.cameraUI.cameraOverlayView viewWithTag:2];
84
+ dispatch_async(dispatch_get_main_queue(), ^{
85
+ [cancelBtn setTitle:@"X" forState:UIControlStateNormal];
86
+ });
82
87
  }
83
88
  }
84
89
 
@@ -87,9 +92,23 @@
87
92
  }
88
93
 
89
94
  - (void)insertPicture:(NSString*)url{
90
- UIView* view = [self.cameraUI.cameraOverlayView viewWithTag:11];
91
- [view setHidden:NO];
95
+ UIView* topBar = [self.cameraUI.cameraOverlayView viewWithTag:11];
92
96
  [mpictures addObject:url];
97
+ dispatch_async(dispatch_get_main_queue(), ^{
98
+ UIButton* cancelBtn = (UIButton *)[self.cameraUI.cameraOverlayView viewWithTag:2];
99
+ [cancelBtn setTitle:@"OK" forState:UIControlStateNormal];
100
+
101
+ // Position top bar below safe area
102
+ CGFloat safeTop = 0;
103
+ if (@available(iOS 11.0, *)) {
104
+ safeTop = self.cameraUI.view.safeAreaInsets.top;
105
+ }
106
+ if (safeTop < 20) safeTop = 20;
107
+ CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
108
+ CGFloat barHeight = 44.0;
109
+ topBar.frame = CGRectMake(0, safeTop, screenWidth, barHeight);
110
+ [topBar setHidden:NO];
111
+ });
93
112
  }
94
113
 
95
114
  - (void)flushPicture:(NSString*)url{
@@ -134,7 +153,7 @@
134
153
  UIButton *cancelBtn = (UIButton *)[self.overLayView viewWithTag:2];
135
154
  UIButton *cancelBtn2 = (UIButton *)[self.overLayView viewWithTag:12];
136
155
  dispatch_async( dispatch_get_main_queue(), ^{
137
- [cancelBtn setTitle:@"Annuler" forState:UIControlStateNormal];
156
+ [cancelBtn setTitle:@"X" forState:UIControlStateNormal];
138
157
  });
139
158
  [cancelBtn addTarget: self action: @selector(cancelClicked:) forControlEvents: UIControlEventTouchUpInside];
140
159
  [cancelBtn2 addTarget: self action: @selector(cancelConfirm:) forControlEvents: UIControlEventTouchUpInside];
@@ -167,11 +186,44 @@
167
186
  self.cameraUI.allowsEditing = NO;
168
187
  self.cameraUI.delegate = delegate;
169
188
  self.overLayView.frame = self.cameraUI.cameraOverlayView.frame;
189
+ self.overLayView.backgroundColor = [UIColor clearColor];
170
190
  self.cameraUI.cameraOverlayView = self.overLayView;
171
- float camHeight = ([[UIScreen mainScreen] bounds].size.height/2)*15/100;
172
191
  UIImageView * imagePreview = ((UIImageView *)[self.overLayView viewWithTag:3]);
173
192
  [self.cameraUI initData:imagePreview mCamera:self];
174
- self.cameraUI.cameraViewTransform = CGAffineTransformTranslate(self.cameraUI.cameraViewTransform, 0, camHeight);
193
+
194
+ // Scale camera preview to fill from safe-area-top to bottom of screen
195
+ CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height;
196
+ CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
197
+ CGFloat safeTop = 0;
198
+ if (@available(iOS 11.0, *)) {
199
+ UIWindow *window = UIApplication.sharedApplication.keyWindow;
200
+ if (window) safeTop = window.safeAreaInsets.top;
201
+ }
202
+ if (safeTop < 20) safeTop = 20;
203
+ CGFloat visibleHeight = screenHeight - safeTop;
204
+ CGFloat cameraPreviewHeight = screenWidth * 4.0 / 3.0;
205
+ CGFloat scale = visibleHeight / cameraPreviewHeight;
206
+ if (scale < 1.0) scale = 1.0;
207
+ // Translate the scaled preview down so its top aligns with safe-area-top
208
+ CGFloat translateY = safeTop / 2.0 + (visibleHeight - cameraPreviewHeight) / 2.0;
209
+ CGAffineTransform scaleTransform = CGAffineTransformMakeScale(scale, scale);
210
+ CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(0, translateY);
211
+ self.cameraUI.cameraViewTransform = CGAffineTransformConcat(scaleTransform, translateTransform);
212
+
213
+ // Add a black bar covering the safe-area-top (behind status bar)
214
+ UIView *statusBarBg = [[UIView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, safeTop)];
215
+ statusBarBg.backgroundColor = [UIColor blackColor];
216
+ statusBarBg.tag = 98;
217
+ [self.overLayView addSubview:statusBarBg];
218
+ [self.overLayView sendSubviewToBack:statusBarBg];
219
+
220
+ // Switch top bar (tag=11) to frame-based positioning so we can place it below safe area
221
+ UIView *topBar = [self.overLayView viewWithTag:11];
222
+ if (topBar) {
223
+ topBar.translatesAutoresizingMaskIntoConstraints = YES;
224
+ topBar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;
225
+ }
226
+
175
227
  [controller presentViewController:self.cameraUI animated:YES completion:nil];
176
228
  return YES;
177
229
  }
@@ -1,9 +1,8 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
2
+ <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
3
3
  <device id="retina6_1" orientation="portrait" appearance="light"/>
4
4
  <dependencies>
5
- <deployment identifier="iOS"/>
6
- <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
5
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24504"/>
7
6
  <capability name="Safe area layout guides" minToolsVersion="9.0"/>
8
7
  <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
9
8
  </dependencies>
@@ -17,41 +16,51 @@
17
16
  <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
18
17
  <subviews>
19
18
  <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Dph-cD-OZJ" userLabel="Preview" customClass="AVCamPreviewView">
20
- <rect key="frame" x="0.0" y="0.0" width="414" height="862"/>
21
- <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
19
+ <rect key="frame" x="0.0" y="0.0" width="414" height="828"/>
20
+ <color key="backgroundColor" white="0" alpha="1" colorSpace="calibratedWhite"/>
22
21
  <gestureRecognizers/>
23
22
  <connections>
24
23
  <outletCollection property="gestureRecognizers" destination="Wqi-ea-KMR" appends="YES" id="oAd-i0-IMA"/>
25
24
  </connections>
26
25
  </view>
27
- <view alpha="0.25" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jGC-3L-9Um" userLabel="black">
28
- <rect key="frame" x="0.0" y="735" width="414" height="127"/>
29
- <color key="backgroundColor" systemColor="viewFlipsideBackgroundColor"/>
30
- <constraints>
31
- <constraint firstAttribute="width" secondItem="jGC-3L-9Um" secondAttribute="height" multiplier="160:49" id="YuY-vB-8QI"/>
32
- </constraints>
33
- </view>
34
26
  <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="lmm-Ty-vl6" userLabel="imageview">
35
- <rect key="frame" x="20" y="787" width="50" height="50"/>
27
+ <rect key="frame" x="20" y="753" width="50" height="50"/>
36
28
  <constraints>
37
29
  <constraint firstAttribute="width" constant="50" id="GxG-7V-98h"/>
38
30
  <constraint firstAttribute="height" constant="50" id="j8A-7b-Pdm"/>
39
31
  </constraints>
40
32
  </imageView>
41
- <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nqA-FF-aMJ">
42
- <rect key="frame" x="275.5" y="797" width="53" height="30"/>
33
+ <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nqA-FF-aMJ">
34
+ <rect key="frame" x="350" y="756" width="44" height="44"/>
43
35
  <constraints>
44
- <constraint firstAttribute="height" constant="30" id="1gH-LF-9jk"/>
36
+ <constraint firstAttribute="height" constant="44" id="1gH-LF-9jk"/>
37
+ <constraint firstAttribute="width" constant="44" id="nqA-w-44w"/>
45
38
  </constraints>
46
- <state key="normal" title="Annuler">
47
- <color key="titleColor" red="1" green="1" blue="0.10045030381944445" alpha="1" colorSpace="calibratedRGB"/>
39
+ <color key="backgroundColor" red="0.6" green="0.6" blue="0.6" alpha="0.8" colorSpace="calibratedRGB"/>
40
+ <state key="normal" title="X">
41
+ <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
48
42
  </state>
43
+ <fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>
44
+ <userDefinedRuntimeAttributes>
45
+ <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
46
+ <real key="value" value="22"/>
47
+ </userDefinedRuntimeAttribute>
48
+ <userDefinedRuntimeAttribute type="boolean" keyPath="layer.masksToBounds">
49
+ <boolean key="value" value="YES"/>
50
+ </userDefinedRuntimeAttribute>
51
+ <userDefinedRuntimeAttribute type="number" keyPath="layer.borderWidth">
52
+ <real key="value" value="2"/>
53
+ </userDefinedRuntimeAttribute>
54
+ <userDefinedRuntimeAttribute type="color" keyPath="layer.borderColor">
55
+ <color key="value" white="0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
56
+ </userDefinedRuntimeAttribute>
57
+ </userDefinedRuntimeAttributes>
49
58
  <connections>
50
59
  <action selector="backtowebview:" destination="AA8-Jf-iAm" eventType="touchUpInside" id="8HQ-lG-VyA"/>
51
60
  </connections>
52
61
  </button>
53
62
  <button opaque="NO" contentMode="center" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cRf-Sp-1y2">
54
- <rect key="frame" x="182" y="787" width="50" height="50"/>
63
+ <rect key="frame" x="182" y="753" width="50" height="50"/>
55
64
  <constraints>
56
65
  <constraint firstAttribute="height" constant="50" id="3uF-tj-9po"/>
57
66
  <constraint firstAttribute="width" secondItem="cRf-Sp-1y2" secondAttribute="height" multiplier="1:1" id="jhA-cY-Fs5"/>
@@ -63,7 +72,7 @@
63
72
  </connections>
64
73
  </button>
65
74
  <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="YrK-DJ-ony">
66
- <rect key="frame" x="182" y="787" width="50" height="50"/>
75
+ <rect key="frame" x="182" y="753" width="50" height="50"/>
67
76
  <constraints>
68
77
  <constraint firstAttribute="height" constant="50" id="1ap-FV-bAg"/>
69
78
  <constraint firstAttribute="width" constant="50" id="8dJ-8K-RdS"/>
@@ -71,27 +80,23 @@
71
80
  </imageView>
72
81
  </subviews>
73
82
  <viewLayoutGuide key="safeArea" id="jij-t7-zCY"/>
74
- <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
83
+ <color key="backgroundColor" white="0" alpha="1" colorSpace="calibratedWhite"/>
75
84
  <constraints>
76
85
  <constraint firstItem="jij-t7-zCY" firstAttribute="bottom" secondItem="YrK-DJ-ony" secondAttribute="bottom" constant="25" id="0MC-Rx-Lgu"/>
77
- <constraint firstItem="jGC-3L-9Um" firstAttribute="leading" secondItem="jij-t7-zCY" secondAttribute="leading" id="3iI-5o-Y21"/>
78
86
  <constraint firstItem="YrK-DJ-ony" firstAttribute="centerX" secondItem="8ze-qw-dRA" secondAttribute="centerX" id="56H-C7-K1g"/>
79
- <constraint firstItem="Dph-cD-OZJ" firstAttribute="bottom" secondItem="jij-t7-zCY" secondAttribute="bottom" id="Bla-Mv-ywP"/>
87
+ <constraint firstItem="Dph-cD-OZJ" firstAttribute="bottom" secondItem="8ze-qw-dRA" secondAttribute="bottom" id="Bla-Mv-ywP"/>
80
88
  <constraint firstItem="cRf-Sp-1y2" firstAttribute="centerX" secondItem="8ze-qw-dRA" secondAttribute="centerX" id="IwK-Lt-F2A"/>
81
89
  <constraint firstItem="Dph-cD-OZJ" firstAttribute="leading" secondItem="8ze-qw-dRA" secondAttribute="leading" id="ZPE-Xb-bhT"/>
82
90
  <constraint firstItem="jij-t7-zCY" firstAttribute="bottom" secondItem="cRf-Sp-1y2" secondAttribute="bottom" constant="25" id="d7J-t5-TqX"/>
83
91
  <constraint firstAttribute="trailing" secondItem="Dph-cD-OZJ" secondAttribute="trailing" id="owy-xt-NSQ"/>
84
92
  <constraint firstItem="Dph-cD-OZJ" firstAttribute="top" secondItem="8ze-qw-dRA" secondAttribute="top" id="pfp-tG-hKN"/>
85
- <constraint firstItem="jGC-3L-9Um" firstAttribute="trailing" secondItem="jij-t7-zCY" secondAttribute="trailing" id="qXe-P5-MY6"/>
86
93
  <constraint firstItem="jij-t7-zCY" firstAttribute="bottom" secondItem="nqA-FF-aMJ" secondAttribute="bottom" constant="35" id="rjG-z6-8aH"/>
87
- <constraint firstItem="jGC-3L-9Um" firstAttribute="bottom" secondItem="jij-t7-zCY" secondAttribute="bottom" id="rq9-se-5dj"/>
88
- <constraint firstItem="nqA-FF-aMJ" firstAttribute="centerX" secondItem="8ze-qw-dRA" secondAttribute="centerX" constant="95" id="sCH-2R-4Wi"/>
94
+ <constraint firstItem="jij-t7-zCY" firstAttribute="trailing" secondItem="nqA-FF-aMJ" secondAttribute="trailing" constant="20" id="sCH-2R-4Wi"/>
89
95
  <constraint firstItem="jij-t7-zCY" firstAttribute="bottom" secondItem="lmm-Ty-vl6" secondAttribute="bottom" constant="25" id="t0l-GX-7v2"/>
90
96
  <constraint firstItem="lmm-Ty-vl6" firstAttribute="leading" secondItem="jij-t7-zCY" secondAttribute="leading" constant="20" id="zuS-KY-tMG"/>
91
97
  </constraints>
92
98
  </view>
93
99
  <connections>
94
- <outlet property="blackbottom" destination="jGC-3L-9Um" id="meg-H7-5oV"/>
95
100
  <outlet property="imageview" destination="lmm-Ty-vl6" id="4tT-eQ-Drt"/>
96
101
  <outlet property="okbutton" destination="nqA-FF-aMJ" id="BGp-mM-zEZ"/>
97
102
  <outlet property="photoButton" destination="cRf-Sp-1y2" id="E8a-Tn-i4I"/>
@@ -108,8 +113,5 @@
108
113
  </scenes>
109
114
  <resources>
110
115
  <image name="cameraicon" width="214" height="213"/>
111
- <systemColor name="viewFlipsideBackgroundColor">
112
- <color red="0.1215686274509804" green="0.12941176470588239" blue="0.14117647058823529" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
113
- </systemColor>
114
116
  </resources>
115
117
  </document>
@@ -7,6 +7,15 @@
7
7
  #import "UICustomPickerController.h"
8
8
 
9
9
  @implementation UICustomPickerController
10
+
11
+ - (BOOL)prefersStatusBarHidden {
12
+ return NO;
13
+ }
14
+
15
+ - (UIStatusBarStyle)preferredStatusBarStyle {
16
+ return UIStatusBarStyleLightContent;
17
+ }
18
+
10
19
  -(void)initData:(UIImageView*)souche mCamera:(ScoplanCamera*)mcm{
11
20
  imageSouche = souche;
12
21
  mCamera = mcm;
@@ -64,8 +64,8 @@ typedef NS_ENUM( NSInteger, AVCamDepthDataDeliveryMode ) {
64
64
  @property (strong, nonatomic) IBOutlet UIView *rootView;
65
65
  @property (strong, nonatomic) IBOutlet UIImageView *imageview;
66
66
  @property (strong, nonatomic) IBOutlet UIButton *okbutton;
67
- @property (strong, nonatomic) IBOutlet UIView *blackbottom;
68
67
  @property (strong, nonatomic) IBOutlet UIImageView *progress;
68
+ @property (nonatomic, strong) AVCaptureVideoPreviewLayer *fullscreenPreviewLayer;
69
69
 
70
70
  // Session management.
71
71
  @property (nonatomic, weak) IBOutlet AVCamPreviewView *previewView;
@@ -113,7 +113,7 @@ typedef NS_ENUM( NSInteger, AVCamDepthDataDeliveryMode ) {
113
113
 
114
114
  - (void)insertImgUrl:(NSString*)url{
115
115
  dispatch_async(dispatch_get_main_queue(), ^{
116
- [_okbutton setTitle: @"OK" forState: UIControlStateNormal];
116
+ [_okbutton setTitle:@"OK" forState:UIControlStateNormal];
117
117
  [_photoButton setHidden:FALSE];
118
118
  [_progress stopAnimating];
119
119
  [_progress setHidden:TRUE];
@@ -160,6 +160,7 @@ typedef NS_ENUM( NSInteger, AVCamDepthDataDeliveryMode ) {
160
160
 
161
161
  // Set up the preview view.
162
162
  self.previewView.session = self.session;
163
+ self.previewView.videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
163
164
 
164
165
  // Communicate with the session and other session objects on this queue.
165
166
  self.sessionQueue = dispatch_queue_create( "session queue", DISPATCH_QUEUE_SERIAL );
@@ -234,6 +235,14 @@ typedef NS_ENUM( NSInteger, AVCamDepthDataDeliveryMode ) {
234
235
  }
235
236
 
236
237
 
238
+ - (void)viewDidLayoutSubviews
239
+ {
240
+ [super viewDidLayoutSubviews];
241
+ if (self.fullscreenPreviewLayer) {
242
+ self.fullscreenPreviewLayer.frame = _rootView.bounds;
243
+ }
244
+ }
245
+
237
246
  - (void)viewWillAppear:(BOOL)animated
238
247
  {
239
248
  [super viewWillAppear:animated];
@@ -268,11 +277,14 @@ typedef NS_ENUM( NSInteger, AVCamDepthDataDeliveryMode ) {
268
277
  }
269
278
 
270
279
  }];
271
- AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
272
- previewLayer.frame = _rootView.bounds;
273
- previewLayer.videoGravity = AVLayerVideoGravityResizeAspect;
274
- [_rootView.layer addSublayer:previewLayer];
275
- _blackbottom.layer.zPosition = 1;
280
+ // Remove old preview layer if any (e.g. on re-appear)
281
+ if (self.fullscreenPreviewLayer) {
282
+ [self.fullscreenPreviewLayer removeFromSuperlayer];
283
+ }
284
+ self.fullscreenPreviewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
285
+ self.fullscreenPreviewLayer.frame = _rootView.bounds;
286
+ self.fullscreenPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
287
+ [_rootView.layer addSublayer:self.fullscreenPreviewLayer];
276
288
  _photoButton.imageView.layer.zPosition = 1;
277
289
  _photoButton.layer.zPosition = 1;
278
290
  _imageview.layer.zPosition = 1;
@@ -1,9 +1,8 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
2
+ <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="24506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
3
3
  <device id="retina4_7" orientation="portrait" appearance="light"/>
4
4
  <dependencies>
5
- <deployment identifier="iOS"/>
6
- <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
5
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24504"/>
7
6
  <capability name="Safe area layout guides" minToolsVersion="9.0"/>
8
7
  <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
9
8
  </dependencies>
@@ -13,9 +12,10 @@
13
12
  <view contentMode="scaleToFill" id="2Si-cO-KSW">
14
13
  <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
15
14
  <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
15
+ <color key="backgroundColor" white="0" alpha="0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
16
16
  <subviews>
17
17
  <view hidden="YES" tag="11" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SRs-0J-vwF">
18
- <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
18
+ <rect key="frame" x="0.0" y="20" width="375" height="44"/>
19
19
  <subviews>
20
20
  <label opaque="NO" userInteractionEnabled="NO" tag="13" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Dessiner sur cette photo" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3SW-lA-FXP">
21
21
  <rect key="frame" x="144" y="11" width="187" height="21"/>
@@ -62,13 +62,30 @@
62
62
  </constraints>
63
63
  </imageView>
64
64
  <button opaque="NO" tag="2" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="utO-5P-M5r">
65
- <rect key="frame" x="251.5" y="602" width="62" height="30"/>
65
+ <rect key="frame" x="311" y="598" width="44" height="44"/>
66
66
  <constraints>
67
- <constraint firstAttribute="height" constant="30" id="tsh-0X-lyK"/>
67
+ <constraint firstAttribute="height" constant="44" id="tsh-0X-lyK"/>
68
+ <constraint firstAttribute="width" constant="44" id="utO-w-44w"/>
68
69
  </constraints>
69
- <state key="normal" title="Annuler">
70
- <color key="titleColor" red="1" green="1" blue="0.1004503038" alpha="1" colorSpace="calibratedRGB"/>
70
+ <color key="backgroundColor" red="0.6" green="0.6" blue="0.6" alpha="0.8" colorSpace="calibratedRGB"/>
71
+ <state key="normal" title="X">
72
+ <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
71
73
  </state>
74
+ <fontDescription key="fontDescription" type="boldSystem" pointSize="18"/>
75
+ <userDefinedRuntimeAttributes>
76
+ <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
77
+ <real key="value" value="22"/>
78
+ </userDefinedRuntimeAttribute>
79
+ <userDefinedRuntimeAttribute type="boolean" keyPath="layer.masksToBounds">
80
+ <boolean key="value" value="YES"/>
81
+ </userDefinedRuntimeAttribute>
82
+ <userDefinedRuntimeAttribute type="number" keyPath="layer.borderWidth">
83
+ <real key="value" value="2"/>
84
+ </userDefinedRuntimeAttribute>
85
+ <userDefinedRuntimeAttribute type="color" keyPath="layer.borderColor">
86
+ <color key="value" white="0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
87
+ </userDefinedRuntimeAttribute>
88
+ </userDefinedRuntimeAttributes>
72
89
  </button>
73
90
  <button opaque="NO" tag="1" contentMode="center" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="f3a-n0-68n">
74
91
  <rect key="frame" x="162.5" y="592" width="50" height="50"/>
@@ -94,8 +111,7 @@
94
111
  <constraint firstItem="w0n-Df-NWM" firstAttribute="bottom" secondItem="f3a-n0-68n" secondAttribute="bottom" constant="25" id="Oi4-t0-CQ6"/>
95
112
  <constraint firstItem="f3a-n0-68n" firstAttribute="centerX" secondItem="2Si-cO-KSW" secondAttribute="centerX" id="SUH-d2-dsP"/>
96
113
  <constraint firstItem="w0n-Df-NWM" firstAttribute="bottom" secondItem="utO-5P-M5r" secondAttribute="bottom" constant="35" id="Sx5-aI-blt"/>
97
- <constraint firstItem="utO-5P-M5r" firstAttribute="centerX" secondItem="2Si-cO-KSW" secondAttribute="centerX" constant="95" id="WjK-KC-LQF"/>
98
- <constraint firstItem="SRs-0J-vwF" firstAttribute="top" secondItem="w0n-Df-NWM" secondAttribute="top" id="aoW-sr-zz9"/>
114
+ <constraint firstItem="w0n-Df-NWM" firstAttribute="trailing" secondItem="utO-5P-M5r" secondAttribute="trailing" constant="20" id="WjK-KC-LQF"/>
99
115
  <constraint firstItem="SRs-0J-vwF" firstAttribute="trailing" secondItem="w0n-Df-NWM" secondAttribute="trailing" id="nVN-7W-wYt"/>
100
116
  <constraint firstItem="WzM-XN-do1" firstAttribute="centerX" secondItem="2Si-cO-KSW" secondAttribute="centerX" id="sAe-d3-TMO"/>
101
117
  <constraint firstItem="w0n-Df-NWM" firstAttribute="bottom" secondItem="Yg9-0S-pJv" secondAttribute="bottom" constant="25" id="uoV-VQ-OE3"/>