Android SafeArea issues fixed

configurations for different plugin for android added
adjusted platform bound opertations
This commit is contained in:
Kingkor Roy Tirtho 2022-03-15 19:47:29 +06:00
parent 39a92a56f3
commit c64f329c42
19 changed files with 793 additions and 736 deletions

View File

@ -1,34 +1,30 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="oss.krtirtho.spotube">
package="oss.krtirtho.spotube">
<application <uses-permission android:name="android.permission.INTERNET" />
android:label="spotube"
android:name="${applicationName}" <queries>
android:icon="@mipmap/ic_launcher"> <!-- If your app opens https URLs -->
<activity <intent>
android:name=".MainActivity" <action android:name="android.intent.action.VIEW" />
android:exported="true" <data android:scheme="https" />
android:launchMode="singleTop" </intent>
android:theme="@style/LaunchTheme" </queries>
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" <application android:label="spotube" android:name="${applicationName}" android:icon="@mipmap/ic_launcher" android:usesCleartextTraffic="true">
android:windowSoftInputMode="adjustResize"> <activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as <!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. --> to determine the Window background behind the Flutter UI. -->
<meta-data <meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" />
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<!-- Don't delete the meta-data below. <!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data <meta-data android:name="flutterEmbedding" android:value="2" />
android:name="flutterEmbedding"
android:value="2" />
</application> </application>
</manifest> </manifest>

View File

@ -1,5 +1,5 @@
buildscript { buildscript {
ext.kotlin_version = '1.3.50' ext.kotlin_version = '1.6.10'
repositories { repositories {
google() google()
mavenCentral() mavenCentral()

View File

@ -37,5 +37,11 @@ end
post_install do |installer| post_install do |installer|
installer.pods_project.targets.each do |target| installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target) flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
'AUDIO_SESSION_MICROPHONE=0'
]
end
end end
end end

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
@ -23,7 +23,7 @@
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true />
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
@ -42,6 +42,13 @@
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<true/> <true />
</dict> <key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true />
<key>NSAllowsArbitraryLoadsForMedia</key>
<true />
</dict>
</dict>
</plist> </plist>

View File

@ -38,7 +38,8 @@ class AlbumView extends ConsumerWidget {
var isPlaylistPlaying = playback.currentPlaylist?.id == album.id; var isPlaylistPlaying = playback.currentPlaylist?.id == album.id;
SpotifyApi spotify = ref.watch(spotifyProvider); SpotifyApi spotify = ref.watch(spotifyProvider);
return Scaffold( return SafeArea(
child: Scaffold(
body: FutureBuilder<Iterable<TrackSimple>>( body: FutureBuilder<Iterable<TrackSimple>>(
future: spotify.albums.getTracks(album.id!).all(), future: spotify.albums.getTracks(album.id!).all(),
builder: (context, snapshot) { builder: (context, snapshot) {
@ -95,6 +96,7 @@ class AlbumView extends ConsumerWidget {
], ],
); );
}), }),
),
); );
} }
} }

View File

@ -59,7 +59,8 @@ class _ArtistAlbumViewState extends ConsumerState<ArtistAlbumView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return SafeArea(
child: Scaffold(
appBar: const PageWindowTitleBar(leading: BackButton()), appBar: const PageWindowTitleBar(leading: BackButton()),
body: Column( body: Column(
children: [ children: [
@ -86,6 +87,7 @@ class _ArtistAlbumViewState extends ConsumerState<ArtistAlbumView> {
), ),
], ],
), ),
),
); );
} }
} }

View File

@ -45,7 +45,8 @@ class ArtistProfile extends HookConsumerWidget {
final breakpoint = useBreakpoints(); final breakpoint = useBreakpoints();
return Scaffold( return SafeArea(
child: Scaffold(
appBar: const PageWindowTitleBar( appBar: const PageWindowTitleBar(
leading: BackButton(), leading: BackButton(),
), ),
@ -128,7 +129,8 @@ class ArtistProfile extends HookConsumerWidget {
text: snapshot text: snapshot
.data?.externalUrls?.spotify), .data?.externalUrls?.spotify),
).then((val) { ).then((val) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context)
.showSnackBar(
const SnackBar( const SnackBar(
width: 300, width: 300,
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
@ -198,8 +200,8 @@ class ArtistProfile extends HookConsumerWidget {
: Icons.play_arrow_rounded), : Icons.play_arrow_rounded),
color: Colors.white, color: Colors.white,
onPressed: trackSnapshot.hasData onPressed: trackSnapshot.hasData
? () => ? () => playPlaylist(
playPlaylist(trackSnapshot.data!.toList()) trackSnapshot.data!.toList())
: null, : null,
), ),
) )
@ -310,6 +312,7 @@ class ArtistProfile extends HookConsumerWidget {
); );
}, },
), ),
),
); );
} }
} }

View File

@ -152,7 +152,8 @@ class Home extends HookConsumerWidget {
return const Login(); return const Login();
} }
return Scaffold( return SafeArea(
child: Scaffold(
body: Column( body: Column(
children: [ children: [
WindowTitleBarBox( WindowTitleBarBox(
@ -165,8 +166,9 @@ class Home extends HookConsumerWidget {
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: titleBarDragMaxWidth.toDouble(), maxWidth: titleBarDragMaxWidth.toDouble(),
), ),
color: color: Theme.of(context)
Theme.of(context).navigationRailTheme.backgroundColor, .navigationRailTheme
.backgroundColor,
child: MoveWindow(), child: MoveWindow(),
), ),
Expanded(child: MoveWindow()), Expanded(child: MoveWindow()),
@ -212,6 +214,7 @@ class Home extends HookConsumerWidget {
), ),
], ],
), ),
),
); );
} }
} }

View File

@ -42,7 +42,8 @@ class PlayerView extends HookConsumerWidget {
[currentTrack?.album?.images], [currentTrack?.album?.images],
); );
return Scaffold( return SafeArea(
child: Scaffold(
appBar: const PageWindowTitleBar( appBar: const PageWindowTitleBar(
leading: BackButton(), leading: BackButton(),
), ),
@ -94,6 +95,7 @@ class PlayerView extends HookConsumerWidget {
PlayerControls(iconColor: paletteColor.bodyTextColor), PlayerControls(iconColor: paletteColor.bodyTextColor),
], ],
), ),
),
); );
} }
} }

View File

@ -38,7 +38,8 @@ class PlaylistView extends ConsumerWidget {
SpotifyApi spotifyApi = ref.watch(spotifyProvider); SpotifyApi spotifyApi = ref.watch(spotifyProvider);
var isPlaylistPlaying = playback.currentPlaylist?.id != null && var isPlaylistPlaying = playback.currentPlaylist?.id != null &&
playback.currentPlaylist?.id == playlist.id; playback.currentPlaylist?.id == playlist.id;
return Scaffold( return SafeArea(
child: Scaffold(
body: FutureBuilder<Iterable<Track>>( body: FutureBuilder<Iterable<Track>>(
future: playlist.id != "user-liked-tracks" future: playlist.id != "user-liked-tracks"
? spotifyApi.playlists.getTracksByPlaylistId(playlist.id).all() ? spotifyApi.playlists.getTracksByPlaylistId(playlist.id).all()
@ -96,6 +97,7 @@ class PlaylistView extends ConsumerWidget {
], ],
); );
}), }),
),
); );
} }
} }

View File

@ -25,7 +25,8 @@ class Settings extends HookConsumerWidget {
geniusAccessToken.value = textEditingController.value.text; geniusAccessToken.value = textEditingController.value.text;
}); });
return Scaffold( return SafeArea(
child: Scaffold(
appBar: PageWindowTitleBar( appBar: PageWindowTitleBar(
leading: const BackButton(), leading: const BackButton(),
center: Text( center: Text(
@ -62,8 +63,8 @@ class Settings extends HookConsumerWidget {
? () async { ? () async {
SharedPreferences localStorage = SharedPreferences localStorage =
await SharedPreferences.getInstance(); await SharedPreferences.getInstance();
preferences preferences.setGeniusAccessToken(
.setGeniusAccessToken(geniusAccessToken.value); geniusAccessToken.value);
localStorage.setString( localStorage.setString(
LocalStorageKeys.geniusAccessToken, LocalStorageKeys.geniusAccessToken,
geniusAccessToken.value ?? ""); geniusAccessToken.value ?? "");
@ -193,6 +194,7 @@ class Settings extends HookConsumerWidget {
], ],
), ),
), ),
),
); );
} }
} }

View File

@ -52,10 +52,23 @@ class PageWindowTitleBar extends StatelessWidget
const PageWindowTitleBar({Key? key, this.leading, this.center}) const PageWindowTitleBar({Key? key, this.leading, this.center})
: super(key: key); : super(key: key);
@override @override
Size get preferredSize => Size.fromHeight(appWindow.titleBarHeight); Size get preferredSize => Size.fromHeight(
!Platform.isIOS && !Platform.isAndroid ? appWindow.titleBarHeight : 0,
);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (Platform.isIOS || Platform.isAndroid) {
return PreferredSize(
preferredSize: const Size.fromHeight(70),
child: Row(
children: [
if (leading != null) leading!,
Expanded(child: Center(child: center)),
],
),
);
}
return WindowTitleBarBox( return WindowTitleBarBox(
child: Row( child: Row(
children: [ children: [

View File

@ -28,7 +28,6 @@ Future<void> Function() usePreviousTrack(Playback playback) {
Future<void> Function([dynamic]) useTogglePlayPause(Playback playback) { Future<void> Function([dynamic]) useTogglePlayPause(Playback playback) {
return ([key]) async { return ([key]) async {
print("CLICK CLICK");
try { try {
if (playback.currentTrack == null) return; if (playback.currentTrack == null) return;
playback.isPlaying playback.isPlaying

View File

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hotkey_manager/hotkey_manager.dart'; import 'package:hotkey_manager/hotkey_manager.dart';
@ -18,6 +20,7 @@ useHotKeys(WidgetRef ref) {
final _playOrPause = useTogglePlayPause(playback); final _playOrPause = useTogglePlayPause(playback);
useEffect(() { useEffect(() {
if (Platform.isIOS || Platform.isAndroid) return null;
_hotKeys = [ _hotKeys = [
GlobalKeyActions( GlobalKeyActions(
HotKey(KeyCode.space, scope: HotKeyScope.inapp), HotKey(KeyCode.space, scope: HotKeyScope.inapp),

View File

@ -14,9 +14,9 @@ import 'package:spotube/provider/ThemeProvider.dart';
import 'package:spotube/provider/YouTube.dart'; import 'package:spotube/provider/YouTube.dart';
void main() async { void main() async {
if (!Platform.isAndroid && !Platform.isIOS) {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await hotKeyManager.unregisterAll(); await hotKeyManager.unregisterAll();
runApp(ProviderScope(child: MyApp()));
doWhenWindowReady(() { doWhenWindowReady(() {
appWindow.minSize = appWindow.minSize =
Size(Platform.isAndroid || Platform.isIOS ? 280 : 359, 700); Size(Platform.isAndroid || Platform.isIOS ? 280 : 359, 700);
@ -25,10 +25,14 @@ void main() async {
appWindow.maximize(); appWindow.maximize();
appWindow.show(); appWindow.show();
}); });
}
runApp(ProviderScope(child: MyApp()));
} }
class MyApp extends HookConsumerWidget { class MyApp extends HookConsumerWidget {
final GoRouter _router = createGoRouter(); final GoRouter _router = createGoRouter();
MyApp({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
var themeMode = ref.watch(themeProvider); var themeMode = ref.watch(themeProvider);

View File

@ -5,7 +5,6 @@ import 'package:spotube/components/Album/AlbumView.dart';
import 'package:spotube/components/Artist/ArtistAlbumView.dart'; import 'package:spotube/components/Artist/ArtistAlbumView.dart';
import 'package:spotube/components/Artist/ArtistProfile.dart'; import 'package:spotube/components/Artist/ArtistProfile.dart';
import 'package:spotube/components/Home/Home.dart'; import 'package:spotube/components/Home/Home.dart';
import 'package:spotube/components/Player/PlayerControls.dart';
import 'package:spotube/components/Player/PlayerView.dart'; import 'package:spotube/components/Player/PlayerView.dart';
import 'package:spotube/components/Playlist/PlaylistView.dart'; import 'package:spotube/components/Playlist/PlaylistView.dart';
import 'package:spotube/components/Settings.dart'; import 'package:spotube/components/Settings.dart';

View File

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:audio_session/audio_session.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:just_audio/just_audio.dart'; import 'package:just_audio/just_audio.dart';
@ -60,9 +61,11 @@ class Playback extends ChangeNotifier {
StreamSubscription<bool>? _playingStreamListener; StreamSubscription<bool>? _playingStreamListener;
StreamSubscription<Duration?>? _durationStreamListener; StreamSubscription<Duration?>? _durationStreamListener;
StreamSubscription<ProcessingState>? _processingStateStreamListener; StreamSubscription<ProcessingState>? _processingStateStreamListener;
StreamSubscription<AudioInterruptionEvent>? _audioInterruptionEventListener;
AudioPlayer player; AudioPlayer player;
YoutubeExplode youtube; YoutubeExplode youtube;
AudioSession? _audioSession;
Playback({ Playback({
required this.player, required this.player,
required this.youtube, required this.youtube,
@ -107,6 +110,14 @@ class Playback extends ChangeNotifier {
print(stack); print(stack);
} }
}); });
AudioSession.instance.then((session) async {
_audioSession = session;
await session.configure(const AudioSessionConfiguration.music());
_audioInterruptionEventListener = session.interruptionEventStream.listen(
(AudioInterruptionEvent event) {},
);
});
} }
CurrentPlaylist? get currentPlaylist => _currentPlaylist; CurrentPlaylist? get currentPlaylist => _currentPlaylist;
@ -175,6 +186,8 @@ class Playback extends ChangeNotifier {
_processingStateStreamListener?.cancel(); _processingStateStreamListener?.cancel();
_durationStreamListener?.cancel(); _durationStreamListener?.cancel();
_playingStreamListener?.cancel(); _playingStreamListener?.cancel();
_audioInterruptionEventListener?.cancel();
_audioSession?.setActive(false);
super.dispose(); super.dispose();
} }
@ -206,7 +219,7 @@ class Playback extends ChangeNotifier {
// the track is already playing so no need to change that // the track is already playing so no need to change that
if (track != null && track.id == _currentTrack?.id) return; if (track != null && track.id == _currentTrack?.id) return;
track ??= _currentTrack; track ??= _currentTrack;
if (track != null) { if (track != null && await _audioSession?.setActive(true) == true) {
Uri? parsedUri = Uri.tryParse(track.uri ?? ""); Uri? parsedUri = Uri.tryParse(track.uri ?? "");
if (parsedUri != null && parsedUri.hasAbsolutePath) { if (parsedUri != null && parsedUri.hasAbsolutePath) {
await player await player

View File

@ -30,7 +30,7 @@ packages:
source: hosted source: hosted
version: "2.8.2" version: "2.8.2"
audio_session: audio_session:
dependency: transitive dependency: "direct main"
description: description:
name: audio_session name: audio_session
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
@ -323,7 +323,7 @@ packages:
name: just_audio_web name: just_audio_web
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.4" version: "0.4.7"
libwinmedia: libwinmedia:
dependency: transitive dependency: transitive
description: description:

View File

@ -54,6 +54,7 @@ dependencies:
hooks_riverpod: ^1.0.3 hooks_riverpod: ^1.0.3
go_router: ^3.0.4 go_router: ^3.0.4
palette_generator: ^0.3.3 palette_generator: ^0.3.3
audio_session: ^0.1.6+1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: