Fix phonebook refresh flicker and duplicate Android recent tasks
Tests / test (push) Canceled after 0s
Build Android (FCM) / build-android-fcm (push) Canceled after 0s
Build Android / build-android (push) Canceled after 0s
Build iOS / build-ios (push) Canceled after 0s
Build Linux / build-linux (push) Canceled after 0s
Build macOS / build-macos (push) Canceled after 0s
Build Windows / build-windows (push) Canceled after 0s
Native crypto / native-crypto (push) Canceled after 0s
Release (main) / android (oneme) (push) Canceled after 0s
Release (main) / android (qlyra) (push) Canceled after 0s
Release (main) / windows (push) Canceled after 0s
Release (main) / linux (push) Canceled after 0s
Release (main) / macos (push) Canceled after 0s
Release (main) / ios (push) Canceled after 0s
Release (main) / release (push) Canceled after 0s

This commit is contained in:
sevenhill
2026-10-08 06:22:47 +03:00
parent 6942981674
commit 68f7957043
10 changed files with 253 additions and 18 deletions
+21 -9
View File
@@ -1,5 +1,6 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -84,11 +85,17 @@ class DeviceContactsService {
return _readBook();
}
static Future<bool> reload() async {
static Future<bool> reload() => ensureLoadedInteractive(force: true);
@visibleForTesting
static Future<bool> readBookForTesting() => _readBook();
@visibleForTesting
static void resetMemoryForTesting() {
_loaded = false;
_byLast10.clear();
_phonebook.clear();
return ensureLoadedInteractive(force: true);
_readInFlight = null;
}
static Future<void> _forgetDenial() async {
@@ -100,30 +107,35 @@ class DeviceContactsService {
.whenComplete(() => _readInFlight = null);
static Future<bool> _readBookOnce() async {
_loaded = false;
_byLast10.clear();
_phonebook.clear();
try {
FlutterContacts.config.includeNonVisibleOnAndroid = true;
final contacts = await FlutterContacts.getContacts(withProperties: true);
_byLast10.clear();
_phonebook.clear();
final nextByLast10 = <String, String>{};
final nextPhonebook = <String, String>{};
for (final contact in contacts) {
final name = contact.displayName.trim();
for (final phone in contact.phones) {
final normalized = normalizePhone(phone.number);
if (normalized != null) {
_phonebook.putIfAbsent(
nextPhonebook.putIfAbsent(
normalized,
() => name.isEmpty ? normalized : name,
);
}
final key = _last10(phone.number);
if (key != null) {
_byLast10.putIfAbsent(key, () => name);
nextByLast10.putIfAbsent(key, () => name);
}
}
}
// Keep the last complete snapshot while the native reader is awaiting.
// Replacing both maps synchronously prevents a fallback to server names.
_byLast10
..clear()
..addAll(nextByLast10);
_phonebook
..clear()
..addAll(nextPhonebook);
_loaded = true;
logger.i(
'Телефонная книга: прочитано ${contacts.length} записей, '
+10 -7
View File
@@ -3,7 +3,6 @@ import 'dart:io';
import 'package:crypto/crypto.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/services.dart';
import 'package:open_filex/open_filex.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path_provider/path_provider.dart';
@@ -70,14 +69,18 @@ abstract class UpdateInstaller {
);
}
final opened = await OpenFilex.open(
file.path,
type: 'application/vnd.android.package-archive',
);
if (opened.type != ResultType.done) {
try {
final launched = await _integrityChannel.invokeMethod<bool>(
'launchApkInstaller',
{'path': file.path},
);
if (launched != true) {
return const UpdateInstallResult(UpdateInstallStatus.installFailed);
}
} on PlatformException catch (error) {
return UpdateInstallResult(
UpdateInstallStatus.installFailed,
error: opened.message,
error: error.message,
);
}
return const UpdateInstallResult(UpdateInstallStatus.done);