#838 HDR color mode
This commit is contained in:
parent
445aa2cb06
commit
49fa3eec96
10 changed files with 160 additions and 81 deletions
|
@ -1,6 +1,7 @@
|
|||
package deckers.thibault.aves.channel.calls.window
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.os.Build
|
||||
import android.view.WindowManager
|
||||
import deckers.thibault.aves.utils.getDisplayCompat
|
||||
|
@ -75,4 +76,21 @@ class ActivityWindowHandler(private val activity: Activity) : WindowHandler(acti
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun supportsHdr(call: MethodCall, result: MethodChannel.Result) {
|
||||
result.success(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && activity.getDisplayCompat()?.isHdr ?: false)
|
||||
}
|
||||
|
||||
override fun setHdrColorMode(call: MethodCall, result: MethodChannel.Result) {
|
||||
val on = call.argument<Boolean>("on")
|
||||
if (on == null) {
|
||||
result.error("setHdrColorMode-args", "missing arguments", null)
|
||||
return
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
activity.window.colorMode = if (on) ActivityInfo.COLOR_MODE_HDR else ActivityInfo.COLOR_MODE_DEFAULT
|
||||
}
|
||||
result.success(null)
|
||||
}
|
||||
}
|
|
@ -28,4 +28,12 @@ class ServiceWindowHandler(service: Service) : WindowHandler(service) {
|
|||
override fun getCutoutInsets(call: MethodCall, result: MethodChannel.Result) {
|
||||
result.success(HashMap<String, Any>())
|
||||
}
|
||||
|
||||
override fun supportsHdr(call: MethodCall, result: MethodChannel.Result) {
|
||||
result.success(false)
|
||||
}
|
||||
|
||||
override fun setHdrColorMode(call: MethodCall, result: MethodChannel.Result) {
|
||||
result.success(null)
|
||||
}
|
||||
}
|
|
@ -18,6 +18,8 @@ abstract class WindowHandler(private val contextWrapper: ContextWrapper) : Metho
|
|||
"requestOrientation" -> Coresult.safe(call, result, ::requestOrientation)
|
||||
"isCutoutAware" -> Coresult.safe(call, result, ::isCutoutAware)
|
||||
"getCutoutInsets" -> Coresult.safe(call, result, ::getCutoutInsets)
|
||||
"supportsHdr" -> Coresult.safe(call, result, ::supportsHdr)
|
||||
"setHdrColorMode" -> Coresult.safe(call, result, ::setHdrColorMode)
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +46,10 @@ abstract class WindowHandler(private val contextWrapper: ContextWrapper) : Metho
|
|||
|
||||
abstract fun getCutoutInsets(call: MethodCall, result: MethodChannel.Result)
|
||||
|
||||
abstract fun supportsHdr(call: MethodCall, result: MethodChannel.Result)
|
||||
|
||||
abstract fun setHdrColorMode(call: MethodCall, result: MethodChannel.Result)
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = LogUtils.createTag<WindowHandler>()
|
||||
const val CHANNEL = "deckers.thibault/aves/window"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:aves/widgets/viewer/controls/controller.dart';
|
||||
import 'package:aves/widgets/viewer/controls/transitions.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
|
|
@ -17,11 +17,17 @@ abstract class WindowService {
|
|||
Future<bool> isCutoutAware();
|
||||
|
||||
Future<EdgeInsets> getCutoutInsets();
|
||||
|
||||
Future<bool> supportsHdr();
|
||||
|
||||
Future<void> setHdrColorMode(bool on);
|
||||
}
|
||||
|
||||
class PlatformWindowService implements WindowService {
|
||||
static const _platform = MethodChannel('deckers.thibault/aves/window');
|
||||
|
||||
bool? _isCutoutAware, _supportsHdr;
|
||||
|
||||
@override
|
||||
Future<bool> isActivity() async {
|
||||
try {
|
||||
|
@ -90,8 +96,6 @@ class PlatformWindowService implements WindowService {
|
|||
}
|
||||
}
|
||||
|
||||
bool? _isCutoutAware;
|
||||
|
||||
@override
|
||||
Future<bool> isCutoutAware() async {
|
||||
if (_isCutoutAware != null) return SynchronousFuture(_isCutoutAware!);
|
||||
|
@ -121,4 +125,27 @@ class PlatformWindowService implements WindowService {
|
|||
}
|
||||
return EdgeInsets.zero;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> supportsHdr() async {
|
||||
if (_supportsHdr != null) return SynchronousFuture(_supportsHdr!);
|
||||
try {
|
||||
final result = await _platform.invokeMethod('supportsHdr');
|
||||
_supportsHdr = result as bool?;
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
return _supportsHdr ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setHdrColorMode(bool on) async {
|
||||
try {
|
||||
await _platform.invokeMethod('setHdrColorMode', <String, dynamic>{
|
||||
'on': on,
|
||||
});
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,6 +154,7 @@ class _BugReportState extends State<BugReport> with FeedbackMixin {
|
|||
final androidInfo = await DeviceInfoPlugin().androidInfo;
|
||||
final storageVolumes = await storageService.getStorageVolumes();
|
||||
final storageGrants = await storageService.getGrantedDirectories();
|
||||
final supportsHdr = await windowService.supportsHdr();
|
||||
return [
|
||||
'Package: ${device.packageName}',
|
||||
'Installer: ${packageInfo.installerStore}',
|
||||
|
@ -162,7 +163,7 @@ class _BugReportState extends State<BugReport> with FeedbackMixin {
|
|||
'Android version: ${androidInfo.version.release}, API ${androidInfo.version.sdkInt}',
|
||||
'Android build: ${androidInfo.display}',
|
||||
'Device: ${androidInfo.manufacturer} ${androidInfo.model}',
|
||||
'Geocoder: ${device.hasGeocoder ? 'ready' : 'not available'}',
|
||||
'Support: dynamic colors=${device.isDynamicColorAvailable}, geocoder=${device.hasGeocoder}, HDR=$supportsHdr',
|
||||
'Mobile services: ${mobileServices.isServiceAvailable ? 'ready' : 'not available'}',
|
||||
'System locales: ${WidgetsBinding.instance.platformDispatcher.locales.join(', ')}',
|
||||
'Storage volumes: ${storageVolumes.map((v) => v.path).join(', ')}',
|
||||
|
|
|
@ -96,10 +96,10 @@ class GridThemeData {
|
|||
else if (entry.isAnimated)
|
||||
const AnimatedImageIcon()
|
||||
else ...[
|
||||
if (entry.isHdr && showHdr) const HdrIcon(),
|
||||
if (entry.isRaw && showRaw) const RawIcon(),
|
||||
if (entry.is360) const PanoramaIcon(),
|
||||
],
|
||||
if (entry.isHdr && showHdr) const HdrIcon(),
|
||||
if (entry.isMotionPhoto && showMotionPhoto) const MotionPhotoIcon(),
|
||||
if (entry.isMultiPage && !entry.isMotionPhoto) MultiPageIcon(entry: entry),
|
||||
if (entry.isGeotiff) const GeoTiffIcon(),
|
||||
|
|
|
@ -2,6 +2,8 @@ import 'dart:async';
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/entry/extensions/multipage.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/widgets/viewer/controls/cast.dart';
|
||||
import 'package:aves/widgets/viewer/controls/events.dart';
|
||||
|
@ -57,6 +59,7 @@ class ViewerController with CastMixin {
|
|||
);
|
||||
}
|
||||
_initialScale = initialScale;
|
||||
entryNotifier.addListener(_onEntryChanged);
|
||||
_autopilotNotifier = ValueNotifier(autopilot);
|
||||
_autopilotNotifier.addListener(_onAutopilotChanged);
|
||||
_onAutopilotChanged();
|
||||
|
@ -66,12 +69,21 @@ class ViewerController with CastMixin {
|
|||
if (kFlutterMemoryAllocationsEnabled) {
|
||||
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
|
||||
}
|
||||
entryNotifier.removeListener(_onEntryChanged);
|
||||
windowService.setHdrColorMode(false);
|
||||
_autopilotNotifier.dispose();
|
||||
_clearAutopilotAnimations();
|
||||
_stopPlayTimer();
|
||||
_streamController.close();
|
||||
}
|
||||
|
||||
Future<void> _onEntryChanged() async {
|
||||
if (await windowService.supportsHdr()) {
|
||||
final enabled = entryNotifier.value?.isHdr ?? false;
|
||||
await windowService.setHdrColorMode(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void _onAutopilotChanged() {
|
||||
_clearAutopilotAnimations();
|
||||
_stopPlayTimer();
|
||||
|
@ -115,79 +127,3 @@ class ViewerController with CastMixin {
|
|||
Future.delayed(ADurations.viewerHorizontalPageAnimation).then((_) => _autopilotAnimationControllers[vsync]?.forward());
|
||||
}
|
||||
}
|
||||
|
||||
class PageTransitionEffects {
|
||||
static TransitionBuilder fade(
|
||||
PageController pageController,
|
||||
int index, {
|
||||
required bool zoomIn,
|
||||
}) =>
|
||||
(context, child) {
|
||||
double opacity = 0;
|
||||
double dx = 0;
|
||||
double scale = 1;
|
||||
if (pageController.hasClients && pageController.position.haveDimensions) {
|
||||
final position = (pageController.page! - index).clamp(-1.0, 1.0);
|
||||
final width = pageController.position.viewportDimension;
|
||||
opacity = (1 - position.abs()).clamp(0, 1);
|
||||
dx = position * width;
|
||||
if (zoomIn) {
|
||||
scale = 1 + position;
|
||||
}
|
||||
}
|
||||
return Opacity(
|
||||
opacity: opacity,
|
||||
child: Transform.translate(
|
||||
offset: Offset(dx, 0),
|
||||
child: Transform.scale(
|
||||
scale: scale,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
static TransitionBuilder slide(
|
||||
PageController pageController,
|
||||
int index, {
|
||||
required bool parallax,
|
||||
}) =>
|
||||
(context, child) {
|
||||
double dx = 0;
|
||||
if (pageController.hasClients && pageController.position.haveDimensions) {
|
||||
final position = (pageController.page! - index).clamp(-1.0, 1.0);
|
||||
final width = pageController.position.viewportDimension;
|
||||
if (parallax) {
|
||||
dx = position * width / 2;
|
||||
}
|
||||
}
|
||||
return ClipRect(
|
||||
child: Transform.translate(
|
||||
offset: Offset(dx, 0),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
static TransitionBuilder none(
|
||||
PageController pageController,
|
||||
int index,
|
||||
) =>
|
||||
(context, child) {
|
||||
double opacity = 0;
|
||||
double dx = 0;
|
||||
if (pageController.hasClients && pageController.position.haveDimensions) {
|
||||
final position = (pageController.page! - index).clamp(-1.0, 1.0);
|
||||
final width = pageController.position.viewportDimension;
|
||||
opacity = (1 - position.abs()).roundToDouble().clamp(0, 1);
|
||||
dx = position * width;
|
||||
}
|
||||
return Opacity(
|
||||
opacity: opacity,
|
||||
child: Transform.translate(
|
||||
offset: Offset(dx, 0),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
77
lib/widgets/viewer/controls/transitions.dart
Normal file
77
lib/widgets/viewer/controls/transitions.dart
Normal file
|
@ -0,0 +1,77 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class PageTransitionEffects {
|
||||
static TransitionBuilder fade(
|
||||
PageController pageController,
|
||||
int index, {
|
||||
required bool zoomIn,
|
||||
}) =>
|
||||
(context, child) {
|
||||
double opacity = 0;
|
||||
double dx = 0;
|
||||
double scale = 1;
|
||||
if (pageController.hasClients && pageController.position.haveDimensions) {
|
||||
final position = (pageController.page! - index).clamp(-1.0, 1.0);
|
||||
final width = pageController.position.viewportDimension;
|
||||
opacity = (1 - position.abs()).clamp(0, 1);
|
||||
dx = position * width;
|
||||
if (zoomIn) {
|
||||
scale = 1 + position;
|
||||
}
|
||||
}
|
||||
return Opacity(
|
||||
opacity: opacity,
|
||||
child: Transform.translate(
|
||||
offset: Offset(dx, 0),
|
||||
child: Transform.scale(
|
||||
scale: scale,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
static TransitionBuilder slide(
|
||||
PageController pageController,
|
||||
int index, {
|
||||
required bool parallax,
|
||||
}) =>
|
||||
(context, child) {
|
||||
double dx = 0;
|
||||
if (pageController.hasClients && pageController.position.haveDimensions) {
|
||||
final position = (pageController.page! - index).clamp(-1.0, 1.0);
|
||||
final width = pageController.position.viewportDimension;
|
||||
if (parallax) {
|
||||
dx = position * width / 2;
|
||||
}
|
||||
}
|
||||
return ClipRect(
|
||||
child: Transform.translate(
|
||||
offset: Offset(dx, 0),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
static TransitionBuilder none(
|
||||
PageController pageController,
|
||||
int index,
|
||||
) =>
|
||||
(context, child) {
|
||||
double opacity = 0;
|
||||
double dx = 0;
|
||||
if (pageController.hasClients && pageController.position.haveDimensions) {
|
||||
final position = (pageController.page! - index).clamp(-1.0, 1.0);
|
||||
final width = pageController.position.viewportDimension;
|
||||
opacity = (1 - position.abs()).roundToDouble().clamp(0, 1);
|
||||
dx = position * width;
|
||||
}
|
||||
return Opacity(
|
||||
opacity: opacity,
|
||||
child: Transform.translate(
|
||||
offset: Offset(dx, 0),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
};
|
||||
}
|
|
@ -21,4 +21,10 @@ class FakeWindowService extends Fake implements WindowService {
|
|||
|
||||
@override
|
||||
Future<EdgeInsets> getCutoutInsets() => SynchronousFuture(EdgeInsets.zero);
|
||||
|
||||
@override
|
||||
Future<bool> supportsHdr() => SynchronousFuture(false);
|
||||
|
||||
@override
|
||||
Future<void> setHdrColorMode(bool on) => SynchronousFuture(null);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue