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.
- package/.github/instructions/snyk_rules.instructions.md +14 -0
- package/.vscode/settings.json +3 -0
- package/package.json +1 -1
- package/plugin.xml +1 -1
- package/src/android/CCropActivity.java +17 -0
- package/src/android/CDrawActivity.java +18 -0
- package/src/android/CTextActivity.java +18 -0
- package/src/android/CameraFragment.java +249 -77
- package/src/android/ImageCameraAvailableListener.java +16 -6
- package/src/android/PhotoEditorActivity.java +19 -0
- package/src/android/PhotoEditorMesureCustomActivity.java +16 -0
- package/src/android/ScoplanCamera.java +5 -3
- package/src/android/res/layout/fragment_camera.xml +1 -1
- package/src/ios/ScoplanCamera.m +57 -5
- package/src/ios/ScoplanCamera.storyboard +32 -30
- package/src/ios/UICustomPickerController.m +9 -0
- package/src/ios/UIScoplanCamera.m +19 -7
- package/src/ios/multicam.xib +26 -10
|
@@ -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.
|
package/package.json
CHANGED
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:
|
|
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
|
-
|
|
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
|
|
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
|
|
147
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
358
|
+
Log.e(SCOPLAN_TAG, "onConfigureFailed for preview session");
|
|
359
|
+
Sentry.captureMessage("onConfigureFailed: " + cameraCaptureSession.toString());
|
|
360
|
+
previewReady = false;
|
|
361
|
+
failedCapture();
|
|
261
362
|
}
|
|
262
|
-
},
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
349
|
-
return new Size(parentWidth, height);
|
|
454
|
+
return new Size(surfaceWidth, surfaceHeight);
|
|
350
455
|
}
|
|
351
456
|
|
|
352
457
|
private Size selectedPreviewSize(Size[] outputSizes) {
|
|
353
|
-
|
|
354
|
-
|
|
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.
|
|
359
|
-
|
|
360
|
-
|
|
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
|
-
|
|
384
|
-
controller.
|
|
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.
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
464
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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()
|
|
533
|
-
|
|
534
|
-
|
|
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
|
-
|
|
546
|
-
|
|
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 (
|
|
41
|
+
catch (Exception e)
|
|
38
42
|
{
|
|
39
43
|
e.printStackTrace();
|
|
40
44
|
this.imageCaptureListener.onImageBuildFailed(e);
|
|
41
45
|
}
|
|
42
46
|
finally {
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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="
|
|
5
|
+
android:background="#000000">
|
|
6
6
|
<LinearLayout
|
|
7
7
|
android:id="@+id/camera_frame_layout"
|
|
8
8
|
android:layout_width="match_parent"
|
package/src/ios/ScoplanCamera.m
CHANGED
|
@@ -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*
|
|
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:@"
|
|
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
|
-
|
|
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="
|
|
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
|
-
<
|
|
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="
|
|
21
|
-
<color key="backgroundColor" white="
|
|
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="
|
|
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"
|
|
42
|
-
<rect key="frame" x="
|
|
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="
|
|
36
|
+
<constraint firstAttribute="height" constant="44" id="1gH-LF-9jk"/>
|
|
37
|
+
<constraint firstAttribute="width" constant="44" id="nqA-w-44w"/>
|
|
45
38
|
</constraints>
|
|
46
|
-
<
|
|
47
|
-
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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;
|
package/src/ios/multicam.xib
CHANGED
|
@@ -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="
|
|
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
|
-
<
|
|
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="
|
|
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="
|
|
65
|
+
<rect key="frame" x="311" y="598" width="44" height="44"/>
|
|
66
66
|
<constraints>
|
|
67
|
-
<constraint firstAttribute="height" constant="
|
|
67
|
+
<constraint firstAttribute="height" constant="44" id="tsh-0X-lyK"/>
|
|
68
|
+
<constraint firstAttribute="width" constant="44" id="utO-w-44w"/>
|
|
68
69
|
</constraints>
|
|
69
|
-
<
|
|
70
|
-
|
|
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="
|
|
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"/>
|