Skip to content

Commit b124c6d

Browse files
[Camera] Fix rotation; disable rotation animation
1 parent a24611c commit b124c6d

File tree

10 files changed

+347
-93
lines changed

10 files changed

+347
-93
lines changed

android/app/src/main/kotlin/org/proninyaroslav/blink_comparison/MainActivity.kt

+10-4
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,20 @@ package org.proninyaroslav.blink_comparison
2121

2222
import android.os.Build
2323
import android.os.Bundle
24-
import androidx.core.view.WindowCompat
25-
import org.proninyaroslav.blink_comparison.channel.SaveRefImageServiceChannel
26-
import org.proninyaroslav.blink_comparison.channel.SaveRefImageServiceQueueChannel
27-
import org.proninyaroslav.blink_comparison.channel.SaveRefImageServiceResultChannel
2824
import io.flutter.embedding.android.FlutterActivity
2925
import io.flutter.embedding.engine.FlutterEngine
3026
import io.flutter.plugin.common.EventChannel
3127
import io.flutter.plugin.common.MethodChannel
28+
import org.proninyaroslav.blink_comparison.channel.SaveRefImageServiceChannel
29+
import org.proninyaroslav.blink_comparison.channel.SaveRefImageServiceQueueChannel
30+
import org.proninyaroslav.blink_comparison.channel.SaveRefImageServiceResultChannel
31+
import org.proninyaroslav.blink_comparison.channel.WindowManagerChannel
3232

3333
class MainActivity : FlutterActivity() {
3434
private lateinit var saveRefImageServiceChannel: SaveRefImageServiceChannel
3535
private lateinit var saveRefImageServiceQueueChannel: SaveRefImageServiceQueueChannel
3636
private lateinit var saveRefImageServiceResultChannel: SaveRefImageServiceResultChannel
37+
private lateinit var windowManagerChannel: WindowManagerChannel
3738

3839
override fun onCreate(savedInstanceState: Bundle?) {
3940
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -55,6 +56,7 @@ class MainActivity : FlutterActivity() {
5556
saveRefImageServiceQueueChannel,
5657
saveRefImageServiceResultChannel,
5758
)
59+
windowManagerChannel = WindowManagerChannel { this }
5860
MethodChannel(
5961
flutterEngine.dartExecutor.binaryMessenger,
6062
SaveRefImageServiceChannel.channelName,
@@ -67,5 +69,9 @@ class MainActivity : FlutterActivity() {
6769
flutterEngine.dartExecutor.binaryMessenger,
6870
SaveRefImageServiceResultChannel.channelName,
6971
).setStreamHandler(saveRefImageServiceResultChannel)
72+
MethodChannel(
73+
flutterEngine.dartExecutor.binaryMessenger,
74+
WindowManagerChannel.channelName,
75+
).setMethodCallHandler(windowManagerChannel)
7076
}
7177
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright (C) 2024 Yaroslav Pronin <[email protected]>
3+
*
4+
* This file is part of Blink Comparison.
5+
*
6+
* Blink Comparison is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* Blink Comparison is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with Blink Comparison. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
package org.proninyaroslav.blink_comparison.channel
21+
22+
import android.app.Activity
23+
import android.view.WindowManager
24+
import io.flutter.plugin.common.MethodCall
25+
import io.flutter.plugin.common.MethodChannel
26+
27+
open class WindowManagerChannel(private val getActivity: () -> Activity) :
28+
MethodChannel.MethodCallHandler {
29+
30+
companion object {
31+
const val channelName = "org.proninyaroslav.blink_comparison/window_manager"
32+
}
33+
34+
object Methods {
35+
const val setSecureFlag = "setSecureFlag"
36+
const val setRotationAnimation = "setRotationAnimation"
37+
}
38+
39+
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
40+
val window = getActivity().window
41+
when (call.method) {
42+
Methods.setSecureFlag -> {
43+
val enabled = call.arguments as Boolean
44+
if (enabled) {
45+
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
46+
} else {
47+
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
48+
}
49+
return result.success(null)
50+
}
51+
52+
Methods.setRotationAnimation -> {
53+
val animationValue = call.arguments as Int
54+
window.attributes = window.attributes.apply {
55+
rotationAnimation = animationValue
56+
}
57+
return result.success(null)
58+
}
59+
60+
else -> {
61+
return result.notImplemented()
62+
}
63+
}
64+
}
65+
}

lib/core/window_manager.dart

+42-13
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,55 @@
1515
// You should have received a copy of the GNU General Public License
1616
// along with Blink Comparison. If not, see <http://www.gnu.org/licenses/>.
1717

18-
import 'package:flutter_windowmanager_plus/flutter_windowmanager_plus.dart';
18+
import 'package:blink_comparison/platform/window_manager_native.dart';
1919
import 'package:injectable/injectable.dart';
2020

2121
abstract class WindowManager {
2222
/// Allow/disallow screenshots
23-
Future<bool> setSecureFlag(bool enable);
23+
Future<void> setSecureFlag(bool enable);
24+
25+
/// Define the exit and entry animations used on this window when the device is rotated.
26+
/// This only has an affect if the incoming and outgoing topmost opaque windows
27+
/// have the #FLAG_FULLSCREEN bit set and are not covered by other windows.
28+
/// All other situations default to the [RotationAnimtation.rotate] behavior.
29+
Future<void> setRotationAnimation(RotationAnimtation animation);
30+
}
31+
32+
enum RotationAnimtation {
33+
/// Window will visually rotate in or out following a rotation.
34+
rotate(0),
35+
36+
/// Window will fade in or out following a rotation.
37+
crossfade(1),
38+
39+
/// Window will immediately disappear or appear following a rotation.
40+
jumpcut(2),
41+
42+
/// This works like [jumpcut] but will fall back to [crossfade] if
43+
/// rotation can't be applied without pausing the screen. For example,
44+
/// this is ideal for Camera apps which don't want the viewfinder contents to
45+
/// ever rotate or fade (and rather to be seamless) but also don't want
46+
/// [jumpcut] during app transition scenarios where seamless rotation can't be applied.
47+
seamless(3);
48+
49+
final int value;
50+
51+
const RotationAnimtation(this.value);
52+
53+
static RotationAnimtation defaultValue = rotate;
2454
}
2555

2656
@Singleton(as: WindowManager)
2757
class WindowManagerImpl implements WindowManager {
58+
final WindowManagerNative _windowManager;
59+
60+
WindowManagerImpl(this._windowManager);
61+
62+
@override
63+
Future<void> setSecureFlag(bool enable) async =>
64+
_windowManager.setSecureFlag(enable);
65+
2866
@override
29-
Future<bool> setSecureFlag(bool enable) async {
30-
if (enable) {
31-
return FlutterWindowManagerPlus.addFlags(
32-
FlutterWindowManagerPlus.FLAG_SECURE,
33-
);
34-
} else {
35-
return FlutterWindowManagerPlus.clearFlags(
36-
FlutterWindowManagerPlus.FLAG_SECURE,
37-
);
38-
}
39-
}
67+
Future<void> setRotationAnimation(RotationAnimtation animation) async =>
68+
_windowManager.setRotationAnimation(animation);
4069
}

lib/injector.config.dart

+4-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (C) 2024 Yaroslav Pronin <[email protected]>
2+
//
3+
// This file is part of Blink Comparison.
4+
//
5+
// Blink Comparison is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// Blink Comparison is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with Blink Comparison. If not, see <http://www.gnu.org/licenses/>.
17+
18+
import 'package:blink_comparison/core/window_manager.dart';
19+
import 'package:flutter/material.dart';
20+
import 'package:flutter/services.dart';
21+
import 'package:injectable/injectable.dart';
22+
23+
@injectable
24+
class WindowManagerNative {
25+
@visibleForTesting
26+
final channel = MethodChannel(
27+
'org.proninyaroslav.blink_comparison/window_manager',
28+
);
29+
30+
Future<void> setSecureFlag(bool enable) async {
31+
await channel.invokeMethod('setSecureFlag', enable);
32+
}
33+
34+
Future<void> setRotationAnimation(RotationAnimtation animtaion) async {
35+
await channel.invokeMethod('setRotationAnimation', animtaion.value);
36+
}
37+
}

lib/ui/components/camera/camera_app_bar.dart

+21-8
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
// You should have received a copy of the GNU General Public License
1616
// along with Blink Comparison. If not, see <http://www.gnu.org/licenses/>.
1717

18+
import 'package:blink_comparison/ui/components/widget.dart';
1819
import 'package:flutter/material.dart';
1920

20-
class CameraAppBar extends StatelessWidget implements PreferredSizeWidget {
21+
class CameraAppBar extends StatelessWidget {
2122
final List<Widget>? actions;
2223
const CameraAppBar({
2324
super.key,
@@ -27,13 +28,25 @@ class CameraAppBar extends StatelessWidget implements PreferredSizeWidget {
2728
@override
2829
Widget build(BuildContext context) {
2930
final color = Theme.of(context).colorScheme.surface.withOpacity(0.54);
30-
return AppBar(
31-
backgroundColor: color,
32-
actionsIconTheme: const IconThemeData(color: Colors.white),
33-
actions: actions,
31+
return PortraitOnlyWidget(
32+
direction: RotateDirection.clockwise,
33+
child: SizedBox.fromSize(
34+
size: const Size.fromHeight(56.0),
35+
child: AppBar(
36+
leading: PortraitOnlyWidget(
37+
child: BackButton(),
38+
),
39+
backgroundColor: color,
40+
actionsIconTheme: const IconThemeData(color: Colors.white),
41+
actions: actions
42+
?.map(
43+
(child) => PortraitOnlyWidget(
44+
child: child,
45+
),
46+
)
47+
.toList(),
48+
),
49+
),
3450
);
3551
}
36-
37-
@override
38-
Size get preferredSize => const Size.fromHeight(56.0);
3952
}

0 commit comments

Comments
 (0)