From 50684b57f55304d15a4645c9a6869d0ea2977ec8 Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 22 Jan 2026 19:17:38 -0300 Subject: [PATCH 01/14] Miscellaneous additions related to notifications. Add notification permissions for Android 13+ Add error notification. --- app/src/main/AndroidManifest.xml | 2 + .../geleia/activities/AboutActivity.java | 3 +- .../geleia/service/DownloadService.java | 5 +- .../notifications/ErrorNotification.java | 49 +++++++++++++++++++ .../adrianvictor/geleia/util/MusicUtil.java | 3 +- app/src/main/res/values/strings.xml | 1 + 6 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/org/adrianvictor/geleia/service/notifications/ErrorNotification.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2ad3febf..56dc0f00 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,9 @@ + + diff --git a/app/src/main/java/org/adrianvictor/geleia/activities/AboutActivity.java b/app/src/main/java/org/adrianvictor/geleia/activities/AboutActivity.java index 50601fac..56ffa5cf 100644 --- a/app/src/main/java/org/adrianvictor/geleia/activities/AboutActivity.java +++ b/app/src/main/java/org/adrianvictor/geleia/activities/AboutActivity.java @@ -7,6 +7,7 @@ import android.view.View; import androidx.annotation.NonNull; +import org.adrianvictor.geleia.service.notifications.ErrorNotification; import org.adrianvictor.geleia.util.NavigationUtil; import org.adrianvictor.geleia.util.PreferenceUtil; import org.adrianvictor.geleia.databinding.ActivityAboutBinding; @@ -102,7 +103,7 @@ public class AboutActivity extends AbsBaseActivity implements View.OnClickListen try { return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); + ErrorNotification.show(context, e.getMessage()); } return "Unknown"; diff --git a/app/src/main/java/org/adrianvictor/geleia/service/DownloadService.java b/app/src/main/java/org/adrianvictor/geleia/service/DownloadService.java index b3a61b5f..e304a40d 100644 --- a/app/src/main/java/org/adrianvictor/geleia/service/DownloadService.java +++ b/app/src/main/java/org/adrianvictor/geleia/service/DownloadService.java @@ -11,6 +11,7 @@ import org.adrianvictor.geleia.BuildConfig; import org.adrianvictor.geleia.database.Cache; import org.adrianvictor.geleia.model.Song; import org.adrianvictor.geleia.service.notifications.DownloadNotification; +import org.adrianvictor.geleia.service.notifications.ErrorNotification; import org.adrianvictor.geleia.util.MusicUtil; import org.adrianvictor.geleia.util.PreferenceUtil; @@ -23,7 +24,6 @@ import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -@SuppressWarnings("ResultOfMethodCallIgnored") public class DownloadService extends Service { public static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID; public static final String ACTION_START = PACKAGE_NAME + ".action.start"; @@ -55,6 +55,7 @@ public class DownloadService extends Service { break; case DownloadService.ACTION_START: List songs = intent.getParcelableArrayListExtra(EXTRA_SONGS); + assert songs != null; for (Song song : songs) { download(song); notification.start(song); @@ -116,7 +117,7 @@ public class DownloadService extends Service { App.getDatabase().cacheDao().insertCache(new Cache(song)); notification.stop(song); } catch (Exception e) { - e.printStackTrace(); + ErrorNotification.show(this, e.getMessage()); } }); } diff --git a/app/src/main/java/org/adrianvictor/geleia/service/notifications/ErrorNotification.java b/app/src/main/java/org/adrianvictor/geleia/service/notifications/ErrorNotification.java new file mode 100644 index 00000000..793a9db7 --- /dev/null +++ b/app/src/main/java/org/adrianvictor/geleia/service/notifications/ErrorNotification.java @@ -0,0 +1,49 @@ +package org.adrianvictor.geleia.service.notifications; + +import static android.content.Context.NOTIFICATION_SERVICE; + +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.os.Build; + +import androidx.core.app.NotificationCompat; + +import org.adrianvictor.geleia.R; + +public class ErrorNotification { + private static final String CHANNEL_ID = ErrorNotification.class.getSimpleName(); + private static final int NOTIFICATION_ID = 3; + + private ErrorNotification() {} + + public static void show(Context context, String error) { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE); + createNotificationChannel(notificationManager); + + NotificationCompat.BigTextStyle style = new NotificationCompat.BigTextStyle(); + style.setBigContentTitle("Error:").bigText(error); + + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID) + .setSmallIcon(R.drawable.ic_bug_report_white_24dp) + .setContentTitle(context.getString(R.string.error_notification_title)) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setStyle(style) + .setContentText("Expand the notification for details."); + } + + public static void createNotificationChannel(NotificationManager notificationManager) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = notificationManager.getNotificationChannel(CHANNEL_ID); + + if (channel == null) { + channel = new NotificationChannel(CHANNEL_ID, "Errors", NotificationManager.IMPORTANCE_LOW); + channel.setDescription("Displays all sorts of errors."); + channel.enableLights(false); + channel.enableVibration(true); + + notificationManager.createNotificationChannel(channel); + } + } + } +} diff --git a/app/src/main/java/org/adrianvictor/geleia/util/MusicUtil.java b/app/src/main/java/org/adrianvictor/geleia/util/MusicUtil.java index 8831fe38..bead4f0e 100644 --- a/app/src/main/java/org/adrianvictor/geleia/util/MusicUtil.java +++ b/app/src/main/java/org/adrianvictor/geleia/util/MusicUtil.java @@ -18,6 +18,7 @@ import org.adrianvictor.geleia.model.Codec; import org.adrianvictor.geleia.model.Genre; import org.adrianvictor.geleia.model.Song; +import org.adrianvictor.geleia.service.notifications.ErrorNotification; import org.jellyfin.apiclient.interaction.ApiClient; import org.jellyfin.apiclient.interaction.Response; import org.jellyfin.apiclient.model.dto.UserItemDataDto; @@ -102,7 +103,7 @@ public class MusicUtil { try { return new Intent(); } catch (IllegalArgumentException e) { - e.printStackTrace(); + ErrorNotification.show(context, e.getMessage()); Toast.makeText(context, R.string.error_share_file, Toast.LENGTH_SHORT).show(); return new Intent(); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6b3353bf..93e8a790 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -235,5 +235,6 @@ Please fill in your username. Forking Phonograph and making Gelli Adrian Victor + An error occurred in Jamfish. From 34c4bcc831f5de7c60e53eb490cba76bb5bdb643 Mon Sep 17 00:00:00 2001 From: adrian Date: Fri, 23 Jan 2026 16:04:27 -0300 Subject: [PATCH 02/14] Add UnreachableActivity for when the server cannot be reached. --- app/src/main/AndroidManifest.xml | 2 +- .../java/org/adrianvictor/geleia/App.java | 3 +- .../geleia/activities/MainActivity.java | 34 ++++++++++++++ .../geleia/activities/SearchActivity.java | 3 ++ .../geleia/activities/SplashActivity.java | 44 +++++++++++++++++-- .../activities/UnreachableActivity.java | 21 +++++++++ .../activities/base/AbsBaseActivity.java | 2 +- .../base/AbsMusicContentActivity.java | 25 +++++------ .../details/AlbumDetailActivity.java | 7 ++- .../details/ArtistDetailActivity.java | 9 +++- .../details/GenreDetailActivity.java | 5 +++ .../details/PlaylistDetailActivity.java | 5 +++ .../CustomFragmentStatePagerAdapter.java | 2 +- .../geleia/dialogs/SongShareDialog.java | 2 +- .../geleia/fragments/OfflineFragment.java | 25 +++++++++++ .../geleia/helper/MusicPlayerRemote.java | 8 ++-- .../org/adrianvictor/geleia/model/Album.java | 4 +- .../org/adrianvictor/geleia/model/Song.java | 6 +-- .../geleia/service/LoginService.java | 15 +++++++ .../notifications/DownloadNotification.java | 2 +- .../adrianvictor/geleia/util/MusicUtil.java | 4 +- .../geleia/util/NavigationUtil.java | 8 ++++ .../geleia/util/PlaylistUtil.java | 2 +- .../geleia/util/PreferenceUtil.java | 1 - .../shortcuts/DynamicShortcutManager.java | 2 +- .../main/res/layout/activity_unreachable.xml | 32 ++++++++++++++ app/src/main/res/layout/fragment_offline.xml | 40 +++++++++++++++++ app/src/main/res/values/strings.xml | 5 +++ 28 files changed, 279 insertions(+), 39 deletions(-) create mode 100644 app/src/main/java/org/adrianvictor/geleia/activities/UnreachableActivity.java create mode 100644 app/src/main/java/org/adrianvictor/geleia/fragments/OfflineFragment.java create mode 100644 app/src/main/res/layout/activity_unreachable.xml create mode 100644 app/src/main/res/layout/fragment_offline.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 56dc0f00..f49e236e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -66,7 +66,7 @@ android:name="org.adrianvictor.geleia.views.shortcuts.AppShortcutLauncherActivity" android:launchMode="singleInstance" android:theme="@android:style/Theme.Translucent.NoTitleBar" /> - + diff --git a/app/src/main/java/org/adrianvictor/geleia/App.java b/app/src/main/java/org/adrianvictor/geleia/App.java index 3145450b..989b2e58 100644 --- a/app/src/main/java/org/adrianvictor/geleia/App.java +++ b/app/src/main/java/org/adrianvictor/geleia/App.java @@ -40,7 +40,7 @@ public class App extends Application { database = createDatabase(this); apiClient = createApiClient(this); - if (database.userDao().getUsers().size() == 0) { + if (database.userDao().getUsers().isEmpty()) { PreferenceUtil.getInstance(this).setServer(null); PreferenceUtil.getInstance(this).setUser(null); } @@ -76,6 +76,7 @@ public class App extends Application { IDevice device = new AndroidDevice(deviceId, deviceName); EventListener eventListener = new EventListener(); + return new ApiClient(httpClient, logger, server, appName, appVersion, device, eventListener); } diff --git a/app/src/main/java/org/adrianvictor/geleia/activities/MainActivity.java b/app/src/main/java/org/adrianvictor/geleia/activities/MainActivity.java index 0f1df8a7..e8cb9410 100644 --- a/app/src/main/java/org/adrianvictor/geleia/activities/MainActivity.java +++ b/app/src/main/java/org/adrianvictor/geleia/activities/MainActivity.java @@ -1,9 +1,12 @@ package org.adrianvictor.geleia.activities; +import static org.adrianvictor.geleia.adapter.CustomFragmentStatePagerAdapter.TAG; + import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -13,10 +16,12 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.drawerlayout.widget.DrawerLayout; +import androidx.lifecycle.Lifecycle; import com.afollestad.materialcab.attached.AttachedCab; import com.afollestad.materialcab.attached.AttachedCabKt; import org.adrianvictor.geleia.activities.base.AbsMusicContentActivity; +import org.adrianvictor.geleia.fragments.OfflineFragment; import org.adrianvictor.geleia.interfaces.CabHolder; import org.adrianvictor.geleia.util.PreferenceUtil; import org.adrianvictor.geleia.util.ThemeUtil; @@ -42,6 +47,7 @@ public class MainActivity extends AbsMusicContentActivity implements CabHolder { private ActivityMainContentBinding contentBinding; private NavigationDrawerHeaderBinding navigationBinding; private boolean onLogout; + private boolean pendingShowOffline = false; @Nullable private AttachedCab cab; @@ -97,6 +103,24 @@ public class MainActivity extends AbsMusicContentActivity implements CabHolder { }); } + @Override + public void onStateOffline() { + Log.d(TAG, "onStateOffline() foi chamado."); + Menu menu = binding.navigationView.getMenu(); + menu.clear(); + + menu.add(R.id.navigation_drawer_menu_category_other, R.id.nav_settings, menu.size(), R.string.action_settings); + menu.getItem(menu.size() - 1).setIcon(R.drawable.ic_settings_white_24dp); + menu.add(R.id.navigation_drawer_menu_category_other, R.id.nav_about, menu.size(), R.string.action_about); + menu.getItem(menu.size() - 1).setIcon(R.drawable.ic_info_outline_white_24dp); + menu.add(R.id.navigation_drawer_menu_category_other, R.id.nav_logout, menu.size(), R.string.logout); + menu.getItem(menu.size() - 1).setIcon(R.drawable.ic_exit_to_app_white_48dp); + + setUpDrawerLayout(); + + pendingShowOffline = true; + } + @Override public void onPause() { super.onPause(); @@ -108,6 +132,16 @@ public class MainActivity extends AbsMusicContentActivity implements CabHolder { } } + @Override + protected void onResume() { + super.onResume(); + + if (pendingShowOffline) { + setCurrentFragment(OfflineFragment.newInstance()); + pendingShowOffline = false; + } + } + private void setCurrentFragment(Fragment fragment) { getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment, null).commit(); } diff --git a/app/src/main/java/org/adrianvictor/geleia/activities/SearchActivity.java b/app/src/main/java/org/adrianvictor/geleia/activities/SearchActivity.java index 2958e587..b483b51d 100644 --- a/app/src/main/java/org/adrianvictor/geleia/activities/SearchActivity.java +++ b/app/src/main/java/org/adrianvictor/geleia/activities/SearchActivity.java @@ -93,6 +93,9 @@ public class SearchActivity extends AbsMusicContentActivity implements SearchVie public void onStateOnline() { } + @Override + public void onStateOffline() {} + private void setUpToolBar() { binding.toolbar.setBackgroundColor(PreferenceUtil.getInstance(this).getPrimaryColor()); setSupportActionBar(binding.toolbar); diff --git a/app/src/main/java/org/adrianvictor/geleia/activities/SplashActivity.java b/app/src/main/java/org/adrianvictor/geleia/activities/SplashActivity.java index 88a3b8af..da451a59 100644 --- a/app/src/main/java/org/adrianvictor/geleia/activities/SplashActivity.java +++ b/app/src/main/java/org/adrianvictor/geleia/activities/SplashActivity.java @@ -1,8 +1,13 @@ package org.adrianvictor.geleia.activities; +import android.content.BroadcastReceiver; +import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.os.Build; import android.os.Bundle; -import android.os.Handler; + +import androidx.annotation.NonNull; import org.adrianvictor.geleia.App; import org.adrianvictor.geleia.R; @@ -15,6 +20,27 @@ import org.adrianvictor.geleia.util.PreferenceUtil; import java.util.List; public class SplashActivity extends AbsBaseActivity { + + private final BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, @NonNull Intent intent) { + if (intent.getAction() == null) { + return; + } + + switch (intent.getAction()) { + case LoginService.STATE_ONLINE: + NavigationUtil.startMain(context); + finish(); + break; + case LoginService.STATE_OFFLINE: + NavigationUtil.startUnreachable(context); + finish(); + break; + } + } + }; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -25,6 +51,7 @@ public class SplashActivity extends AbsBaseActivity { @Override public void onPause() { super.onPause(); + unregisterReceiver(receiver); overridePendingTransition(0, R.anim.fade_delay); } @@ -32,16 +59,27 @@ public class SplashActivity extends AbsBaseActivity { protected void onResume() { super.onResume(); + final IntentFilter filter = new IntentFilter(); + filter.addAction(LoginService.STATE_ONLINE); + filter.addAction(LoginService.STATE_OFFLINE); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED); + } else { + registerReceiver(receiver, filter); + } + User user = App.getDatabase().userDao().getUser(PreferenceUtil.getInstance(this).getUser()); List available = App.getDatabase().userDao().getUsers(); - if (user == null && available.size() != 0) { + if (user == null && !available.isEmpty()) { NavigationUtil.startSelect(this); + finish(); } else if (user == null) { NavigationUtil.startLogin(this); + finish(); } else { startService(new Intent(this, LoginService.class)); - new Handler().postDelayed(() -> NavigationUtil.startMain(this), 1000); } } } diff --git a/app/src/main/java/org/adrianvictor/geleia/activities/UnreachableActivity.java b/app/src/main/java/org/adrianvictor/geleia/activities/UnreachableActivity.java new file mode 100644 index 00000000..94afba90 --- /dev/null +++ b/app/src/main/java/org/adrianvictor/geleia/activities/UnreachableActivity.java @@ -0,0 +1,21 @@ +package org.adrianvictor.geleia.activities; + +import android.os.Bundle; +import android.view.View; + +import org.adrianvictor.geleia.R; +import org.adrianvictor.geleia.activities.base.AbsThemeActivity; +import org.adrianvictor.geleia.util.NavigationUtil; + +public class UnreachableActivity extends AbsThemeActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_unreachable); + } + + public void onSelectClick(View view) { + NavigationUtil.startSelect(this); + } +} diff --git a/app/src/main/java/org/adrianvictor/geleia/activities/base/AbsBaseActivity.java b/app/src/main/java/org/adrianvictor/geleia/activities/base/AbsBaseActivity.java index 406f6237..041effb2 100644 --- a/app/src/main/java/org/adrianvictor/geleia/activities/base/AbsBaseActivity.java +++ b/app/src/main/java/org/adrianvictor/geleia/activities/base/AbsBaseActivity.java @@ -59,7 +59,7 @@ public abstract class AbsBaseActivity extends AbsThemeActivity { .setPositiveButton(R.string.disable, (dialog, id) -> requestBatteryOptimization()); new Handler().postDelayed(builder::show, 2000); - } else if (permissions.size() != 0 && ActivityCompat.shouldShowRequestPermissionRationale(this, permissions.get(0))) { + } else if (!permissions.isEmpty() && ActivityCompat.shouldShowRequestPermissionRationale(this, permissions.get(0))) { builder.setMessage(getPermissionMessage()) .setTitle(R.string.permissions_denied) .setPositiveButton(R.string.action_grant, (dialog, id) -> requestPermissions()); diff --git a/app/src/main/java/org/adrianvictor/geleia/activities/base/AbsMusicContentActivity.java b/app/src/main/java/org/adrianvictor/geleia/activities/base/AbsMusicContentActivity.java index 904b928f..2c760a46 100644 --- a/app/src/main/java/org/adrianvictor/geleia/activities/base/AbsMusicContentActivity.java +++ b/app/src/main/java/org/adrianvictor/geleia/activities/base/AbsMusicContentActivity.java @@ -1,14 +1,19 @@ package org.adrianvictor.geleia.activities.base; +import static org.adrianvictor.geleia.adapter.CustomFragmentStatePagerAdapter.TAG; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.os.Build; import android.os.Bundle; +import android.util.Log; import androidx.annotation.NonNull; import org.adrianvictor.geleia.App; +import org.adrianvictor.geleia.fragments.OfflineFragment; import org.adrianvictor.geleia.interfaces.StateListener; import org.adrianvictor.geleia.service.LoginService; import org.adrianvictor.geleia.util.NavigationUtil; @@ -24,7 +29,7 @@ public abstract class AbsMusicContentActivity extends AbsMusicPanelActivity impl onStateOnline(); break; case LoginService.STATE_OFFLINE: - NavigationUtil.startLogin(context); + NavigationUtil.startSelect(context); break; } } @@ -39,7 +44,11 @@ public abstract class AbsMusicContentActivity extends AbsMusicPanelActivity impl filter.addAction(LoginService.STATE_ONLINE); filter.addAction(LoginService.STATE_OFFLINE); - registerReceiver(receiver, filter); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED); + } else { + registerReceiver(receiver, filter); + } if (App.getApiClient() == null) { startService(new Intent(this, LoginService.class)); @@ -51,24 +60,14 @@ public abstract class AbsMusicContentActivity extends AbsMusicPanelActivity impl @Override protected void onResume() { super.onResume(); - - if (App.getApiClient() == null) { - startService(new Intent(this, LoginService.class)); - } } @Override protected void onDestroy() { unregisterReceiver(receiver); - super.onDestroy(); } @Override - public void onStatePolling() { - } - - @Override - public void onStateOffline() { - } + public void onStatePolling() {} } diff --git a/app/src/main/java/org/adrianvictor/geleia/activities/details/AlbumDetailActivity.java b/app/src/main/java/org/adrianvictor/geleia/activities/details/AlbumDetailActivity.java index 9c7ca01b..ad920baa 100644 --- a/app/src/main/java/org/adrianvictor/geleia/activities/details/AlbumDetailActivity.java +++ b/app/src/main/java/org/adrianvictor/geleia/activities/details/AlbumDetailActivity.java @@ -74,6 +74,11 @@ public class AlbumDetailActivity extends AbsMusicContentActivity implements Pale }); } + @Override + public void onStateOffline() { + + } + @Override public void onOffsetChanged (AppBarLayout appBarLayout, int verticalOffset) { float headerAlpha = Math.max(0, Math.min(1, 1 + (2 * (float) verticalOffset / headerViewHeight))); @@ -231,6 +236,6 @@ public class AlbumDetailActivity extends AbsMusicContentActivity implements Pale binding.durationText.setText(MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, album.songs))); binding.albumYearText.setText(MusicUtil.getYearString(album.year)); - if (album.songs.size() != 0) adapter.swapDataSet(album.songs); + if (!album.songs.isEmpty()) adapter.swapDataSet(album.songs); } } diff --git a/app/src/main/java/org/adrianvictor/geleia/activities/details/ArtistDetailActivity.java b/app/src/main/java/org/adrianvictor/geleia/activities/details/ArtistDetailActivity.java index d7cde428..2f98a935 100644 --- a/app/src/main/java/org/adrianvictor/geleia/activities/details/ArtistDetailActivity.java +++ b/app/src/main/java/org/adrianvictor/geleia/activities/details/ArtistDetailActivity.java @@ -87,6 +87,11 @@ public class ArtistDetailActivity extends AbsMusicContentActivity implements Pal }); } + @Override + public void onStateOffline() { + + } + @Override public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) { float headerAlpha = Math.max(0, Math.min(1, 1 + (2 * (float) verticalOffset / headerViewHeight))); @@ -258,7 +263,7 @@ public class ArtistDetailActivity extends AbsMusicContentActivity implements Pal binding.albumCountText.setText(MusicUtil.getAlbumCountString(this, artist.albums.size())); binding.durationText.setText(MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, artist.songs))); - if (artist.songs.size() != 0) songAdapter.swapDataSet(artist.songs); - if (artist.albums.size() != 0) albumAdapter.swapDataSet(artist.albums); + if (!artist.songs.isEmpty()) songAdapter.swapDataSet(artist.songs); + if (!artist.albums.isEmpty()) albumAdapter.swapDataSet(artist.albums); } } diff --git a/app/src/main/java/org/adrianvictor/geleia/activities/details/GenreDetailActivity.java b/app/src/main/java/org/adrianvictor/geleia/activities/details/GenreDetailActivity.java index 9449daa5..aacc7db8 100644 --- a/app/src/main/java/org/adrianvictor/geleia/activities/details/GenreDetailActivity.java +++ b/app/src/main/java/org/adrianvictor/geleia/activities/details/GenreDetailActivity.java @@ -59,6 +59,11 @@ public class GenreDetailActivity extends AbsMusicContentActivity implements CabH }); } + @Override + public void onStateOffline() { + + } + @Override protected View createContentView() { binding = ActivityGenreDetailBinding.inflate(getLayoutInflater()); diff --git a/app/src/main/java/org/adrianvictor/geleia/activities/details/PlaylistDetailActivity.java b/app/src/main/java/org/adrianvictor/geleia/activities/details/PlaylistDetailActivity.java index f111dc14..bb3d1d57 100644 --- a/app/src/main/java/org/adrianvictor/geleia/activities/details/PlaylistDetailActivity.java +++ b/app/src/main/java/org/adrianvictor/geleia/activities/details/PlaylistDetailActivity.java @@ -71,6 +71,11 @@ public class PlaylistDetailActivity extends AbsMusicContentActivity implements C }); } + @Override + public void onStateOffline() { + + } + @Override protected View createContentView() { binding = ActivityPlaylistDetailBinding.inflate(getLayoutInflater()); diff --git a/app/src/main/java/org/adrianvictor/geleia/adapter/CustomFragmentStatePagerAdapter.java b/app/src/main/java/org/adrianvictor/geleia/adapter/CustomFragmentStatePagerAdapter.java index d19a6ffe..f6c1c4f5 100644 --- a/app/src/main/java/org/adrianvictor/geleia/adapter/CustomFragmentStatePagerAdapter.java +++ b/app/src/main/java/org/adrianvictor/geleia/adapter/CustomFragmentStatePagerAdapter.java @@ -180,7 +180,7 @@ public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter { @Override public Parcelable saveState() { Bundle state = null; - if (mSavedState.size() > 0) { + if (!mSavedState.isEmpty()) { state = new Bundle(); Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()]; mSavedState.toArray(fss); diff --git a/app/src/main/java/org/adrianvictor/geleia/dialogs/SongShareDialog.java b/app/src/main/java/org/adrianvictor/geleia/dialogs/SongShareDialog.java index 101c9377..d2becf6a 100644 --- a/app/src/main/java/org/adrianvictor/geleia/dialogs/SongShareDialog.java +++ b/app/src/main/java/org/adrianvictor/geleia/dialogs/SongShareDialog.java @@ -31,7 +31,7 @@ public class SongShareDialog extends DialogFragment { final String currentlyListening = getString(R.string.currently_listening_to_x_by_x, song.title, song.artistName); return new MaterialDialog.Builder(requireActivity()) .title(R.string.what_do_you_want_to_share) - .items(getString(R.string.the_audio_file), "\u201C" + currentlyListening + "\u201D") + .items(getString(R.string.the_audio_file), "“" + currentlyListening + "”") .itemsCallback((materialDialog, view, i, charSequence) -> { switch (i) { case 0: diff --git a/app/src/main/java/org/adrianvictor/geleia/fragments/OfflineFragment.java b/app/src/main/java/org/adrianvictor/geleia/fragments/OfflineFragment.java new file mode 100644 index 00000000..ed1d40a0 --- /dev/null +++ b/app/src/main/java/org/adrianvictor/geleia/fragments/OfflineFragment.java @@ -0,0 +1,25 @@ +package org.adrianvictor.geleia.fragments; + +import android.app.Activity; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import org.adrianvictor.geleia.R; + +public class OfflineFragment extends Fragment { + public static OfflineFragment newInstance() { + return new OfflineFragment(); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_offline, container, false); + } +} diff --git a/app/src/main/java/org/adrianvictor/geleia/helper/MusicPlayerRemote.java b/app/src/main/java/org/adrianvictor/geleia/helper/MusicPlayerRemote.java index 4f658348..f409842c 100644 --- a/app/src/main/java/org/adrianvictor/geleia/helper/MusicPlayerRemote.java +++ b/app/src/main/java/org/adrianvictor/geleia/helper/MusicPlayerRemote.java @@ -273,7 +273,7 @@ public class MusicPlayerRemote { public static boolean playNext(Song song) { if (musicService != null && musicService.queueManager != null) { - if (getPlayingQueue().size() > 0) { + if (!getPlayingQueue().isEmpty()) { musicService.queueManager.addSong(getPosition() + 1, song); } else { List queue = new ArrayList<>(); @@ -290,7 +290,7 @@ public class MusicPlayerRemote { public static boolean playNext(@NonNull List songs) { if (musicService != null && musicService.queueManager != null) { - if (getPlayingQueue().size() > 0) { + if (!getPlayingQueue().isEmpty()) { musicService.queueManager.addSongs(getPosition() + 1, songs); } else { openQueue(songs, 0, false); @@ -306,7 +306,7 @@ public class MusicPlayerRemote { public static boolean enqueue(Song song) { if (musicService != null && musicService.queueManager != null) { - if (getPlayingQueue().size() > 0) { + if (!getPlayingQueue().isEmpty()) { musicService.queueManager.addSong(song); } else { List queue = new ArrayList<>(); @@ -323,7 +323,7 @@ public class MusicPlayerRemote { public static boolean enqueue(@NonNull List songs) { if (musicService != null && musicService.queueManager != null) { - if (getPlayingQueue().size() > 0) { + if (!getPlayingQueue().isEmpty()) { musicService.queueManager.addSongs(songs); } else { openQueue(songs, 0, false); diff --git a/app/src/main/java/org/adrianvictor/geleia/model/Album.java b/app/src/main/java/org/adrianvictor/geleia/model/Album.java index 36ff5554..81e4e481 100644 --- a/app/src/main/java/org/adrianvictor/geleia/model/Album.java +++ b/app/src/main/java/org/adrianvictor/geleia/model/Album.java @@ -29,10 +29,10 @@ public class Album implements Parcelable { this.title = itemDto.getName(); this.year = itemDto.getProductionYear() != null ? itemDto.getProductionYear() : 0; - if (itemDto.getAlbumArtists().size() != 0) { + if (!itemDto.getAlbumArtists().isEmpty()) { this.artistId = itemDto.getAlbumArtists().get(0).getId(); this.artistName = itemDto.getAlbumArtists().get(0).getName(); - } else if (itemDto.getArtistItems().size() != 0) { + } else if (!itemDto.getArtistItems().isEmpty()) { this.artistId = itemDto.getArtistItems().get(0).getId(); this.artistName = itemDto.getArtistItems().get(0).getName(); } diff --git a/app/src/main/java/org/adrianvictor/geleia/model/Song.java b/app/src/main/java/org/adrianvictor/geleia/model/Song.java index 277a5942..b9218cb3 100644 --- a/app/src/main/java/org/adrianvictor/geleia/model/Song.java +++ b/app/src/main/java/org/adrianvictor/geleia/model/Song.java @@ -67,10 +67,10 @@ public class Song implements Parcelable { this.albumId = itemDto.getAlbumId(); this.albumName = itemDto.getAlbum(); - if (itemDto.getArtistItems().size() != 0) { + if (!itemDto.getArtistItems().isEmpty()) { this.artistId = itemDto.getArtistItems().get(0).getId(); this.artistName = itemDto.getArtistItems().get(0).getName(); - } else if (itemDto.getAlbumArtists().size() != 0) { + } else if (!itemDto.getAlbumArtists().isEmpty()) { this.artistId = itemDto.getAlbumArtists().get(0).getId(); this.artistName = itemDto.getAlbumArtists().get(0).getName(); } @@ -93,7 +93,7 @@ public class Song implements Parcelable { this.supportsTranscoding = source.getSupportsTranscoding(); - if (source.getMediaStreams() != null && source.getMediaStreams().size() != 0) { + if (source.getMediaStreams() != null && !source.getMediaStreams().isEmpty()) { MediaStream stream = source.getMediaStreams().get(0); this.codec = stream.getCodec(); diff --git a/app/src/main/java/org/adrianvictor/geleia/service/LoginService.java b/app/src/main/java/org/adrianvictor/geleia/service/LoginService.java index 82f7a2a7..f00c69d1 100644 --- a/app/src/main/java/org/adrianvictor/geleia/service/LoginService.java +++ b/app/src/main/java/org/adrianvictor/geleia/service/LoginService.java @@ -46,6 +46,21 @@ public class LoginService extends Service { if (user == null) { Toast.makeText(this, context.getResources().getString(R.string.error_unexpected), Toast.LENGTH_SHORT).show(); + sendBroadcast(new Intent(STATE_OFFLINE)); + return; + } + + if (App.getApiClient() == null) { + try { + App.createApiClient(context); + } catch (Exception e) { + sendBroadcast(new Intent(STATE_OFFLINE)); + return; + } + } + + if (App.getApiClient() == null) { + sendBroadcast(new Intent(STATE_OFFLINE)); return; } diff --git a/app/src/main/java/org/adrianvictor/geleia/service/notifications/DownloadNotification.java b/app/src/main/java/org/adrianvictor/geleia/service/notifications/DownloadNotification.java index fe6c0efa..58f12445 100644 --- a/app/src/main/java/org/adrianvictor/geleia/service/notifications/DownloadNotification.java +++ b/app/src/main/java/org/adrianvictor/geleia/service/notifications/DownloadNotification.java @@ -83,7 +83,7 @@ public class DownloadNotification { songs.clear(); } - if (songs.size() != 0) { + if (!songs.isEmpty()) { return; } diff --git a/app/src/main/java/org/adrianvictor/geleia/util/MusicUtil.java b/app/src/main/java/org/adrianvictor/geleia/util/MusicUtil.java index bead4f0e..05f6f8ae 100644 --- a/app/src/main/java/org/adrianvictor/geleia/util/MusicUtil.java +++ b/app/src/main/java/org/adrianvictor/geleia/util/MusicUtil.java @@ -50,7 +50,7 @@ public class MusicUtil { List codecs = preferenceUtil.getDirectPlayCodecs(); Stream values = codecs.stream().map(codec -> codec.value); - if (codecs.size() != 0) { + if (!codecs.isEmpty()) { builder.append("&Container=").append(values.collect(Collectors.joining(","))); } @@ -111,7 +111,7 @@ public class MusicUtil { @NonNull public static String getArtistInfoString(@NonNull final Context context, @NonNull final Artist artist) { - return artist.genres.size() != 0 ? artist.genres.get(0).name : ""; + return !artist.genres.isEmpty() ? artist.genres.get(0).name : ""; } @NonNull diff --git a/app/src/main/java/org/adrianvictor/geleia/util/NavigationUtil.java b/app/src/main/java/org/adrianvictor/geleia/util/NavigationUtil.java index 924c47ba..30460c35 100644 --- a/app/src/main/java/org/adrianvictor/geleia/util/NavigationUtil.java +++ b/app/src/main/java/org/adrianvictor/geleia/util/NavigationUtil.java @@ -12,6 +12,7 @@ import androidx.core.util.Pair; import org.adrianvictor.geleia.activities.LoginActivity; import org.adrianvictor.geleia.activities.MainActivity; import org.adrianvictor.geleia.activities.SelectActivity; +import org.adrianvictor.geleia.activities.UnreachableActivity; import org.adrianvictor.geleia.model.Album; import org.adrianvictor.geleia.model.Artist; import org.adrianvictor.geleia.model.Genre; @@ -61,6 +62,13 @@ public class NavigationUtil { context.startActivity(intent); } + public static void startUnreachable(Context context) { + final Intent intent = new Intent(context, UnreachableActivity.class); + + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); + context.startActivity(intent); + } + public static void startSelect(Context context) { final Intent intent = new Intent(context, SelectActivity.class); diff --git a/app/src/main/java/org/adrianvictor/geleia/util/PlaylistUtil.java b/app/src/main/java/org/adrianvictor/geleia/util/PlaylistUtil.java index 9144b16a..731724ea 100644 --- a/app/src/main/java/org/adrianvictor/geleia/util/PlaylistUtil.java +++ b/app/src/main/java/org/adrianvictor/geleia/util/PlaylistUtil.java @@ -48,7 +48,7 @@ public class PlaylistUtil { PlaylistCreationRequest request = new PlaylistCreationRequest(); request.setUserId(App.getApiClient().getCurrentUserId()); request.setName(name); - if (ids.size() != 0) request.setItemIdList(ids); + if (!ids.isEmpty()) request.setItemIdList(ids); App.getApiClient().CreatePlaylist(request, new Response<>()); } diff --git a/app/src/main/java/org/adrianvictor/geleia/util/PreferenceUtil.java b/app/src/main/java/org/adrianvictor/geleia/util/PreferenceUtil.java index 1becdcb9..71ff53d5 100644 --- a/app/src/main/java/org/adrianvictor/geleia/util/PreferenceUtil.java +++ b/app/src/main/java/org/adrianvictor/geleia/util/PreferenceUtil.java @@ -448,7 +448,6 @@ public final class PreferenceUtil { }).collect(Collectors.toList()); } - @SuppressWarnings("SimplifyStreamApiCallChains") public void setCategories(List categories) { List values = categories.stream().map(category -> { return category.select ? category.toString() : category.toString().toLowerCase(); diff --git a/app/src/main/java/org/adrianvictor/geleia/views/shortcuts/DynamicShortcutManager.java b/app/src/main/java/org/adrianvictor/geleia/views/shortcuts/DynamicShortcutManager.java index ee889cd8..c45ab175 100644 --- a/app/src/main/java/org/adrianvictor/geleia/views/shortcuts/DynamicShortcutManager.java +++ b/app/src/main/java/org/adrianvictor/geleia/views/shortcuts/DynamicShortcutManager.java @@ -36,7 +36,7 @@ public class DynamicShortcutManager { } public void initDynamicShortcuts() { - if (shortcutManager.getDynamicShortcuts().size() == 0) { + if (shortcutManager.getDynamicShortcuts().isEmpty()) { shortcutManager.setDynamicShortcuts(getDefaultShortcuts()); } } diff --git a/app/src/main/res/layout/activity_unreachable.xml b/app/src/main/res/layout/activity_unreachable.xml new file mode 100644 index 00000000..ddc6f028 --- /dev/null +++ b/app/src/main/res/layout/activity_unreachable.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + +