Skip to content

Commit d0d4a95

Browse files
committed
issue #67 fixes and finishing configurable sharing for server
1 parent 78448c2 commit d0d4a95

19 files changed

Lines changed: 225 additions & 128 deletions

yaacc/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ dependencies {
1515
testImplementation 'com.tngtech.junit.dataprovider:junit4-dataprovider:2.10'
1616
testImplementation 'xmlpull:xmlpull:1.1.3.1'
1717
testImplementation 'net.sf.kxml:kxml2:2.3.0'
18-
testImplementation 'org.slf4j:slf4j-android:1.7.36'
1918
implementation 'jakarta.enterprise:jakarta.enterprise.cdi-api:4.0.1'
2019
implementation 'com.google.android.material:material:1.7.0'
2120
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'

yaacc/src/main/java/de/yaacc/browser/BrowseDeviceAdapter.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
import java.net.URL;
4242
import java.util.LinkedList;
4343
import java.util.List;
44+
import java.util.Timer;
45+
import java.util.TimerTask;
4446

4547
import de.yaacc.R;
4648
import de.yaacc.upnp.UpnpClient;
@@ -141,17 +143,24 @@ static class ViewHolder extends RecyclerView.ViewHolder {
141143
ImageButton configButton;
142144

143145
Context context;
144-
146+
private Timer timer;
145147

146148
public ViewHolder(View itemView, Context context) {
147149
super(itemView);
148150
this.context = context;
151+
timer = new Timer();
149152
this.icon = itemView.findViewById(R.id.browseDeviceItemIcon);
150153
this.name = itemView.findViewById(R.id.browseDeviceItemName);
151154
this.scanButtonLabel = itemView.findViewById(R.id.browseDeviceItemMediaStoreScanLabel);
152155
this.scanButton = itemView.findViewById(R.id.browseDeviceItemRescan);
153156
scanButton.setOnClickListener((v) -> {
154-
new MediaStoreScanner().scanMediaFiles(getActivity(v.getContext()));
157+
timer.schedule(new TimerTask() {
158+
@Override
159+
public void run() {
160+
new MediaStoreScanner().scanMediaFiles(getActivity(v.getContext()));
161+
}
162+
}, 10L);
163+
155164
});
156165
this.configButton = itemView.findViewById(R.id.browseDeviceItemConfig);
157166
configButton.setOnClickListener((v) -> {

yaacc/src/main/java/de/yaacc/imageviewer/ImageViewerActivity.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ private void init(Bundle savedInstanceState, Intent intent) {
165165
}
166166
pictureShowActive = intent.getBooleanExtra(AUTO_START_SHOW, false);
167167
}
168-
if (imageUris.size() > 0) {
168+
if (imageUris != null && !imageUris.isEmpty()) {
169169
loadImage();
170170
} else {
171171
runOnUiThread(() -> {
@@ -180,12 +180,12 @@ private void init(Bundle savedInstanceState, Intent intent) {
180180

181181
@Override
182182
protected void onDestroy() {
183-
super.onDestroy();
184183
try {
185184
unbindService(this);
186185
} catch (IllegalArgumentException iae) {
187186
Log.d(getClass().getName(), "Ignore exception on unbind service while activity destroy");
188187
}
188+
super.onDestroy();
189189
}
190190

191191
/*
@@ -198,9 +198,9 @@ protected void onResume() {
198198

199199
imageViewerBroadcastReceiver = new ImageViewerBroadcastReceiver(this);
200200
imageViewerBroadcastReceiver.registerReceiver();
201-
super.onResume();
202201
this.bindService(new Intent(this, PlayerService.class),
203202
this, Context.BIND_AUTO_CREATE);
203+
super.onResume();
204204
}
205205

206206
/*

yaacc/src/main/java/de/yaacc/imageviewer/RetrieveImageTask.java

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -183,23 +183,12 @@ private InputStream getUriAsStream(Uri imageUri)
183183
private Bitmap decodeSampledBitmapFromStream(Uri imageUri, int reqWidth,
184184
int reqHeight) throws IOException {
185185
InputStream is = getUriAsStream(imageUri);
186-
int tmpWidth = reqWidth;
187-
int tmpHeight = reqHeight;
188-
if (reqHeight > 2048) {
189-
tmpHeight = 2048;
190-
tmpWidth = reqHeight * (2048 / reqWidth);
191-
}
192-
if (reqWidth > 2048) {
193-
tmpWidth = 2048;
194-
tmpHeight = reqWidth * (2048 / reqHeight);
195-
}
196-
197186
final BitmapFactory.Options options = new BitmapFactory.Options();
198187
options.inJustDecodeBounds = false;
199-
options.outHeight = tmpHeight;
200-
options.outWidth = tmpWidth;
188+
options.outHeight = reqHeight;
189+
options.outWidth = reqWidth;
201190
options.inDensity = DisplayMetrics.DENSITY_LOW;
202-
options.inTempStorage = new byte[7680016];
191+
options.inSampleSize = 2;
203192
Log.d(this.getClass().getName(),
204193
"displaying image size width, height, inSampleSize "
205194
+ options.outWidth + "," + options.outHeight + ","

yaacc/src/main/java/de/yaacc/musicplayer/BackgroundMusicService.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,21 @@ public BackgroundMusicService() {
6767
public void onCreate() {
6868
super.onCreate();
6969
Log.d(this.getClass().getName(), "On Create");
70+
71+
// Start foreground immediately with a basic notification
72+
Notification minimalNotification = new NotificationCompat.Builder(this, Yaacc.NOTIFICATION_CHANNEL_ID)
73+
.setContentTitle("Background Music Service")
74+
.setContentText("Initializing...")
75+
.setSmallIcon(R.drawable.ic_notification_default)
76+
.setGroup(Yaacc.NOTIFICATION_GROUP_KEY) // Ensure group is set for consistency
77+
.setSilent(true) // Keep it silent initially
78+
.build();
79+
startForeground(NotificationId.BACKGROUND_MUSIC_SERVICE.getId(), minimalNotification);
80+
81+
// Perform potentially long-running initializations
7082
((Yaacc) getApplicationContext()).createYaaccGroupNotification();
83+
84+
// Now create and set the final notification
7185
Intent notificationIntent = new Intent(this, TabBrowserActivity.class);
7286
PendingIntent pendingIntent = PendingIntent.getActivity(this,
7387
0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
@@ -79,6 +93,7 @@ public void onCreate() {
7993
.setContentIntent(pendingIntent)
8094
.setGroup(Yaacc.NOTIFICATION_GROUP_KEY)
8195
.build();
96+
// Update the notification by calling startForeground again
8297
startForeground(NotificationId.BACKGROUND_MUSIC_SERVICE.getId(), notification);
8398

8499
}
@@ -290,4 +305,4 @@ public BackgroundMusicService getService() {
290305
}
291306
}
292307

293-
}
308+
}

yaacc/src/main/java/de/yaacc/player/PlayableItem.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public class PlayableItem {
3939
private Uri uri;
4040
private long duration;
4141

42-
private UUID id;
42+
private final UUID id;
4343

4444

4545
public PlayableItem(Item item, int defaultDuration) {
@@ -50,7 +50,7 @@ public PlayableItem(Item item, int defaultDuration) {
5050
if (resource != null) {
5151
setUri(Uri.parse(resource.getValue()));
5252
String mimeType = resource.getProtocolInfo().getContentFormat();
53-
if (mimeType == null || mimeType.equals("")) {
53+
if (mimeType == null || mimeType.isEmpty()) {
5454
String fileExtension = MimeTypeMap.getFileExtensionFromUrl(getUri().toString());
5555
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);
5656
}
@@ -59,7 +59,7 @@ public PlayableItem(Item item, int defaultDuration) {
5959
// calculate duration
6060

6161
long millis = defaultDuration;
62-
Log.d(getClass().getName(), "resource.getDuration(): " + resource.getDuration());
62+
Log.v(getClass().getName(), "resource.getDuration(): " + resource.getDuration());
6363
if (resource.getDuration() != null) {
6464
try {
6565
String[] tokens = resource.getDuration().split(":");
@@ -72,7 +72,7 @@ public PlayableItem(Item item, int defaultDuration) {
7272
if (tokens.length > 2) {
7373
String seconds = tokens[2];
7474
if (tokens[2].contains(".")) {
75-
Log.d(getClass().getName(), "tokens[2]: " + tokens[2] + "spli: " + tokens[2].split("\\.").length);
75+
Log.d(getClass().getName(), "tokens[2]: " + tokens[2] + "split: " + tokens[2].split("\\.").length);
7676
seconds = tokens[2].split("\\.")[0];
7777
}
7878
millis += Long.parseLong(seconds);

yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerControlActivity.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import android.util.TypedValue;
2828
import android.view.Menu;
2929
import android.view.MenuItem;
30+
import android.widget.Button;
3031
import android.widget.CheckBox;
3132
import android.widget.TextView;
3233

@@ -43,6 +44,7 @@
4344

4445
import de.yaacc.R;
4546
import de.yaacc.settings.SettingsActivity;
47+
import de.yaacc.upnp.server.contentdirectory.MediaPathFilter;
4648
import de.yaacc.util.AboutActivity;
4749
import de.yaacc.util.NotificationId;
4850

@@ -97,7 +99,10 @@ protected void onCreate(Bundle savedInstanceState) {
9799
stop();
98100
}
99101
}));
100-
102+
Button resetButton = findViewById(R.id.sharedFoldersReset);
103+
resetButton.setOnClickListener(v -> {
104+
MediaPathFilter.resetMediaPaths(getApplicationContext());
105+
});
101106

102107
TextView localServerControlInterface = findViewById(R.id.localServerControlInterface);
103108
String[] ipConfig = YaaccUpnpServerService.getIfAndIpAddress(this);

yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,6 @@ public class YaaccUpnpServerService extends Service implements SharedPreferences
116116
public String mediaServerUuid;
117117
public String mediaRendererUuid;
118118
protected IBinder binder = new YaaccUpnpServerServiceBinder();
119-
// make preferences available for the whole service, since there might be
120-
// more things to configure in the future
121119
SharedPreferences preferences;
122120
private LocalDevice localServer;
123121
private LocalDevice localRenderer;
@@ -331,12 +329,15 @@ public void execute(Exception ex) {
331329
.setCanonicalHostName(getIpAddress(getApplicationContext()))
332330
.register("*", new YaaccUpnpServerServiceHttpHandler(getApplicationContext()))
333331
.create();
334-
335-
httpServer.listen(new InetSocketAddress(PORT), URIScheme.HTTP);
336332
httpServer.start();
337333
} else {
334+
338335
httpServer.resume();
339336
}
337+
httpServer.listen(new InetSocketAddress(PORT), URIScheme.HTTP);
338+
Log.d(getClass().getName(), "Server status: " + httpServer.getStatus().name());
339+
Log.d(getClass().getName(), "Server Endpoints: " + httpServer.getEndpoints().size());
340+
httpServer.getEndpoints().forEach(endpoint -> Log.d(getClass().getName(), "Endpoint: " + endpoint.toString()));
340341
timer.schedule(new TimerTask() {
341342

342343
@Override
@@ -369,7 +370,20 @@ private void checkIfHttpServerIsRunning() {
369370
}
370371
} catch (IOException e) {
371372
Log.e(getClass().getName(), "HttpServer is NOT responding to HTTP requests or is unreachable. Trying restart", e);
372-
restartServerService();
373+
//restartServerService();
374+
if (httpServer != null) {
375+
httpServer.listen(new InetSocketAddress(PORT), URIScheme.HTTP);
376+
timer.schedule(new TimerTask() {
377+
378+
@Override
379+
public void run() {
380+
Log.d(getClass().getName(), "Server Endpoints after restart listener: " + httpServer.getEndpoints().size());
381+
httpServer.getEndpoints().forEach(endpoint -> Log.d(getClass().getName(), "Endpoint: " + endpoint.toString()));
382+
}
383+
}, 500L);
384+
385+
}
386+
373387
return;
374388
}
375389
}

yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerServiceHttpHandler.java

Lines changed: 59 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,18 @@
1919
package de.yaacc.upnp.server;
2020

2121
import android.annotation.SuppressLint;
22+
import android.content.ContentUris;
2223
import android.content.Context;
2324
import android.content.SharedPreferences;
2425
import android.database.Cursor;
2526
import android.graphics.Bitmap;
2627
import android.graphics.drawable.BitmapDrawable;
2728
import android.graphics.drawable.Drawable;
2829
import android.net.Uri;
30+
import android.os.Build;
2931
import android.provider.MediaStore;
3032
import android.util.Log;
33+
import android.util.Size;
3134

3235
import androidx.core.content.res.ResourcesCompat;
3336
import androidx.preference.PreferenceManager;
@@ -54,6 +57,7 @@
5457

5558
import java.io.ByteArrayOutputStream;
5659
import java.io.File;
60+
import java.io.FileOutputStream;
5761
import java.io.IOException;
5862
import java.io.InputStream;
5963
import java.io.RandomAccessFile;
@@ -271,43 +275,64 @@ private ContentHolder lookupAlbumArt(String albumId, List<HttpRange> ranges) {
271275
if (albumId == null) {
272276
return result;
273277
}
274-
Log.d(getClass().getName(), "System media store lookup album: "
275-
+ albumId);
276-
String[] projection = {MediaStore.Audio.Albums._ID,
277-
// FIXME what is the right mime type?
278-
// MediaStore.Audio.Albums.MIME_TYPE,
279-
MediaStore.Audio.Albums.ALBUM_ART};
280-
String selection = MediaStore.Audio.Albums._ID + "=?";
281-
String[] selectionArgs = {albumId};
282-
try (Cursor cursor = getContext().getContentResolver().query(
283-
MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, projection,
284-
selection, selectionArgs, null)) {
285-
286-
if (cursor != null) {
287-
cursor.moveToFirst();
288-
while (!cursor.isAfterLast()) {
289-
@SuppressLint("Range") String dataUri = cursor.getString(cursor
290-
.getColumnIndex(MediaStore.Audio.Albums.ALBUM_ART));
291-
292-
// String mimeTypeStr = null;
293-
// FIXME mime type resolving cursor
294-
// .getString(cursor
295-
// .getColumnIndex(MediaStore.Files.FileColumns.MIME_TYPE));
296-
297-
MimeType mimeType = MimeType.valueOf("image/png");
298-
// if (mimeTypeStr != null) {
299-
// mimeType = MimeType.valueOf(mimeTypeStr);
300-
// }
301-
if (dataUri != null) {
302-
Log.d(getClass().getName(), "Content found: " + mimeType
303-
+ " Uri: " + dataUri);
304-
result = new ContentHolder(mimeType, dataUri, ranges);
278+
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
279+
Log.d(getClass().getName(), "System media store lookup album: "
280+
+ albumId);
281+
String[] projection = {MediaStore.Audio.Albums._ID,
282+
// FIXME what is the right mime type?
283+
// MediaStore.Audio.Albums.MIME_TYPE,
284+
MediaStore.Audio.Albums.ALBUM_ART};
285+
String selection = MediaStore.Audio.Albums._ID + "=?";
286+
String[] selectionArgs = {albumId};
287+
try (Cursor cursor = getContext().getContentResolver().query(
288+
MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, projection,
289+
selection, selectionArgs, null)) {
290+
291+
if (cursor != null) {
292+
cursor.moveToFirst();
293+
while (!cursor.isAfterLast()) {
294+
@SuppressLint("Range") String dataUri = cursor.getString(cursor
295+
.getColumnIndex(MediaStore.Audio.Albums.ALBUM_ART));
296+
297+
// String mimeTypeStr = null;
298+
// FIXME mime type resolving cursor
299+
// .getString(cursor
300+
// .getColumnIndex(MediaStore.Files.FileColumns.MIME_TYPE));
301+
302+
MimeType mimeType = MimeType.valueOf("image/png");
303+
// if (mimeTypeStr != null) {
304+
// mimeType = MimeType.valueOf(mimeTypeStr);
305+
// }
306+
if (dataUri != null) {
307+
Log.d(getClass().getName(), "Content found: " + mimeType
308+
+ " Uri: " + dataUri);
309+
result = new ContentHolder(mimeType, dataUri, ranges);
310+
}
311+
cursor.moveToNext();
305312
}
306-
cursor.moveToNext();
313+
} else {
314+
Log.d(getClass().getName(), "System media store is empty.");
307315
}
308-
} else {
309-
Log.d(getClass().getName(), "System media store is empty.");
310316
}
317+
} else {
318+
Uri albumArtUri = ContentUris.withAppendedId(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, Long.parseLong(albumId));
319+
MimeType mimeType = MimeType.valueOf("image/jpeg");
320+
Log.d(getClass().getName(), "Content found: " + mimeType
321+
+ " Uri: " + albumArtUri);
322+
try {
323+
Bitmap bitmap = context.getContentResolver().loadThumbnail(albumArtUri, new Size(1024, 1024), null);
324+
325+
File art = new File(context.getCacheDir(), "albumart" + albumId + ".jpg");
326+
art.createNewFile();
327+
FileOutputStream fos = new FileOutputStream(art);
328+
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);
329+
fos.flush();
330+
fos.close();
331+
result = new ContentHolder(mimeType, art.getAbsolutePath(), ranges);
332+
} catch (IOException e) {
333+
Log.e(getClass().getName(), "Error loading album art", e);
334+
}
335+
311336
}
312337
return result;
313338
}

yaacc/src/main/java/de/yaacc/upnp/server/contentdirectory/MediaPathFilter.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,11 @@ public static void saveMediaPaths(Context context, Set<String> newPaths) {
4444
editor.putStringSet(context.getString(R.string.settings_media_paths_pref_key), newPaths);
4545
editor.apply();
4646
}
47+
48+
public static void resetMediaPaths(Context context) {
49+
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
50+
SharedPreferences.Editor editor = prefs.edit();
51+
editor.putStringSet(context.getString(R.string.settings_media_paths_pref_key), new HashSet<>());
52+
editor.apply();
53+
}
4754
}

0 commit comments

Comments
 (0)