Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
Merge branch 'main' into android-handle-quick-action-without-restart
Browse files Browse the repository at this point in the history
  • Loading branch information
TabooSun committed Jul 6, 2022
2 parents 116386b + a0c773b commit 791fcfb
Show file tree
Hide file tree
Showing 147 changed files with 2,079 additions and 772 deletions.
2 changes: 1 addition & 1 deletion .ci/flutter_master.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
02558d69d92384e7ed1b66f50006796342c8945a
39a38b7882fe40436176ebc97e554be11141cc7d
1 change: 1 addition & 0 deletions .cirrus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ task:
# run with --require-excerpts and no exclusions.
- ./script/tool_runner.sh readme-check --require-excerpts --exclude=script/configs/temp_exclude_excerpt.yaml
license_script: dart $PLUGIN_TOOL license-check
dependabot_script: dart $PLUGIN_TOOL dependabot-check
- name: federated_safety
# This check is only meaningful for PRs, as it validates changes
# rather than state.
Expand Down
30 changes: 27 additions & 3 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
version: 2
updates:
- package-ecosystem: "gradle"
directory: "/packages/camera/camera/android"
directory: "/packages/camera/camera_android/android"
commit-message:
prefix: "[camera]"
schedule:
interval: "weekly"
open-pull-requests-limit: 10

- package-ecosystem: "gradle"
directory: "/packages/camera/camera_android/example/android/app"
commit-message:
prefix: "[camera]"
schedule:
Expand Down Expand Up @@ -216,6 +224,14 @@ updates:
interval: "weekly"
open-pull-requests-limit: 10

- package-ecosystem: "gradle"
directory: "/packages/shared_preferences/shared_preferences_android/android"
commit-message:
prefix: "[shared_pref]"
schedule:
interval: "weekly"
open-pull-requests-limit: 10

- package-ecosystem: "gradle"
directory: "/packages/shared_preferences/shared_preferences_android/example/android/app"
commit-message:
Expand Down Expand Up @@ -248,6 +264,14 @@ updates:
interval: "weekly"
open-pull-requests-limit: 10

- package-ecosystem: "gradle"
directory: "/packages/video_player/video_player/example/android/app"
commit-message:
prefix: "[video_player]"
schedule:
interval: "weekly"
open-pull-requests-limit: 10

- package-ecosystem: "gradle"
directory: "/packages/video_player/video_player_android/android"
commit-message:
Expand Down Expand Up @@ -281,13 +305,13 @@ updates:
open-pull-requests-limit: 10

- package-ecosystem: "gradle"
directory: "/packages/webview_flutter/webview_flutter_android/example/android"
directory: "/packages/webview_flutter/webview_flutter_android/example/android/app"
commit-message:
prefix: "[webview]"
schedule:
interval: "weekly"
open-pull-requests-limit: 10

- package-ecosystem: "github-actions"
directory: "/"
commit-message:
Expand Down
4 changes: 4 additions & 0 deletions packages/camera/camera/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## NEXT

* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316).

## 0.9.8+1

* Ignores deprecation warnings for upcoming styleFrom button API changes.
Expand Down
2 changes: 1 addition & 1 deletion packages/camera/camera/example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ flutter {
}

dependencies {
testImplementation 'junit:junit:4.12'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
Expand Down
4 changes: 4 additions & 0 deletions packages/camera/camera/test/camera_value_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316)
// ignore: unnecessary_import
import 'dart:ui';

import 'package:camera/camera.dart';
// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316)
// ignore: unnecessary_import
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
Expand Down
8 changes: 8 additions & 0 deletions packages/camera/camera_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 0.9.8+3

* Skips duplicate calls to stop background thread and removes unnecessary closings of camera capture sessions on Android.

## 0.9.8+2

* Fixes exception in registerWith caused by the switch to an in-package method channel.

## 0.9.8+1

* Ignores deprecation warnings for upcoming styleFrom button API changes.
Expand Down
2 changes: 1 addition & 1 deletion packages/camera/camera_android/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ android {

dependencies {
compileOnly 'androidx.annotation:annotation:1.1.0'
testImplementation 'junit:junit:4.12'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-inline:4.0.0'
testImplementation 'androidx.test:core:1.3.0'
testImplementation 'org.robolectric:robolectric:4.5'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ class Camera

/** An additional thread for running tasks that shouldn't block the UI. */
private HandlerThread backgroundHandlerThread;
/** True when backgroundHandlerThread is in the process of being stopped. */
private boolean stoppingBackgroundHandlerThread = false;

private CameraDeviceWrapper cameraDevice;
private CameraCaptureSession captureSession;
Expand Down Expand Up @@ -382,16 +384,16 @@ public void onError(@NonNull CameraDevice cameraDevice, int errorCode) {
backgroundHandler);
}

private void createCaptureSession(int templateType, Surface... surfaces)
throws CameraAccessException {
@VisibleForTesting
void createCaptureSession(int templateType, Surface... surfaces) throws CameraAccessException {
createCaptureSession(templateType, null, surfaces);
}

private void createCaptureSession(
int templateType, Runnable onSuccessCallback, Surface... surfaces)
throws CameraAccessException {
// Close any existing capture session.
closeCaptureSession();
captureSession = null;

// Create a new capture builder.
previewRequestBuilder = cameraDevice.createCaptureRequest(templateType);
Expand Down Expand Up @@ -669,7 +671,11 @@ public void startBackgroundThread() {

/** Stops the background thread and its {@link Handler}. */
public void stopBackgroundThread() {
if (stoppingBackgroundHandlerThread) {
return;
}
if (backgroundHandlerThread != null) {
stoppingBackgroundHandlerThread = true;
backgroundHandlerThread.quitSafely();
try {
backgroundHandlerThread.join();
Expand All @@ -679,6 +685,7 @@ public void stopBackgroundThread() {
}
backgroundHandlerThread = null;
backgroundHandler = null;
stoppingBackgroundHandlerThread = false;
}

/** Start capturing a picture, doing autofocus first. */
Expand Down Expand Up @@ -1173,12 +1180,19 @@ private void closeCaptureSession() {

public void close() {
Log.i(TAG, "close");
closeCaptureSession();

if (cameraDevice != null) {
cameraDevice.close();
cameraDevice = null;

// Closing the CameraDevice without closing the CameraCaptureSession is recommended
// for quickly closing the camera:
// https://developer.android.com/reference/android/hardware/camera2/CameraCaptureSession#close()
captureSession = null;
} else {
closeCaptureSession();
}

if (pictureImageReader != null) {
pictureImageReader.close();
pictureImageReader = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
import static org.mockito.Mockito.when;

import android.app.Activity;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.SessionConfiguration;
Expand All @@ -28,13 +30,15 @@
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Size;
import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleObserver;
import io.flutter.embedding.engine.systemchannels.PlatformChannel;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.camera.features.CameraFeatureFactory;
import io.flutter.plugins.camera.features.CameraFeatures;
import io.flutter.plugins.camera.features.Point;
import io.flutter.plugins.camera.features.autofocus.AutoFocusFeature;
import io.flutter.plugins.camera.features.autofocus.FocusMode;
Expand Down Expand Up @@ -833,6 +837,28 @@ public void startBackgroundThread_shouldNotStartNewThreadWhenAlreadyCreated() {
verify(mockHandlerThread, times(1)).start();
}

@Test
public void stopBackgroundThread_cancelsDuplicateCalls() throws InterruptedException {
TestUtils.setPrivateField(camera, "stoppingBackgroundHandlerThread", true);

camera.startBackgroundThread();
camera.stopBackgroundThread();

verify(mockHandlerThread, never()).quitSafely();
verify(mockHandlerThread, never()).join();
}

@Test
public void stopBackgroundThread_proceedsWithoutDuplicateCall() throws InterruptedException {
TestUtils.setPrivateField(camera, "stoppingBackgroundHandlerThread", false);

camera.startBackgroundThread();
camera.stopBackgroundThread();

verify(mockHandlerThread).quitSafely();
verify(mockHandlerThread).join();
}

@Test
public void onConverge_shouldTakePictureWithoutAbortingSession() throws CameraAccessException {
ArrayList<CaptureRequest.Builder> mockRequestBuilders = new ArrayList<>();
Expand All @@ -856,6 +882,52 @@ public void onConverge_shouldTakePictureWithoutAbortingSession() throws CameraAc
verify(mockCaptureSession, never()).abortCaptures();
}

@Test
public void createCaptureSession_doesNotCloseCaptureSession() throws CameraAccessException {
Surface mockSurface = mock(Surface.class);
SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class);
ResolutionFeature mockResolutionFeature = mock(ResolutionFeature.class);
Size mockSize = mock(Size.class);
ArrayList<CaptureRequest.Builder> mockRequestBuilders = new ArrayList<>();
mockRequestBuilders.add(mock(CaptureRequest.Builder.class));
CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders);
TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera);

TextureRegistry.SurfaceTextureEntry cameraFlutterTexture =
(TextureRegistry.SurfaceTextureEntry) TestUtils.getPrivateField(camera, "flutterTexture");
CameraFeatures cameraFeatures =
(CameraFeatures) TestUtils.getPrivateField(camera, "cameraFeatures");
ResolutionFeature resolutionFeature =
(ResolutionFeature)
TestUtils.getPrivateField(mockCameraFeatureFactory, "mockResolutionFeature");

when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture);
when(resolutionFeature.getPreviewSize()).thenReturn(mockSize);

camera.createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, mockSurface);

verify(mockCaptureSession, never()).close();
}

@Test
public void close_doesCloseCaptureSessionWhenCameraDeviceNull() {
camera.close();

verify(mockCaptureSession).close();
}

@Test
public void close_doesNotCloseCaptureSessionWhenCameraDeviceNonNull() {
ArrayList<CaptureRequest.Builder> mockRequestBuilders = new ArrayList<>();
mockRequestBuilders.add(mock(CaptureRequest.Builder.class));
CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders);
TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera);

camera.close();

verify(mockCaptureSession, never()).close();
}

private static class TestCameraFeatureFactory implements CameraFeatureFactory {
private final AutoFocusFeature mockAutoFocusFeature;
private final ExposureLockFeature mockExposureLockFeature;
Expand Down
38 changes: 18 additions & 20 deletions packages/camera/camera_android/lib/src/android_camera.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,19 @@ const MethodChannel _channel =

/// The Android implementation of [CameraPlatform] that uses method channels.
class AndroidCamera extends CameraPlatform {
/// Construct a new method channel camera instance.
AndroidCamera() {
const MethodChannel channel =
MethodChannel('plugins.flutter.io/camera_android/fromPlatform');
channel.setMethodCallHandler(
(MethodCall call) => handleDeviceMethodCall(call));
}

/// Registers this class as the default instance of [CameraPlatform].
static void registerWith() {
CameraPlatform.instance = AndroidCamera();
}

final Map<int, MethodChannel> _channels = <int, MethodChannel>{};

/// The name of the channel that device events from the platform side are
/// sent on.
@visibleForTesting
static const String deviceEventChannelName =
'plugins.flutter.io/camera_android/fromPlatform';

/// The controller we need to broadcast the different events coming
/// from handleMethodCall, specific to camera events.
///
Expand All @@ -50,11 +48,15 @@ class AndroidCamera extends CameraPlatform {
///
/// It is a `broadcast` because multiple controllers will connect to
/// different stream views of this Controller.
/// This is only exposed for test purposes. It shouldn't be used by clients of
/// the plugin as it may break or change at any time.
@visibleForTesting
final StreamController<DeviceEvent> deviceEventStreamController =
StreamController<DeviceEvent>.broadcast();
late final StreamController<DeviceEvent> _deviceEventStreamController =
_createDeviceEventStreamController();

StreamController<DeviceEvent> _createDeviceEventStreamController() {
// Set up the method handler lazily.
const MethodChannel channel = MethodChannel(deviceEventChannelName);
channel.setMethodCallHandler(_handleDeviceMethodCall);
return StreamController<DeviceEvent>.broadcast();
}

// The stream to receive frames from the native code.
StreamSubscription<dynamic>? _platformImageStreamSubscription;
Expand Down Expand Up @@ -192,7 +194,7 @@ class AndroidCamera extends CameraPlatform {

@override
Stream<DeviceOrientationChangedEvent> onDeviceOrientationChanged() {
return deviceEventStreamController.stream
return _deviceEventStreamController.stream
.whereType<DeviceOrientationChangedEvent>();
}

Expand Down Expand Up @@ -518,14 +520,10 @@ class AndroidCamera extends CameraPlatform {
}

/// Converts messages received from the native platform into device events.
///
/// This is only exposed for test purposes. It shouldn't be used by clients of
/// the plugin as it may break or change at any time.
@visibleForTesting
Future<dynamic> handleDeviceMethodCall(MethodCall call) async {
Future<dynamic> _handleDeviceMethodCall(MethodCall call) async {
switch (call.method) {
case 'orientation_changed':
deviceEventStreamController.add(DeviceOrientationChangedEvent(
_deviceEventStreamController.add(DeviceOrientationChangedEvent(
deserializeDeviceOrientation(
call.arguments['orientation']! as String)));
break;
Expand Down
2 changes: 1 addition & 1 deletion packages/camera/camera_android/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: camera_android
description: Android implementation of the camera plugin.
repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
version: 0.9.8+1
version: 0.9.8+3

environment:
sdk: ">=2.14.0 <3.0.0"
Expand Down
Loading

0 comments on commit 791fcfb

Please sign in to comment.