Fixed a package.json error

This commit is contained in:
Carlos Pérez
2022-05-26 20:10:50 -03:00
parent 619efb1d6a
commit 122d2da700
18 changed files with 226 additions and 70 deletions

View File

@@ -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"

View File

@@ -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;
}
}

View File

@@ -1,5 +1,5 @@
package tech.logica10.soniclair;
public interface BroadcastObserver {
public interface IBroadcastObserver {
public void update(String action);
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

2
build-pwa.ps1 Normal file
View File

@@ -0,0 +1,2 @@
npm run build
Copy-Item -Path "build/*" -Destination "../SonicLair-PWA/" -Recurse -Force

10
package-lock.json generated
View File

@@ -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",

View File

@@ -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() {

View File

@@ -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>
)}

View 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;