Anonymous
Класс Activity, объявленный в вашем AndroidManifest.xml, неверен или не предоставил правильный FlutterEngine.
Сообщение
Anonymous » 27 дек 2024, 12:36
мне нужно работать со звуком в фоновом режиме
это код
файл audio_handler
Код: Выделить всё
import 'package:audio_service/audio_service.dart';
import 'package:just_audio/just_audio.dart';
Future initAudioService() async {
return await AudioService.init(
builder: () => MyAudioHandler(),
config: const AudioServiceConfig(
androidNotificationChannelId: 'com.mycompany.myapp.audio',
androidNotificationChannelName: 'Audio Service Demo',
androidNotificationOngoing: true,
androidStopForegroundOnPause: true,
),
);
}
class MyAudioHandler extends BaseAudioHandler {
final _player = AudioPlayer();
final _playlist = ConcatenatingAudioSource(children: []);
MyAudioHandler() {
_loadEmptyPlaylist();
_notifyAudioHandlerAboutPlaybackEvents();
_listenForDurationChanges();
_listenForCurrentSongIndexChanges();
_listenForSequenceStateChanges();
}
Future _loadEmptyPlaylist() async {
try {
await _player.setAudioSource(_playlist);
} catch (e) {
print("Error: $e");
}
}
void _notifyAudioHandlerAboutPlaybackEvents() {
_player.playbackEventStream.listen((PlaybackEvent event) {
final playing = _player.playing;
playbackState.add(playbackState.value.copyWith(
controls: [
MediaControl.skipToPrevious,
if (playing) MediaControl.pause else MediaControl.play,
MediaControl.stop,
MediaControl.skipToNext,
],
systemActions: const {
MediaAction.seek,
},
androidCompactActionIndices: const [0, 1, 3],
processingState: const {
ProcessingState.idle: AudioProcessingState.idle,
ProcessingState.loading: AudioProcessingState.loading,
ProcessingState.buffering: AudioProcessingState.buffering,
ProcessingState.ready: AudioProcessingState.ready,
ProcessingState.completed: AudioProcessingState.completed,
}[_player.processingState]!,
repeatMode: const {
LoopMode.off: AudioServiceRepeatMode.none,
LoopMode.one: AudioServiceRepeatMode.one,
LoopMode.all: AudioServiceRepeatMode.all,
}[_player.loopMode]!,
shuffleMode: (_player.shuffleModeEnabled)
? AudioServiceShuffleMode.all
: AudioServiceShuffleMode.none,
playing: playing,
updatePosition: _player.position,
bufferedPosition: _player.bufferedPosition,
speed: _player.speed,
queueIndex: event.currentIndex,
));
});
}
void _listenForDurationChanges() {
_player.durationStream.listen((duration) {
var index = _player.currentIndex;
final newQueue = queue.value;
if (index == null || newQueue.isEmpty) return;
if (_player.shuffleModeEnabled) {
index = _player.shuffleIndices!.indexOf(index);
}
final oldMediaItem = newQueue[index];
final newMediaItem = oldMediaItem.copyWith(duration: duration);
newQueue[index] = newMediaItem;
queue.add(newQueue);
mediaItem.add(newMediaItem);
});
}
void _listenForCurrentSongIndexChanges() {
_player.currentIndexStream.listen((index) {
final playlist = queue.value;
if (index == null || playlist.isEmpty) return;
if (_player.shuffleModeEnabled) {
index = _player.shuffleIndices!.indexOf(index);
}
mediaItem.add(playlist[index]);
});
}
void _listenForSequenceStateChanges() {
_player.sequenceStateStream.listen((SequenceState? sequenceState) {
final sequence = sequenceState?.effectiveSequence;
if (sequence == null || sequence.isEmpty) return;
final items = sequence.map((source) => source.tag as MediaItem);
queue.add(items.toList());
});
}
@override
Future addQueueItems(List mediaItems) async {
// manage Just Audio
final audioSource = mediaItems.map(_createAudioSource);
_playlist.addAll(audioSource.toList());
// notify system
final newQueue = queue.value..addAll(mediaItems);
queue.add(newQueue);
}
@override
Future addQueueItem(MediaItem mediaItem) async {
// manage Just Audio
final audioSource = _createAudioSource(mediaItem);
_playlist.add(audioSource);
// notify system
final newQueue = queue.value..add(mediaItem);
queue.add(newQueue);
}
UriAudioSource _createAudioSource(MediaItem mediaItem) {
return AudioSource.uri(
Uri.parse(mediaItem.extras!['url'] as String),
tag: mediaItem,
);
}
@override
Future removeQueueItemAt(int index) async {
// manage Just Audio
_playlist.removeAt(index);
// notify system
final newQueue = queue.value..removeAt(index);
queue.add(newQueue);
}
@override
Future play() => _player.play();
@override
Future pause() => _player.pause();
@override
Future seek(Duration position) => _player.seek(position);
@override
Future skipToQueueItem(int index) async {
if (index < 0 || index >= queue.value.length) return;
if (_player.shuffleModeEnabled) {
index = _player.shuffleIndices![index];
}
_player.seek(Duration.zero, index: index);
}
@override
Future skipToNext() => _player.seekToNext();
@override
Future skipToPrevious() => _player.seekToPrevious();
@override
Future setRepeatMode(AudioServiceRepeatMode repeatMode) async {
switch (repeatMode) {
case AudioServiceRepeatMode.none:
_player.setLoopMode(LoopMode.off);
break;
case AudioServiceRepeatMode.one:
_player.setLoopMode(LoopMode.one);
break;
case AudioServiceRepeatMode.group:
case AudioServiceRepeatMode.all:
_player.setLoopMode(LoopMode.all);
break;
}
}
@override
Future setShuffleMode(AudioServiceShuffleMode shuffleMode) async {
if (shuffleMode == AudioServiceShuffleMode.none) {
_player.setShuffleModeEnabled(false);
} else {
await _player.shuffle();
_player.setShuffleModeEnabled(true);
}
}
@override
Future customAction(String name, [Map? extras]) async {
if (name == 'dispose') {
await _player.dispose();
super.stop();
}
}
@override
Future stop() async {
await _player.stop();
return super.stop();
}
}
Файл Service_Locator
Код: Выделить всё
import 'package:audio_service/audio_service.dart';
import '../page_manager.dart';
import 'audio_handler.dart';
import 'playlist_repository.dart';
import 'package:get_it/get_it.dart';
GetIt getIt = GetIt.instance;
Future setupServiceLocator() async {
// services
getIt.registerSingleton(await initAudioService());
getIt.registerLazySingleton
(() => DemoPlaylist());
// page state
getIt.registerLazySingleton(() => PageManager());
}
аудиофайл вызова
Код: Выделить всё
class Audio_Calling extends StatefulWidget {
const Audio_Calling({super.key});
@override
State createState() => _Audio_CallingState();
}
class _Audio_CallingState extends State {
@override
void initState() {
super.initState();
getIt().init();
}
@override
void dispose() {
getIt().dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: const [
CurrentSongTitle(),
Playlist(),
AddRemoveSongButtons(),
AudioProgressBar(),
AudioControlButtons(),
],
),
),
),
);
}
}
файл менеджера страниц
Код: Выделить всё
class PageManager {
// Listeners: Updates going to the UI
final currentSongTitleNotifier = ValueNotifier('');
final playlistNotifier = ValueNotifier([]);
final progressNotifier = ProgressNotifier();
final repeatButtonNotifier = RepeatButtonNotifier();
final isFirstSongNotifier = ValueNotifier(true);
final playButtonNotifier = PlayButtonNotifier();
final isLastSongNotifier = ValueNotifier(true);
final isShuffleModeEnabledNotifier = ValueNotifier(false);
final _audioHandler = getIt();
// Events: Calls coming from the UI
void init() async {
await _loadPlaylist();
_listenToChangesInPlaylist();
_listenToPlaybackState();
_listenToCurrentPosition();
_listenToBufferedPosition();
_listenToTotalDuration();
_listenToChangesInSong();
}
Future _loadPlaylist() async {
final songRepository = getIt();
final playlist = await songRepository.fetchInitialPlaylist();
final mediaItems = playlist
.map((song) => MediaItem(
id: song['id'] ?? '',
album: song['album'] ?? '',
title: song['title'] ?? '',
extras: {'url': song['url']},
))
.toList();
_audioHandler.addQueueItems(mediaItems);
}
void _listenToChangesInPlaylist() {
_audioHandler.queue.listen((playlist) {
if (playlist.isEmpty) {
playlistNotifier.value = [];
currentSongTitleNotifier.value = '';
} else {
final newList = playlist.map((item) => item.title).toList();
playlistNotifier.value = newList;
}
_updateSkipButtons();
});
}
void _listenToPlaybackState() {
_audioHandler.playbackState.listen((playbackState) {
final isPlaying = playbackState.playing;
final processingState = playbackState.processingState;
if (processingState == AudioProcessingState.loading ||
processingState == AudioProcessingState.buffering) {
playButtonNotifier.value = ButtonState.loading;
} else if (!isPlaying) {
playButtonNotifier.value = ButtonState.paused;
} else if (processingState != AudioProcessingState.completed) {
playButtonNotifier.value = ButtonState.playing;
} else {
_audioHandler.seek(Duration.zero);
_audioHandler.pause();
}
});
}
void _listenToCurrentPosition() {
AudioService.position.listen((position) {
final oldState = progressNotifier.value;
progressNotifier.value = ProgressBarState(
current: position,
buffered: oldState.buffered,
total: oldState.total,
);
});
}
void _listenToBufferedPosition() {
_audioHandler.playbackState.listen((playbackState) {
final oldState = progressNotifier.value;
progressNotifier.value = ProgressBarState(
current: oldState.current,
buffered: playbackState.bufferedPosition,
total: oldState.total,
);
});
}
void _listenToTotalDuration() {
_audioHandler.mediaItem.listen((mediaItem) {
final oldState = progressNotifier.value;
progressNotifier.value = ProgressBarState(
current: oldState.current,
buffered: oldState.buffered,
total: mediaItem?.duration ?? Duration.zero,
);
});
}
void _listenToChangesInSong() {
_audioHandler.mediaItem.listen((mediaItem) {
currentSongTitleNotifier.value = mediaItem?.title ?? '';
_updateSkipButtons();
});
}
void _updateSkipButtons() {
final mediaItem = _audioHandler.mediaItem.value;
final playlist = _audioHandler.queue.value;
if (playlist.length < 2 || mediaItem == null) {
isFirstSongNotifier.value = true;
isLastSongNotifier.value = true;
} else {
isFirstSongNotifier.value = playlist.first == mediaItem;
isLastSongNotifier.value = playlist.last == mediaItem;
}
}
void play() => _audioHandler.play();
void pause() => _audioHandler.pause();
void seek(Duration position) => _audioHandler.seek(position);
void previous() => _audioHandler.skipToPrevious();
void next() => _audioHandler.skipToNext();
void repeat() {
repeatButtonNotifier.nextState();
final repeatMode = repeatButtonNotifier.value;
switch (repeatMode) {
case RepeatState.off:
_audioHandler.setRepeatMode(AudioServiceRepeatMode.none);
break;
case RepeatState.repeatSong:
_audioHandler.setRepeatMode(AudioServiceRepeatMode.one);
break;
case RepeatState.repeatPlaylist:
_audioHandler.setRepeatMode(AudioServiceRepeatMode.all);
break;
}
}
void shuffle() {
final enable = !isShuffleModeEnabledNotifier.value;
isShuffleModeEnabledNotifier.value = enable;
if (enable) {
_audioHandler.setShuffleMode(AudioServiceShuffleMode.all);
} else {
_audioHandler.setShuffleMode(AudioServiceShuffleMode.none);
}
}
Future add() async {
final songRepository = getIt();
final song = await songRepository.fetchAnotherSong();
final mediaItem = MediaItem(
id: song['id'] ?? '',
album: song['album'] ?? '',
title: song['title'] ?? '',
extras: {'url': song['url']},
);
_audioHandler.addQueueItem(mediaItem);
}
void remove() {
final lastIndex = _audioHandler.queue.value.length - 1;
if (lastIndex < 0) return;
_audioHandler.removeQueueItemAt(lastIndex);
}
void dispose() {
_audioHandler.customAction('dispose');
}
void stop() {
_audioHandler.stop();
}
}
основной
Код: Выделить всё
void main() async {
BlocOverrides.runZoned(
() async {
WidgetsFlutterBinding.ensureInitialized();
await setupServiceLocator();
runApp(Audio_Calling());
})}
когда я запускаю свой код, я получаю эту ошибку:
PlatformException (PlatformException(Класс Activity, объявленный в вашем AndroidManifest.xml, неверен или не предоставил правильный FlutterEngine. См. README для инструкций., null, null, null))
как это решить
Подробнее здесь:
https://stackoverflow.com/questions/758 ... s-not-prov
1735292195
Anonymous
мне нужно работать со звуком в фоновом режиме это код файл audio_handler [code]import 'package:audio_service/audio_service.dart'; import 'package:just_audio/just_audio.dart'; Future initAudioService() async { return await AudioService.init( builder: () => MyAudioHandler(), config: const AudioServiceConfig( androidNotificationChannelId: 'com.mycompany.myapp.audio', androidNotificationChannelName: 'Audio Service Demo', androidNotificationOngoing: true, androidStopForegroundOnPause: true, ), ); } class MyAudioHandler extends BaseAudioHandler { final _player = AudioPlayer(); final _playlist = ConcatenatingAudioSource(children: []); MyAudioHandler() { _loadEmptyPlaylist(); _notifyAudioHandlerAboutPlaybackEvents(); _listenForDurationChanges(); _listenForCurrentSongIndexChanges(); _listenForSequenceStateChanges(); } Future _loadEmptyPlaylist() async { try { await _player.setAudioSource(_playlist); } catch (e) { print("Error: $e"); } } void _notifyAudioHandlerAboutPlaybackEvents() { _player.playbackEventStream.listen((PlaybackEvent event) { final playing = _player.playing; playbackState.add(playbackState.value.copyWith( controls: [ MediaControl.skipToPrevious, if (playing) MediaControl.pause else MediaControl.play, MediaControl.stop, MediaControl.skipToNext, ], systemActions: const { MediaAction.seek, }, androidCompactActionIndices: const [0, 1, 3], processingState: const { ProcessingState.idle: AudioProcessingState.idle, ProcessingState.loading: AudioProcessingState.loading, ProcessingState.buffering: AudioProcessingState.buffering, ProcessingState.ready: AudioProcessingState.ready, ProcessingState.completed: AudioProcessingState.completed, }[_player.processingState]!, repeatMode: const { LoopMode.off: AudioServiceRepeatMode.none, LoopMode.one: AudioServiceRepeatMode.one, LoopMode.all: AudioServiceRepeatMode.all, }[_player.loopMode]!, shuffleMode: (_player.shuffleModeEnabled) ? AudioServiceShuffleMode.all : AudioServiceShuffleMode.none, playing: playing, updatePosition: _player.position, bufferedPosition: _player.bufferedPosition, speed: _player.speed, queueIndex: event.currentIndex, )); }); } void _listenForDurationChanges() { _player.durationStream.listen((duration) { var index = _player.currentIndex; final newQueue = queue.value; if (index == null || newQueue.isEmpty) return; if (_player.shuffleModeEnabled) { index = _player.shuffleIndices!.indexOf(index); } final oldMediaItem = newQueue[index]; final newMediaItem = oldMediaItem.copyWith(duration: duration); newQueue[index] = newMediaItem; queue.add(newQueue); mediaItem.add(newMediaItem); }); } void _listenForCurrentSongIndexChanges() { _player.currentIndexStream.listen((index) { final playlist = queue.value; if (index == null || playlist.isEmpty) return; if (_player.shuffleModeEnabled) { index = _player.shuffleIndices!.indexOf(index); } mediaItem.add(playlist[index]); }); } void _listenForSequenceStateChanges() { _player.sequenceStateStream.listen((SequenceState? sequenceState) { final sequence = sequenceState?.effectiveSequence; if (sequence == null || sequence.isEmpty) return; final items = sequence.map((source) => source.tag as MediaItem); queue.add(items.toList()); }); } @override Future addQueueItems(List mediaItems) async { // manage Just Audio final audioSource = mediaItems.map(_createAudioSource); _playlist.addAll(audioSource.toList()); // notify system final newQueue = queue.value..addAll(mediaItems); queue.add(newQueue); } @override Future addQueueItem(MediaItem mediaItem) async { // manage Just Audio final audioSource = _createAudioSource(mediaItem); _playlist.add(audioSource); // notify system final newQueue = queue.value..add(mediaItem); queue.add(newQueue); } UriAudioSource _createAudioSource(MediaItem mediaItem) { return AudioSource.uri( Uri.parse(mediaItem.extras!['url'] as String), tag: mediaItem, ); } @override Future removeQueueItemAt(int index) async { // manage Just Audio _playlist.removeAt(index); // notify system final newQueue = queue.value..removeAt(index); queue.add(newQueue); } @override Future play() => _player.play(); @override Future pause() => _player.pause(); @override Future seek(Duration position) => _player.seek(position); @override Future skipToQueueItem(int index) async { if (index < 0 || index >= queue.value.length) return; if (_player.shuffleModeEnabled) { index = _player.shuffleIndices![index]; } _player.seek(Duration.zero, index: index); } @override Future skipToNext() => _player.seekToNext(); @override Future skipToPrevious() => _player.seekToPrevious(); @override Future setRepeatMode(AudioServiceRepeatMode repeatMode) async { switch (repeatMode) { case AudioServiceRepeatMode.none: _player.setLoopMode(LoopMode.off); break; case AudioServiceRepeatMode.one: _player.setLoopMode(LoopMode.one); break; case AudioServiceRepeatMode.group: case AudioServiceRepeatMode.all: _player.setLoopMode(LoopMode.all); break; } } @override Future setShuffleMode(AudioServiceShuffleMode shuffleMode) async { if (shuffleMode == AudioServiceShuffleMode.none) { _player.setShuffleModeEnabled(false); } else { await _player.shuffle(); _player.setShuffleModeEnabled(true); } } @override Future customAction(String name, [Map? extras]) async { if (name == 'dispose') { await _player.dispose(); super.stop(); } } @override Future stop() async { await _player.stop(); return super.stop(); } } [/code] Файл Service_Locator [code]import 'package:audio_service/audio_service.dart'; import '../page_manager.dart'; import 'audio_handler.dart'; import 'playlist_repository.dart'; import 'package:get_it/get_it.dart'; GetIt getIt = GetIt.instance; Future setupServiceLocator() async { // services getIt.registerSingleton(await initAudioService()); getIt.registerLazySingleton (() => DemoPlaylist()); // page state getIt.registerLazySingleton(() => PageManager()); } [/code] аудиофайл вызова [code]class Audio_Calling extends StatefulWidget { const Audio_Calling({super.key}); @override State createState() => _Audio_CallingState(); } class _Audio_CallingState extends State { @override void initState() { super.initState(); getIt().init(); } @override void dispose() { getIt().dispose(); super.dispose(); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Padding( padding: const EdgeInsets.all(20.0), child: Column( children: const [ CurrentSongTitle(), Playlist(), AddRemoveSongButtons(), AudioProgressBar(), AudioControlButtons(), ], ), ), ), ); } } [/code] файл менеджера страниц [code]class PageManager { // Listeners: Updates going to the UI final currentSongTitleNotifier = ValueNotifier(''); final playlistNotifier = ValueNotifier([]); final progressNotifier = ProgressNotifier(); final repeatButtonNotifier = RepeatButtonNotifier(); final isFirstSongNotifier = ValueNotifier(true); final playButtonNotifier = PlayButtonNotifier(); final isLastSongNotifier = ValueNotifier(true); final isShuffleModeEnabledNotifier = ValueNotifier(false); final _audioHandler = getIt(); // Events: Calls coming from the UI void init() async { await _loadPlaylist(); _listenToChangesInPlaylist(); _listenToPlaybackState(); _listenToCurrentPosition(); _listenToBufferedPosition(); _listenToTotalDuration(); _listenToChangesInSong(); } Future _loadPlaylist() async { final songRepository = getIt(); final playlist = await songRepository.fetchInitialPlaylist(); final mediaItems = playlist .map((song) => MediaItem( id: song['id'] ?? '', album: song['album'] ?? '', title: song['title'] ?? '', extras: {'url': song['url']}, )) .toList(); _audioHandler.addQueueItems(mediaItems); } void _listenToChangesInPlaylist() { _audioHandler.queue.listen((playlist) { if (playlist.isEmpty) { playlistNotifier.value = []; currentSongTitleNotifier.value = ''; } else { final newList = playlist.map((item) => item.title).toList(); playlistNotifier.value = newList; } _updateSkipButtons(); }); } void _listenToPlaybackState() { _audioHandler.playbackState.listen((playbackState) { final isPlaying = playbackState.playing; final processingState = playbackState.processingState; if (processingState == AudioProcessingState.loading || processingState == AudioProcessingState.buffering) { playButtonNotifier.value = ButtonState.loading; } else if (!isPlaying) { playButtonNotifier.value = ButtonState.paused; } else if (processingState != AudioProcessingState.completed) { playButtonNotifier.value = ButtonState.playing; } else { _audioHandler.seek(Duration.zero); _audioHandler.pause(); } }); } void _listenToCurrentPosition() { AudioService.position.listen((position) { final oldState = progressNotifier.value; progressNotifier.value = ProgressBarState( current: position, buffered: oldState.buffered, total: oldState.total, ); }); } void _listenToBufferedPosition() { _audioHandler.playbackState.listen((playbackState) { final oldState = progressNotifier.value; progressNotifier.value = ProgressBarState( current: oldState.current, buffered: playbackState.bufferedPosition, total: oldState.total, ); }); } void _listenToTotalDuration() { _audioHandler.mediaItem.listen((mediaItem) { final oldState = progressNotifier.value; progressNotifier.value = ProgressBarState( current: oldState.current, buffered: oldState.buffered, total: mediaItem?.duration ?? Duration.zero, ); }); } void _listenToChangesInSong() { _audioHandler.mediaItem.listen((mediaItem) { currentSongTitleNotifier.value = mediaItem?.title ?? ''; _updateSkipButtons(); }); } void _updateSkipButtons() { final mediaItem = _audioHandler.mediaItem.value; final playlist = _audioHandler.queue.value; if (playlist.length < 2 || mediaItem == null) { isFirstSongNotifier.value = true; isLastSongNotifier.value = true; } else { isFirstSongNotifier.value = playlist.first == mediaItem; isLastSongNotifier.value = playlist.last == mediaItem; } } void play() => _audioHandler.play(); void pause() => _audioHandler.pause(); void seek(Duration position) => _audioHandler.seek(position); void previous() => _audioHandler.skipToPrevious(); void next() => _audioHandler.skipToNext(); void repeat() { repeatButtonNotifier.nextState(); final repeatMode = repeatButtonNotifier.value; switch (repeatMode) { case RepeatState.off: _audioHandler.setRepeatMode(AudioServiceRepeatMode.none); break; case RepeatState.repeatSong: _audioHandler.setRepeatMode(AudioServiceRepeatMode.one); break; case RepeatState.repeatPlaylist: _audioHandler.setRepeatMode(AudioServiceRepeatMode.all); break; } } void shuffle() { final enable = !isShuffleModeEnabledNotifier.value; isShuffleModeEnabledNotifier.value = enable; if (enable) { _audioHandler.setShuffleMode(AudioServiceShuffleMode.all); } else { _audioHandler.setShuffleMode(AudioServiceShuffleMode.none); } } Future add() async { final songRepository = getIt(); final song = await songRepository.fetchAnotherSong(); final mediaItem = MediaItem( id: song['id'] ?? '', album: song['album'] ?? '', title: song['title'] ?? '', extras: {'url': song['url']}, ); _audioHandler.addQueueItem(mediaItem); } void remove() { final lastIndex = _audioHandler.queue.value.length - 1; if (lastIndex < 0) return; _audioHandler.removeQueueItemAt(lastIndex); } void dispose() { _audioHandler.customAction('dispose'); } void stop() { _audioHandler.stop(); } } [/code] основной [code]void main() async { BlocOverrides.runZoned( () async { WidgetsFlutterBinding.ensureInitialized(); await setupServiceLocator(); runApp(Audio_Calling()); })} [/code] когда я запускаю свой код, я получаю эту ошибку: PlatformException (PlatformException(Класс Activity, объявленный в вашем AndroidManifest.xml, неверен или не предоставил правильный FlutterEngine. См. README для инструкций., null, null, null)) [img]https://i.sstatic.net/UYOTM.png[/img] как это решить Подробнее здесь: [url]https://stackoverflow.com/questions/75840543/the-activity-class-declared-in-your-androidmanifest-xml-is-wrong-or-has-not-prov[/url]