feat: music search (#1270)

* tweak

* feat: music search
This commit is contained in:
My-Responsitories
2025-09-17 00:33:33 +08:00
committed by GitHub
parent 3897efd82f
commit 349a4dfc0b
3 changed files with 113 additions and 69 deletions

View File

@@ -1,17 +1,19 @@
package com.example.piliplus package com.example.piliplus
import io.flutter.embedding.android.FlutterActivity import android.app.SearchManager
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import com.ryanheise.audioservice.AudioServiceActivity
import android.content.ComponentName import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore
import android.provider.Settings import android.provider.Settings
import android.view.WindowManager.LayoutParams import android.view.WindowManager.LayoutParams
import androidx.core.net.toUri
import com.ryanheise.audioservice.AudioServiceActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import kotlin.system.exitProcess import kotlin.system.exitProcess
class MainActivity : AudioServiceActivity() { class MainActivity : AudioServiceActivity() {
@@ -22,9 +24,9 @@ class MainActivity : AudioServiceActivity() {
methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "PiliPlus") methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "PiliPlus")
methodChannel.setMethodCallHandler { call, result -> methodChannel.setMethodCallHandler { call, result ->
if (call.method == "back") { when (call.method) {
back() "back" -> back();
} else if (call.method == "biliSendCommAntifraud") { "biliSendCommAntifraud" -> {
try { try {
val action = call.argument<Int>("action") ?: 0 val action = call.argument<Int>("action") ?: 0
val oid = call.argument<Number>("oid") ?: 0L val oid = call.argument<Number>("oid") ?: 0L
@@ -56,25 +58,51 @@ class MainActivity : AudioServiceActivity() {
putStringArrayListExtra("cookies", ArrayList(cookies)) putStringArrayListExtra("cookies", ArrayList(cookies))
} }
startActivity(intent) startActivity(intent)
} catch (e: Exception) {} } catch (_: Exception) {}
} else if (call.method == "linkVerifySettings") {
try {
val intent = Intent(android.provider.Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,
Uri.parse("package:" + context.packageName))
context.startActivity(intent)
} catch (t: Throwable) {
try {
val intent = Intent("android.intent.action.MAIN", Uri.parse("package:" + context.packageName))
intent.setClassName("com.android.settings", "com.android.settings.applications.InstalledAppOpenByDefaultActivity")
context.startActivity(intent)
} catch (t2: Throwable) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.parse("package:" + context.packageName))
context.startActivity(intent)
}
} }
"linkVerifySettings" -> {
val uri = ("package:" + context.packageName).toUri()
try {
val intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
Intent(Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS, uri)
} else { } else {
result.notImplemented() Intent("android.intent.action.MAIN", uri).setClassName("com.android.settings",
"com.android.settings.applications.InstalledAppOpenByDefaultActivity")
}
context.startActivity(intent)
} catch (_: Throwable) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, uri)
context.startActivity(intent)
}
}
"music" -> {
val title = call.argument<String>("title")
val intent = Intent(MediaStore.INTENT_ACTION_MEDIA_SEARCH).apply {
putExtra(SearchManager.QUERY, title)
putExtra(MediaStore.EXTRA_MEDIA_TITLE, title)
call.argument<String?>("artist")?.let { putExtra(MediaStore.EXTRA_MEDIA_ARTIST, it) }
call.argument<String?>("album")?.let { putExtra(MediaStore.EXTRA_MEDIA_ALBUM, it) }
addCategory(Intent.CATEGORY_DEFAULT)
}
try {
if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
startActivity(intent)
result.success(true)
return@setMethodCallHandler
}
} catch (_: Throwable) {}
try {
intent.action = MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH
if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
startActivity(intent)
result.success(true)
return@setMethodCallHandler
}
} catch (_: Throwable) {}
result.success(false)
}
else -> result.notImplemented()
} }
} }
} }
@@ -109,7 +137,7 @@ class MainActivity : AudioServiceActivity() {
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration?) { override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration?) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig) super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
MethodChannel( MethodChannel(
flutterEngine!!.getDartExecutor()!!.getBinaryMessenger(), flutterEngine!!.dartExecutor.binaryMessenger,
"floating" "floating"
).invokeMethod("onPipChanged", isInPictureInPictureMode) ).invokeMethod("onPipChanged", isInPictureInPictureMode)
} }

View File

@@ -1,3 +1,4 @@
import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/badge.dart';
@@ -448,10 +449,8 @@ class _MusicDetailPageState extends CommonDynPageState<MusicDetailPage> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
GestureDetector( GestureDetector(
// TODO: android intent ACTION_MEDIA_SEARCH onTap: () => _searchMusic(item),
onTap: () => Utils.copyText( onLongPress: () => Utils.copyText(item.musicTitle!),
item.musicTitle!,
),
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
child: MarqueeText( child: MarqueeText(
item.musicTitle!, item.musicTitle!,
@@ -493,13 +492,18 @@ class _MusicDetailPageState extends CommonDynPageState<MusicDetailPage> {
cid: item.mvCid!, cid: item.mvCid!,
aid: item.mvAid, aid: item.mvAid,
), ),
child: ColoredBox( child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(
Radius.circular(4),
),
color: theme.colorScheme.secondaryContainer color: theme.colorScheme.secondaryContainer
.withValues(alpha: 0.5), .withValues(alpha: 0.5),
),
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
vertical: 2, vertical: 3,
horizontal: 3, horizontal: 4,
), ),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@@ -676,4 +680,18 @@ class _MusicDetailPageState extends CommonDynPageState<MusicDetailPage> {
), ),
); );
} }
Future<void> _searchMusic(MusicDetail item) async {
final res =
Platform.isAndroid &&
(await Utils.channel.invokeMethod<bool>('music', {
'title': item.musicTitle,
'artist': item.originArtist ?? item.originArtistList,
'album': item.album,
}) ??
false);
if (!res) {
Utils.copyText(item.musicTitle!);
}
}
} }

View File

@@ -1,6 +1,5 @@
import 'dart:async' show FutureOr; import 'dart:async' show FutureOr;
import 'dart:io' show Platform; import 'dart:io' show Platform;
import 'dart:math';
import 'package:PiliPlus/grpc/grpc_req.dart'; import 'package:PiliPlus/grpc/grpc_req.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
@@ -21,13 +20,12 @@ import 'package:PiliPlus/utils/accounts/account.dart';
import 'package:PiliPlus/utils/request_utils.dart'; import 'package:PiliPlus/utils/request_utils.dart';
import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart' as web; import 'package:flutter_inappwebview/flutter_inappwebview.dart' as web;
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
abstract class LoginUtils { abstract class LoginUtils {
static final random = Random();
static FutureOr setWebCookie([Account? account]) { static FutureOr setWebCookie([Account? account]) {
if (Platform.isWindows) { if (Platform.isWindows) {
return null; return null;
@@ -163,7 +161,7 @@ abstract class LoginUtils {
static String generateBuvid() { static String generateBuvid() {
var md5Str = Iterable.generate( var md5Str = Iterable.generate(
32, 32,
(_) => random.nextInt(16).toRadixString(16), (_) => Utils.random.nextInt(16).toRadixString(16),
).join().toUpperCase(); ).join().toUpperCase();
return 'XY${md5Str[2]}${md5Str[12]}${md5Str[22]}$md5Str'; return 'XY${md5Str[2]}${md5Str[12]}${md5Str[22]}$md5Str';
} }
@@ -188,11 +186,11 @@ abstract class LoginUtils {
final String randomHex32 = List.generate( final String randomHex32 = List.generate(
32, 32,
(index) => random.nextInt(16).toRadixString(16), (index) => Utils.random.nextInt(16).toRadixString(16),
).join(); ).join();
final String randomHex16 = List.generate( final String randomHex16 = List.generate(
16, 16,
(index) => random.nextInt(16).toRadixString(16), (index) => Utils.random.nextInt(16).toRadixString(16),
).join(); ).join();
final String deviceID = randomHex32 + yyyyMMddHHmmss + randomHex16; final String deviceID = randomHex32 + yyyyMMddHHmmss + randomHex16;