fix: replace open_file_plus with native Kotlin MethodChannel for APK install
open_file_plus 3.4.1 uses deprecated PluginRegistry.Registrar API incompatible with current Flutter. Replaced with a MethodChannel in MainActivity.kt that calls FileProvider.getUriForFile() and launches the system package installer directly. Also rework chat_input: 4px border radius on desktop, vPad 18px, 56px send button. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,51 @@
|
||||
package chat.pyramid.pyramid
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.core.content.FileProvider
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import java.io.File
|
||||
|
||||
class MainActivity : FlutterActivity()
|
||||
class MainActivity : FlutterActivity() {
|
||||
private val channel = "chat.pyramid.pyramid/install"
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, channel)
|
||||
.setMethodCallHandler { call, result ->
|
||||
if (call.method == "installApk") {
|
||||
val path = call.argument<String>("path")
|
||||
if (path == null) {
|
||||
result.error("INVALID_ARG", "path is null", null)
|
||||
return@setMethodCallHandler
|
||||
}
|
||||
try {
|
||||
val file = File(path)
|
||||
val uri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
FileProvider.getUriForFile(
|
||||
this,
|
||||
"${applicationContext.packageName}.fileprovider",
|
||||
file
|
||||
)
|
||||
} else {
|
||||
Uri.fromFile(file)
|
||||
}
|
||||
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||
setDataAndType(uri, "application/vnd.android.package-archive")
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
startActivity(intent)
|
||||
result.success(null)
|
||||
} catch (e: Exception) {
|
||||
result.error("INSTALL_ERROR", e.message, null)
|
||||
}
|
||||
} else {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,11 +43,7 @@ class _ChatInputState extends State<ChatInput> {
|
||||
if (text.isEmpty) return;
|
||||
_ctrl.clear();
|
||||
setState(() => _canSend = false);
|
||||
|
||||
await widget.room.sendTextEvent(
|
||||
text,
|
||||
inReplyTo: widget.replyTo,
|
||||
);
|
||||
await widget.room.sendTextEvent(text, inReplyTo: widget.replyTo);
|
||||
widget.onClearReply?.call();
|
||||
}
|
||||
|
||||
@@ -59,21 +55,30 @@ class _ChatInputState extends State<ChatInput> {
|
||||
defaultTargetPlatform == TargetPlatform.linux ||
|
||||
defaultTargetPlatform == TargetPlatform.macOS);
|
||||
|
||||
// Desktop: taller bar, 4px square corners, bigger button
|
||||
// Mobile: compact bar, pill shape, smaller button
|
||||
final fieldRadius = isDesktop ? 4.0 : 20.0;
|
||||
final btnSize = isDesktop ? 56.0 : 44.0;
|
||||
final btnRadius = isDesktop ? 6.0 : 12.0;
|
||||
final iconSize = isDesktop ? 24.0 : 20.0;
|
||||
final fontSize = isDesktop ? 15.0 : 14.0;
|
||||
final vPad = isDesktop ? 18.0 : 10.0;
|
||||
final hPad = isDesktop ? 16.0 : 14.0;
|
||||
final outerPad = isDesktop
|
||||
? const EdgeInsets.fromLTRB(12, 10, 12, 12)
|
||||
: const EdgeInsets.fromLTRB(8, 4, 8, 8);
|
||||
|
||||
return SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (widget.replyTo != null) _ReplyBar(
|
||||
event: widget.replyTo!,
|
||||
onClear: widget.onClearReply ?? () {},
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
isDesktop ? 12 : 8,
|
||||
isDesktop ? 10 : 4,
|
||||
isDesktop ? 12 : 8,
|
||||
isDesktop ? 14 : 8,
|
||||
if (widget.replyTo != null)
|
||||
_ReplyBar(
|
||||
event: widget.replyTo!,
|
||||
onClear: widget.onClearReply ?? () {},
|
||||
),
|
||||
Padding(
|
||||
padding: outerPad,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
@@ -83,19 +88,24 @@ class _ChatInputState extends State<ChatInput> {
|
||||
minLines: 1,
|
||||
maxLines: 6,
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
style: TextStyle(fontSize: isDesktop ? 16 : 14),
|
||||
style: TextStyle(fontSize: fontSize),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Nachricht...',
|
||||
hintStyle: TextStyle(
|
||||
fontSize: isDesktop ? 16 : 14,
|
||||
color: theme.colorScheme.onSurface.withAlpha(100),
|
||||
),
|
||||
hintStyle: TextStyle(fontSize: fontSize),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: isDesktop ? 22 : 16,
|
||||
vertical: isDesktop ? 20 : 10,
|
||||
horizontal: hPad,
|
||||
vertical: vPad,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(isDesktop ? 16 : 24),
|
||||
borderRadius: BorderRadius.circular(fieldRadius),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(fieldRadius),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(fieldRadius),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
filled: true,
|
||||
@@ -113,18 +123,18 @@ class _ChatInputState extends State<ChatInput> {
|
||||
onTap: _canSend ? _send : null,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 150),
|
||||
width: isDesktop ? 60 : 44,
|
||||
height: isDesktop ? 60 : 44,
|
||||
width: btnSize,
|
||||
height: btnSize,
|
||||
decoration: BoxDecoration(
|
||||
color: _canSend
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.primary.withAlpha(70),
|
||||
borderRadius: BorderRadius.circular(isDesktop ? 16 : 14),
|
||||
borderRadius: BorderRadius.circular(btnRadius),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.send_rounded,
|
||||
size: isDesktop ? 26 : 20,
|
||||
size: iconSize,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:open_file_plus/open_file_plus.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:pyramid/core/theme.dart';
|
||||
@@ -111,27 +111,28 @@ class _UpdateDownloadDialogState extends ConsumerState<_UpdateDownloadDialog>
|
||||
}
|
||||
}
|
||||
|
||||
static const _installChannel =
|
||||
MethodChannel('chat.pyramid.pyramid/install');
|
||||
|
||||
Future<void> _install(File file) async {
|
||||
setState(() => _state = _DownloadState.installing);
|
||||
|
||||
if (Platform.isWindows) {
|
||||
await Process.start(file.path, [], runInShell: false);
|
||||
if (mounted) Navigator.of(context).pop();
|
||||
exit(0);
|
||||
} else if (Platform.isAndroid) {
|
||||
final result = await OpenFile.open(
|
||||
file.path,
|
||||
type: 'application/vnd.android.package-archive',
|
||||
);
|
||||
try {
|
||||
if (Platform.isWindows) {
|
||||
await Process.start(file.path, [], runInShell: false);
|
||||
if (mounted) Navigator.of(context).pop();
|
||||
exit(0);
|
||||
} else if (Platform.isAndroid) {
|
||||
await _installChannel
|
||||
.invokeMethod('installApk', {'path': file.path});
|
||||
if (mounted) setState(() => _state = _DownloadState.done);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
if (result.type == ResultType.done) {
|
||||
setState(() => _state = _DownloadState.done);
|
||||
} else {
|
||||
setState(() {
|
||||
_state = _DownloadState.error;
|
||||
_errorMsg = result.message;
|
||||
});
|
||||
}
|
||||
setState(() {
|
||||
_state = _DownloadState.error;
|
||||
_errorMsg = e.toString().split('\n').first;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -757,14 +757,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.3.0"
|
||||
open_file_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: open_file_plus
|
||||
sha256: "01ad7011d46d253aa22e5f0bc7345ca49cfe652c50ae9b0af945491c1728a8c3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.1+1"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -57,7 +57,6 @@ dependencies:
|
||||
# File handling
|
||||
file_picker: ^8.1.7
|
||||
desktop_drop: ^0.4.4
|
||||
open_file_plus: ^3.4.1
|
||||
|
||||
# Media playback
|
||||
media_kit: ^1.1.11
|
||||
|
||||
Reference in New Issue
Block a user