Compare commits

...

3 Commits

Author SHA1 Message Date
dom
51163dd985 create user shorcut on Android
Signed-off-by: dom <githubaccount56556@proton.me>
2026-04-05 20:11:13 +08:00
dom
f0d9b3a9a7 upgrade dep
Signed-off-by: dom <githubaccount56556@proton.me>
2026-04-05 20:11:13 +08:00
dom
8f3707fbf1 opt reserve btn
Signed-off-by: dom <githubaccount56556@proton.me>
2026-04-05 13:35:19 +08:00
5 changed files with 145 additions and 46 deletions

View File

@@ -1,11 +1,16 @@
package com.example.piliplus package com.example.piliplus
import android.app.PendingIntent
import android.app.PictureInPictureParams import android.app.PictureInPictureParams
import android.app.SearchManager import android.app.SearchManager
import android.content.ComponentName import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.BitmapFactory
import android.graphics.drawable.Icon
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore import android.provider.MediaStore
@@ -16,6 +21,7 @@ import com.ryanheise.audioservice.AudioServiceActivity
import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
import kotlin.system.exitProcess import kotlin.system.exitProcess
import java.io.File
class MainActivity : AudioServiceActivity() { class MainActivity : AudioServiceActivity() {
private lateinit var methodChannel: MethodChannel private lateinit var methodChannel: MethodChannel
@@ -133,6 +139,38 @@ class MainActivity : AudioServiceActivity() {
} }
} }
"createShortcut" -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
val shortcutManager =
context.getSystemService(ShortcutManager::class.java)
if (shortcutManager.isRequestPinShortcutSupported) {
val id = call.argument<String>("id")!!
val uri = call.argument<String>("uri")!!
val label = call.argument<String>("label")!!
val icon = call.argument<String>("icon")!!
val bitmap = BitmapFactory.decodeFile(icon)
val shortcut =
ShortcutInfo.Builder(context, id)
.setShortLabel(label)
.setIcon(Icon.createWithAdaptiveBitmap(bitmap))
.setIntent(Intent(Intent.ACTION_VIEW, uri.toUri()))
.build()
val pinIntent =
shortcutManager.createShortcutResultIntent(shortcut)
val pendingIntent = PendingIntent.getBroadcast(
context, 0, pinIntent, PendingIntent.FLAG_IMMUTABLE
)
shortcutManager.requestPinShortcut(
shortcut,
pendingIntent.intentSender
)
}
} catch (e: Exception) {
}
}
}
else -> result.notImplemented() else -> result.notImplemented()
} }
} }

View File

@@ -1,3 +1,5 @@
import 'package:PiliPlus/utils/parse_string.dart';
class ReservationCardItem { class ReservationCardItem {
int? sid; int? sid;
String? name; String? name;
@@ -26,7 +28,7 @@ class ReservationCardItem {
total: json['total'] ?? 0, total: json['total'] ?? 0,
isFollow: json['is_follow'] == 1, isFollow: json['is_follow'] == 1,
livePlanStartTime: json['live_plan_start_time'] as int?, livePlanStartTime: json['live_plan_start_time'] as int?,
descText1: json['desc_text_1']?['text'], descText1: noneNullOrEmptyString(json['desc_text_1']?['text']),
dynamicId: json['dynamic_id'] as String?, dynamicId: json['dynamic_id'] as String?,
lotteryPrizeInfo: json['lottery_prize_info'] == null lotteryPrizeInfo: json['lottery_prize_info'] == null
? null ? null

View File

@@ -28,6 +28,7 @@ class MemberController extends CommonDataController<SpaceData, SpaceData?>
MemberController({required this.mid}); MemberController({required this.mid});
int mid; int mid;
String? username; String? username;
String? userAvatar;
late final account = Accounts.main; late final account = Accounts.main;
@@ -71,7 +72,10 @@ class MemberController extends CommonDataController<SpaceData, SpaceData?>
final data = response.response; final data = response.response;
final card = data.card; final card = data.card;
username = card?.name ?? ''; username = card?.name ?? '';
userAvatar = card?.face;
isFollowed = card?.relation?.isFollowed; isFollowed = card?.relation?.isFollowed;
// charge // charge
final elec = data.elec; final elec = data.elec;
charges = elec?.list; charges = elec?.list;

View File

@@ -2,6 +2,7 @@ import 'dart:io' show Platform;
import 'dart:math' as math; import 'dart:math' as math;
import 'package:PiliPlus/common/style.dart'; import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
import 'package:PiliPlus/common/widgets/dialog/report_member.dart'; import 'package:PiliPlus/common/widgets/dialog/report_member.dart';
import 'package:PiliPlus/common/widgets/dynamic_sliver_app_bar/dynamic_sliver_app_bar.dart'; import 'package:PiliPlus/common/widgets/dynamic_sliver_app_bar/dynamic_sliver_app_bar.dart';
import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart'; import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart';
@@ -33,11 +34,15 @@ import 'package:PiliPlus/pages/member_video_web/archive/view.dart';
import 'package:PiliPlus/pages/member_video_web/season_series/view.dart'; import 'package:PiliPlus/pages/member_video_web/season_series/view.dart';
import 'package:PiliPlus/utils/date_utils.dart'; import 'package:PiliPlus/utils/date_utils.dart';
import 'package:PiliPlus/utils/extension/context_ext.dart'; import 'package:PiliPlus/utils/extension/context_ext.dart';
import 'package:PiliPlus/utils/extension/string_ext.dart';
import 'package:PiliPlus/utils/num_utils.dart';
import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/utils.dart';
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@@ -209,6 +214,60 @@ class _MemberPageState extends State<MemberPage> {
...list.map((e) { ...list.map((e) {
return Builder( return Builder(
builder: (context) { builder: (context) {
Widget trailing = FilledButton.tonal(
onPressed: () async {
final isFollow = e.isFollow;
final res = await UserHttp.spaceReserve(
sid: e.sid!,
isFollow: isFollow,
);
if (res.isSuccess) {
if (!context.mounted) return;
e
..total += isFollow ? -1 : 1
..isFollow = !isFollow;
(context as Element).markNeedsBuild();
} else {
res.toast();
}
},
style: FilledButton.styleFrom(
backgroundColor: e.isFollow
? scheme.onInverseSurface
: null,
foregroundColor: e.isFollow ? scheme.outline : null,
tapTargetSize: .shrinkWrap,
minimumSize: const Size(68, 40),
padding: const .symmetric(horizontal: 10),
visualDensity: const .new(horizontal: -2, vertical: -3),
shape: const RoundedRectangleBorder(
borderRadius: .all(.circular(6)),
),
),
child: Text(
'${e.isFollow ? '' : ''}预约',
style: const TextStyle(fontSize: 13),
),
);
if (e.dynamicId?.isNotEmpty ?? false) {
trailing = Row(
spacing: 8,
mainAxisSize: .min,
children: [
iconButton(
tooltip: '预约动态',
size: 32,
iconSize: 20,
iconColor: scheme.outline,
icon: const Icon(Icons.open_in_browser),
onPressed: () => PageUtils.pushDynFromId(
id: e.dynamicId,
),
),
trailing,
],
);
}
return ListTile( return ListTile(
dense: true, dense: true,
title: Text( title: Text(
@@ -221,7 +280,11 @@ class _MemberPageState extends State<MemberPage> {
style: TextStyle(fontSize: 12, color: scheme.outline), style: TextStyle(fontSize: 12, color: scheme.outline),
TextSpan( TextSpan(
children: [ children: [
TextSpan(text: '${e.descText1} ${e.total}人预约'), TextSpan(
text:
'${e.descText1 == null ? '' : '${e.descText1} '}'
'${NumUtils.numFormat(e.total)}人预约',
),
if (e.lotteryPrizeInfo case final lottery?) ...[ if (e.lotteryPrizeInfo case final lottery?) ...[
const TextSpan(text: '\n'), const TextSpan(text: '\n'),
WidgetSpan( WidgetSpan(
@@ -254,44 +317,7 @@ class _MemberPageState extends State<MemberPage> {
), ),
), ),
), ),
trailing: FilledButton.tonal( trailing: trailing,
onPressed: () async {
final isFollow = e.isFollow;
final res = await UserHttp.spaceReserve(
sid: e.sid!,
isFollow: isFollow,
);
if (res.isSuccess) {
if (!context.mounted) return;
e
..total += isFollow ? -1 : 1
..isFollow = !isFollow;
(context as Element).markNeedsBuild();
} else {
res.toast();
}
},
style: FilledButton.styleFrom(
shape: const RoundedRectangleBorder(
borderRadius: .all(.circular(6)),
),
backgroundColor: e.isFollow
? scheme.onInverseSurface
: null,
foregroundColor: e.isFollow ? scheme.outline : null,
visualDensity: const VisualDensity(
horizontal: -2,
vertical: -3,
),
tapTargetSize: .shrinkWrap,
padding: const .symmetric(horizontal: 10),
minimumSize: const Size(68, 40),
),
child: Text(
'${e.isFollow ? '' : ''}预约',
style: const TextStyle(fontSize: 13),
),
),
); );
}, },
); );
@@ -357,11 +383,9 @@ class _MemberPageState extends State<MemberPage> {
], ],
), ),
), ),
if (kDebugMode || Platform.isIOS) if (kDebugMode || PlatformUtils.isMobile)
PopupMenuItem( PopupMenuItem(
onTap: () => PageUtils.launchURL( onTap: _createShortcut,
'https://www.bilibili.com/blackboard/disablelink/go-to-up-space.html?mid=$_mid',
),
child: const Row( child: const Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@@ -665,4 +689,35 @@ class _MemberPageState extends State<MemberPage> {
SmartDialog.showToast(e.toString()); SmartDialog.showToast(e.toString());
} }
} }
void _createShortcut() {
if (Platform.isIOS) {
PageUtils.launchURL(
'https://www.bilibili.com/blackboard/disablelink/go-to-up-space.html?mid=$_mid',
);
} else if (Platform.isAndroid) {
_createShortcutAndroid();
}
}
Future<void> _createShortcutAndroid() async {
try {
SmartDialog.showLoading();
final file = (await DefaultCacheManager().getSingleFile(
'${_userController.userAvatar!}@200w_200h.webp'.http2https,
));
SmartDialog.dismiss();
await Utils.channel.invokeMethod(
'createShortcut',
<String, String>{
'id': _userController.mid.toString(),
'uri': 'bilibili://space/${_userController.mid}',
'label': _userController.username!,
'icon': file.path,
},
);
} catch (e) {
SmartDialog.showToast(e.toString());
}
}
} }

View File

@@ -530,10 +530,10 @@ packages:
description: description:
path: "." path: "."
ref: mod ref: mod
resolved-ref: a3714717dacde780e6dd55c21094112cdf6ee3ba resolved-ref: aba199942d9b92b5c08cf3559d8a8f06594f0480
url: "https://github.com/bggRGjQaUbCoE/flutter_file_picker.git" url: "https://github.com/bggRGjQaUbCoE/flutter_file_picker.git"
source: git source: git
version: "11.0.0" version: "11.0.1"
file_selector_linux: file_selector_linux:
dependency: transitive dependency: transitive
description: description: