Skip to content

Commit

Permalink
Merge PR Yubico#1344.
Browse files Browse the repository at this point in the history
  • Loading branch information
AdamVe committed Jan 24, 2024
2 parents 2843066 + d56166b commit e58a25e
Show file tree
Hide file tree
Showing 18 changed files with 1,112 additions and 122 deletions.
2 changes: 2 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,7 @@ analyzer:
- "**/*.g.dart"
- "**/*.freezed.dart"
- "build/**/intermediates/**/AndroidManifest.xml"
errors:
invalid_annotation_target: ignore # see https://github.com/rrousselGit/freezed/issues/488
plugins:
- custom_lint
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022-2023 Yubico.
* Copyright (C) 2022-2024 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -38,6 +38,7 @@ import androidx.activity.viewModels
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.lifecycle.lifecycleScope
import com.google.android.material.color.DynamicColors
import com.yubico.authenticator.logging.FlutterLog
import com.yubico.authenticator.oath.AppLinkMethodChannel
import com.yubico.authenticator.oath.OathManager
Expand Down Expand Up @@ -383,6 +384,11 @@ class MainActivity : FlutterFragmentActivity() {
methodCall.arguments as Boolean,
)
)

"getPrimaryColor" -> result.success(
getPrimaryColor(this@MainActivity)
)

"getAndroidSdkVersion" -> result.success(
Build.VERSION.SDK_INT
)
Expand Down Expand Up @@ -456,6 +462,30 @@ class MainActivity : FlutterFragmentActivity() {
return FLAG_SECURE != (window.attributes.flags and FLAG_SECURE)
}

private fun getPrimaryColor(context: Context): Int? {
if (DynamicColors.isDynamicColorAvailable()) {
val dynamicColorContext = DynamicColors.wrapContextIfAvailable(
context,
com.google.android.material.R.style.ThemeOverlay_Material3_DynamicColors_DayNight
)

val typedArray = dynamicColorContext.obtainStyledAttributes(
intArrayOf(
android.R.attr.colorPrimary,
)
)
try {
return if (typedArray.hasValue(0))
typedArray.getColor(0, 0)
else
null
} finally {
typedArray.recycle()
}
}
return null
}

@SuppressLint("SourceLockedOrientationActivity")
private fun forcePortraitOrientation() {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
Expand All @@ -465,5 +495,5 @@ class MainActivity : FlutterFragmentActivity() {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}

private fun isPortraitOnly() = resources.getBoolean(R.bool.portrait_only);
private fun isPortraitOnly() = resources.getBoolean(R.bool.portrait_only)
}
7 changes: 6 additions & 1 deletion lib/android/app_methods.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022-2023 Yubico.
* Copyright (C) 2022-2024 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -52,6 +52,11 @@ Future<int> getAndroidSdkVersion() async {
return await appMethodsChannel.invokeMethod('getAndroidSdkVersion');
}

Future<Color?> getPrimaryColor() async {
final value = await appMethodsChannel.invokeMethod('getPrimaryColor');
return value != null ? Color(value) : null;
}

Future<void> setPrimaryClip(String toClipboard, bool isSensitive) async {
await appMethodsChannel.invokeMethod('setPrimaryClip',
{'toClipboard': toClipboard, 'isSensitive': isSensitive});
Expand Down
5 changes: 3 additions & 2 deletions lib/android/init.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022-2023 Yubico.
* Copyright (C) 2022-2024 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -84,7 +84,8 @@ Future<Widget> initialize() async {
androidNfcSupportProvider.overrideWithValue(await getHasNfc()),
supportedThemesProvider.overrideWith(
(ref) => ref.watch(androidSupportedThemesProvider),
)
),
primaryColorProvider.overrideWithValue(await getPrimaryColor()),
],
child: DismissKeyboard(
child: YubicoAuthenticatorApp(page: Consumer(
Expand Down
7 changes: 3 additions & 4 deletions lib/app/app.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Yubico.
* Copyright (C) 2022,2024 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,7 +19,6 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../theme.dart';
import 'logging.dart';
import 'shortcuts.dart';
import 'state.dart';
Expand All @@ -34,8 +33,8 @@ class YubicoAuthenticatorApp extends StatelessWidget {
child: Consumer(
builder: (context, ref, _) => MaterialApp(
title: ref.watch(l10nProvider).app_name,
theme: AppTheme.lightTheme,
darkTheme: AppTheme.darkTheme,
theme: ref.watch(lightThemeProvider),
darkTheme: ref.watch(darkThemeProvider),
themeMode: ref.watch(themeModeProvider),
home: page,
debugShowCheckedModeBanner: false,
Expand Down
75 changes: 75 additions & 0 deletions lib/app/key_customization/key_customization.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2024 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import 'dart:convert';
import 'dart:ui';

import 'package:logging/logging.dart';
import 'package:shared_preferences/shared_preferences.dart';

import '../logging.dart';
import 'models.dart';

final _log = Logger('key_customization_manager');
const _prefKeyCustomizations = 'KEY_CUSTOMIZATIONS';

class KeyCustomizationManager {
final SharedPreferences _prefs;
final Map<String, KeyCustomization> _customizations;

KeyCustomizationManager(this._prefs)
: _customizations =
readCustomizations(_prefs.getString(_prefKeyCustomizations));

static Map<String, KeyCustomization> readCustomizations(String? pref) {
if (pref == null) {
return {};
}

try {
final retval = <String, KeyCustomization>{};
for (var element in json.decode(pref)) {
final keyCustomization = KeyCustomization.fromJson(element);
retval[keyCustomization.serial] = keyCustomization;
}
return retval;
} catch (e) {
_log.error('Failure reading customizations: $e');
return {};
}
}

KeyCustomization? get(String? serial) {
_log.debug('Getting key customization for $serial');
return _customizations[serial];
}

void set({required String serial, String? name, Color? color}) {
_log.debug('Setting key customization for $serial: $name, $color');
if (name == null && color == null) {
// remove this customization
_customizations.removeWhere((key, value) => key == serial);
} else {
_customizations[serial] =
KeyCustomization(serial: serial, name: name, color: color);
}
}

Future<void> write() async {
await _prefs.setString(
_prefKeyCustomizations, json.encode(_customizations.values.toList()));
}
}
45 changes: 45 additions & 0 deletions lib/app/key_customization/models.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2024 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import 'dart:ui';

import 'package:freezed_annotation/freezed_annotation.dart';

part 'models.freezed.dart';

part 'models.g.dart';

@freezed
class KeyCustomization with _$KeyCustomization {
factory KeyCustomization({
required String serial,
@JsonKey(includeIfNull: false) String? name,
@JsonKey(includeIfNull: false) @_ColorConverter() Color? color,
}) = _KeyCustomization;

factory KeyCustomization.fromJson(Map<String, dynamic> json) =>
_$KeyCustomizationFromJson(json);
}

class _ColorConverter implements JsonConverter<Color?, int?> {
const _ColorConverter();

@override
Color? fromJson(int? json) => json != null ? Color(json) : null;

@override
int? toJson(Color? object) => object?.value;
}
Loading

0 comments on commit e58a25e

Please sign in to comment.