mirror of
https://github.com/thelinkin3000/SonicLair.git
synced 2026-05-19 14:18:23 -04:00
Fixed a package.json error
This commit is contained in:
@@ -11,6 +11,16 @@
|
||||
android:theme="@style/AppTheme"
|
||||
android:usesCleartextTraffic="true">
|
||||
|
||||
<!--Used by Android Auto-->
|
||||
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
|
||||
android:resource="@drawable/ic_stat_soniclair" />
|
||||
|
||||
<service android:name=".MediaBrowser"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.media.browse.MediaBrowserService"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
<activity
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
|
||||
android:name="tech.logica10.soniclair.MainActivity"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package tech.logica10.soniclair;
|
||||
|
||||
import android.media.browse.MediaBrowser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -9,14 +11,16 @@ public class Globals {
|
||||
private static Globals single_instance = null;
|
||||
|
||||
// Declaring a variable of type String
|
||||
private List<BroadcastObserver> observers;
|
||||
private List<IBroadcastObserver> observers;
|
||||
private ArrayList<MediaBrowser.MediaItem> mediaItems;
|
||||
|
||||
// Constructor
|
||||
// Here we will be creating private constructor
|
||||
// restricted to this class itself
|
||||
private Globals()
|
||||
{
|
||||
observers = new ArrayList<BroadcastObserver>();
|
||||
mediaItems = new ArrayList<MediaBrowser.MediaItem>();
|
||||
observers = new ArrayList<IBroadcastObserver>();
|
||||
}
|
||||
|
||||
// Static method
|
||||
@@ -29,13 +33,21 @@ public class Globals {
|
||||
return single_instance;
|
||||
}
|
||||
|
||||
public static void RegisterObserver(BroadcastObserver observer){
|
||||
public static void RegisterObserver(IBroadcastObserver observer){
|
||||
getInstance().observers.add(observer);
|
||||
}
|
||||
|
||||
public static void NotifyObservers(String action){
|
||||
for (BroadcastObserver observer : getInstance().observers) {
|
||||
for (IBroadcastObserver observer : getInstance().observers) {
|
||||
observer.update(action);
|
||||
}
|
||||
}
|
||||
|
||||
public static ArrayList<MediaBrowser.MediaItem> GetMediaItems(){
|
||||
return getInstance().mediaItems;
|
||||
}
|
||||
|
||||
public static void SetMediaItems(ArrayList<MediaBrowser.MediaItem> mediaItemArray) {
|
||||
getInstance().mediaItems = mediaItemArray;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package tech.logica10.soniclair;
|
||||
|
||||
public interface BroadcastObserver {
|
||||
public interface IBroadcastObserver {
|
||||
public void update(String action);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package tech.logica10.soniclair;
|
||||
|
||||
import android.media.browse.MediaBrowser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public interface IMediaItemSource {
|
||||
public ArrayList<MediaBrowser.MediaItem> getItems();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package tech.logica10.soniclair;
|
||||
|
||||
import android.media.session.MediaSession;
|
||||
import android.media.session.PlaybackState;
|
||||
import android.os.Bundle;
|
||||
import android.service.media.MediaBrowserService;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MediaBrowser extends MediaBrowserService {
|
||||
private static final String MY_MEDIA_ROOT_ID = "media_root_id";
|
||||
private static final String MY_EMPTY_MEDIA_ROOT_ID = "empty_root_id";
|
||||
|
||||
private MediaSession mediaSession;
|
||||
private PlaybackState.Builder stateBuilder;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
// Create a MediaSessionCompat
|
||||
mediaSession = new MediaSession(MainActivity.context, "SONICLAIR");
|
||||
|
||||
// // Set an initial PlaybackState with ACTION_PLAY, so media buttons can start the player
|
||||
// stateBuilder = new PlaybackState.Builder()
|
||||
// .setActions(
|
||||
// PlaybackStateCompat.ACTION_PLAY |
|
||||
// PlaybackStateCompat.ACTION_PLAY_PAUSE);
|
||||
mediaSession.setPlaybackState(stateBuilder.build());
|
||||
|
||||
// MySessionCallback() has methods that handle callbacks from a media controller
|
||||
mediaSession.setCallback(new SonicLairSessionCallbacks());
|
||||
|
||||
// Set the session's token so that client activities can communicate with it.
|
||||
setSessionToken(mediaSession.getSessionToken());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) {
|
||||
return new BrowserRoot("sonicLairRoot",null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadChildren(@NonNull String parentMediaId, @NonNull Result<List<android.media.browse.MediaBrowser.MediaItem>> result) {
|
||||
// Assume for example that the music catalog is already loaded/cached.
|
||||
List<android.media.browse.MediaBrowser.MediaItem> mediaItems = Globals.GetMediaItems();
|
||||
result.sendResult(mediaItems);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package tech.logica10.soniclair;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.MediaDescription;
|
||||
import android.media.browse.MediaBrowser;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.FutureTarget;
|
||||
import com.getcapacitor.JSArray;
|
||||
import com.getcapacitor.Plugin;
|
||||
import com.getcapacitor.PluginCall;
|
||||
import com.getcapacitor.PluginMethod;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class MediaBrowserPlugin extends Plugin {
|
||||
ArrayList<MediaBrowser.MediaItem> mediaItems;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
mediaItems = new ArrayList<>();
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void loadItems(PluginCall call) throws JSONException, ExecutionException, InterruptedException {
|
||||
JSArray array = call.getArray("items");
|
||||
List<MediaItemJson> itemArray = array.toList();
|
||||
ArrayList<MediaBrowser.MediaItem> mediaItemArray = new ArrayList<MediaBrowser.MediaItem>();
|
||||
MediaDescription.Builder builder = new MediaDescription.Builder();
|
||||
for(MediaItemJson item: itemArray){
|
||||
builder.setTitle(item.song);
|
||||
builder.setSubtitle(String.format("by %s", item.artist));
|
||||
Uri albumArtUri = Uri.parse(item.albumArt);
|
||||
FutureTarget<Bitmap> futureBitmap = Glide.with(MainActivity.context)
|
||||
.asBitmap()
|
||||
.load(albumArtUri)
|
||||
.submit();
|
||||
Bitmap albumArtBitmap = futureBitmap.get();
|
||||
builder.setIconBitmap(albumArtBitmap);
|
||||
builder.setMediaId(item.id);
|
||||
mediaItemArray.add(new MediaBrowser.MediaItem(builder.build(), MediaBrowser.MediaItem.FLAG_PLAYABLE));
|
||||
}
|
||||
Globals.SetMediaItems(mediaItemArray);
|
||||
}
|
||||
}
|
||||
|
||||
class MediaItemJson{
|
||||
public String artist;
|
||||
public String album;
|
||||
public String albumArt;
|
||||
public String song;
|
||||
public float duration;
|
||||
public String id;
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import android.media.MediaMetadata;
|
||||
import android.media.session.MediaSession;
|
||||
import android.media.session.PlaybackState;
|
||||
import android.net.Uri;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.FutureTarget;
|
||||
@@ -26,7 +25,7 @@ import java.util.Locale;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
@CapacitorPlugin(name = "MediaSession")
|
||||
public class MediaSessionPlugin extends Plugin implements BroadcastObserver {
|
||||
public class MediaSessionPlugin extends Plugin implements IBroadcastObserver {
|
||||
|
||||
private MediaSession mediaSession;
|
||||
private Notification.Style mediaStyle;
|
||||
@@ -105,6 +104,7 @@ public class MediaSessionPlugin extends Plugin implements BroadcastObserver {
|
||||
call.resolve();
|
||||
}
|
||||
|
||||
@PluginMethod()
|
||||
public void pause(PluginCall call) {
|
||||
stateBuilder.setState(PlaybackState.STATE_PAUSED, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1);
|
||||
mediaSession.setPlaybackState(stateBuilder.build());
|
||||
@@ -127,7 +127,7 @@ public class MediaSessionPlugin extends Plugin implements BroadcastObserver {
|
||||
metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, call.getString("artist"));
|
||||
metadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, call.getString("song"));
|
||||
mediaSession.setMetadata(metadataBuilder.build());
|
||||
notificationBuilder.setSmallIcon(R.mipmap.ic_launcher);
|
||||
notificationBuilder.setSmallIcon(R.drawable.ic_stat_soniclair);
|
||||
notificationBuilder.setLargeIcon(albumArtBitmap);
|
||||
notificationBuilder.setContentTitle(call.getString("song"));
|
||||
notificationBuilder.setContentText(call.getString("album"));
|
||||
@@ -146,48 +146,7 @@ public class MediaSessionPlugin extends Plugin implements BroadcastObserver {
|
||||
}
|
||||
|
||||
|
||||
private class SonicLairSessionCallbacks extends MediaSession.Callback {
|
||||
@Override
|
||||
public void onPlay() {
|
||||
super.onPlay();
|
||||
Globals.NotifyObservers("SLPAUSE");
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
Globals.NotifyObservers("SLPAUSE");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSkipToNext() {
|
||||
super.onSkipToNext();
|
||||
Globals.NotifyObservers("SLNEXT");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSkipToPrevious() {
|
||||
super.onSkipToPrevious();
|
||||
Globals.NotifyObservers("SLPREV");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMediaButtonEvent(Intent mediaButtonIntent){
|
||||
KeyEvent ke = mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
|
||||
if (ke != null && ke.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
int keyCode = ke.getKeyCode();
|
||||
if(keyCode == KeyEvent.KEYCODE_MEDIA_PLAY || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE){
|
||||
Globals.NotifyObservers("SLPAUSE");
|
||||
}
|
||||
else if(keyCode == KeyEvent.KEYCODE_MEDIA_NEXT){
|
||||
Globals.NotifyObservers("SLNEXT");
|
||||
}
|
||||
else if(keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS){
|
||||
Globals.NotifyObservers("SLPREV");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package tech.logica10.soniclair;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.media.session.MediaSession;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
public class SonicLairSessionCallbacks extends MediaSession.Callback {
|
||||
@Override
|
||||
public void onPlay() {
|
||||
super.onPlay();
|
||||
Globals.NotifyObservers("SLPAUSE");
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
Globals.NotifyObservers("SLPAUSE");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSkipToNext() {
|
||||
super.onSkipToNext();
|
||||
Globals.NotifyObservers("SLNEXT");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSkipToPrevious() {
|
||||
super.onSkipToPrevious();
|
||||
Globals.NotifyObservers("SLPREV");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMediaButtonEvent(Intent mediaButtonIntent){
|
||||
KeyEvent ke = mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
|
||||
if (ke != null && ke.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
int keyCode = ke.getKeyCode();
|
||||
if(keyCode == KeyEvent.KEYCODE_MEDIA_PLAY || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE){
|
||||
Globals.NotifyObservers("SLPAUSE");
|
||||
}
|
||||
else if(keyCode == KeyEvent.KEYCODE_MEDIA_NEXT){
|
||||
Globals.NotifyObservers("SLNEXT");
|
||||
}
|
||||
else if(keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS){
|
||||
Globals.NotifyObservers("SLPREV");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
BIN
android/app/src/main/res/drawable-hdpi/ic_stat_soniclair.png
Normal file
BIN
android/app/src/main/res/drawable-hdpi/ic_stat_soniclair.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 675 B |
BIN
android/app/src/main/res/drawable-mdpi/ic_stat_soniclair.png
Normal file
BIN
android/app/src/main/res/drawable-mdpi/ic_stat_soniclair.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 472 B |
BIN
android/app/src/main/res/drawable-xhdpi/ic_stat_soniclair.png
Normal file
BIN
android/app/src/main/res/drawable-xhdpi/ic_stat_soniclair.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 982 B |
BIN
android/app/src/main/res/drawable-xxhdpi/ic_stat_soniclair.png
Normal file
BIN
android/app/src/main/res/drawable-xxhdpi/ic_stat_soniclair.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
android/app/src/main/res/drawable-xxxhdpi/ic_stat_soniclair.png
Normal file
BIN
android/app/src/main/res/drawable-xxxhdpi/ic_stat_soniclair.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
2
build-pwa.ps1
Normal file
2
build-pwa.ps1
Normal file
@@ -0,0 +1,2 @@
|
||||
npm run build
|
||||
Copy-Item -Path "build/*" -Destination "../SonicLair-PWA/" -Recurse -Force
|
||||
10
package-lock.json
generated
10
package-lock.json
generated
@@ -1231,11 +1231,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
|
||||
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="
|
||||
},
|
||||
"@benestudioco/react-scrollfade": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@benestudioco/react-scrollfade/-/react-scrollfade-1.0.2.tgz",
|
||||
"integrity": "sha512-mE/U+XV/IdoiSRaIfA6c+iTGxRV68FCy8w0cOKZgx3nJdYMdy7lUG7/W9ZL0gBPDyINTCUKzH0peBAwHTk3bpQ=="
|
||||
},
|
||||
"@capacitor/android": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/android/-/android-3.5.1.tgz",
|
||||
@@ -6302,11 +6297,6 @@
|
||||
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
|
||||
"integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ=="
|
||||
},
|
||||
"howler": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/howler/-/howler-2.2.3.tgz",
|
||||
"integrity": "sha512-QM0FFkw0LRX1PR8pNzJVAY25JhIWvbKMBFM4gqk+QdV+kPXOhleWGCB6AiAF/goGjIHK2e/nIElplvjQwhr0jg=="
|
||||
},
|
||||
"hpack.js": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
|
||||
|
||||
@@ -19,7 +19,6 @@ import AlbumCard from "./AlbumCard";
|
||||
import "./Artist.scss";
|
||||
import Loading from "./Loading";
|
||||
import useAutoFill from "../Hooks/useAutoFill";
|
||||
import ScrollFade from '@benestudioco/react-scrollfade';
|
||||
|
||||
|
||||
export default function Artist() {
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import logo from '../logo.svg';
|
||||
import PlayTest from './PlayTest';
|
||||
import classnames from 'classnames';
|
||||
import { motion } from "framer-motion";
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import GetTopAlbums from '../Api/GetTopAlbums';
|
||||
import { AppContext } from '../AppContext';
|
||||
import { IAlbumArtistResponse, IAlbumSongResponse, IRandomSongsResponse } from '../Models/API/Responses/IArtistResponse';
|
||||
import { IAlbumArtistResponse, IAlbumSongResponse } from '../Models/API/Responses/IArtistResponse';
|
||||
import AlbumCard from './AlbumCard';
|
||||
import GetRandomSongs from '../Api/GetRandomSongs';
|
||||
import RandomSongCard from './RandomSongCard';
|
||||
import { AutoSizer } from 'react-virtualized';
|
||||
import MediaBrowser from '../Plugins/MediaBrowser';
|
||||
|
||||
export default function Home() {
|
||||
const navigate = useNavigate();
|
||||
const [albumsFetched, setAlbumsFetched] = useState<boolean>(false);
|
||||
const [albums, setAlbums] = useState<IAlbumArtistResponse[]>([]);
|
||||
const [songsFetched, setSongsFetched] = useState<boolean>(false);
|
||||
const [songs, setSongs] = useState<IAlbumSongResponse[]>([]);
|
||||
const { context } = useContext(AppContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (albumsFetched)
|
||||
return;
|
||||
@@ -38,9 +34,18 @@ export default function Home() {
|
||||
const randomSongs = await GetRandomSongs(context);
|
||||
setSongs(randomSongs.randomSongs.song);
|
||||
setSongsFetched(true);
|
||||
MediaBrowser.loadItems(randomSongs.randomSongs.song.map(s => {
|
||||
return {
|
||||
album: s.album,
|
||||
artist: s.artist,
|
||||
song: s.title,
|
||||
duration: s.duration,
|
||||
albumArt: "",
|
||||
id: s.id
|
||||
}
|
||||
}));
|
||||
};
|
||||
fetch();
|
||||
|
||||
}, [songsFetched, context]);
|
||||
|
||||
return (
|
||||
@@ -53,7 +58,7 @@ export default function Home() {
|
||||
<div className="col-12 overflow-scroll scrollable scrollable-hidden" style={{ height: "auto" }}>
|
||||
<div className="d-flex flex-row">
|
||||
{albums.map(s => <div style={{ margin: "10px" }}>
|
||||
<AlbumCard item={s} forceWidth={true}/>
|
||||
<AlbumCard item={s} forceWidth={true} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
8
src/Plugins/MediaBrowser.ts
Normal file
8
src/Plugins/MediaBrowser.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { registerPlugin } from '@capacitor/core';
|
||||
|
||||
export interface MediaBrowserPlugin {
|
||||
loadItems(items: {albumArt:string, album: string, artist: string, duration: number, song: string, id:string}[]): Promise<{ status: string }>
|
||||
}
|
||||
const MediaBrowser = registerPlugin<MediaBrowserPlugin>('MediaBrowser');
|
||||
|
||||
export default MediaBrowser;
|
||||
Reference in New Issue
Block a user