diff --git a/app/build.gradle b/app/build.gradle index 3848365f..534ad6d3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,7 +13,9 @@ android { versionCode 2 versionName '1.4.1' + // for SDK < 19 multiDexEnabled true + vectorDrawables { useSupportLibrary true } @@ -105,8 +107,9 @@ dependencies { implementation 'com.android.support:multidex:1.0.3' implementation 'com.mlegy.redscreenofdeath:red-screen-of-death:0.1.3' - implementation 'com.squareup.retrofit2:retrofit:2.9.0' - implementation 'com.squareup.retrofit2:converter-gson:2.9.0' + // old version of retrofit to work with api < 21 + implementation 'com.squareup.retrofit2:retrofit:2.6.4' + implementation 'com.squareup.retrofit2:converter-gson:2.6.0' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' @@ -117,4 +120,10 @@ dependencies { implementation 'com.github.bumptech.glide:annotations:4.12.0' implementation 'com.github.bumptech.glide:glide:4.12.0' implementation 'com.github.bumptech.glide:okhttp3-integration:4.12.0' + + // for supporting legacy android versions: + implementation('com.squareup.okhttp3:okhttp:3.12.13') + + implementation "androidx.multidex:multidex:2.0.1" + implementation 'org.conscrypt:conscrypt-android:2.5.2' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f49e236e..e5ab2f2f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -51,6 +51,7 @@ + @@ -138,6 +139,12 @@ android:resource="@xml/provider_paths" /> + + diff --git a/app/src/main/java/org/adrianvictor/geleia/App.java b/app/src/main/java/org/adrianvictor/geleia/App.java index 989b2e58..231cee4c 100644 --- a/app/src/main/java/org/adrianvictor/geleia/App.java +++ b/app/src/main/java/org/adrianvictor/geleia/App.java @@ -6,6 +6,9 @@ import android.content.Context; import android.os.Build; import android.provider.Settings; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatDelegate; +import androidx.multidex.MultiDexApplication; import androidx.room.Room; import org.adrianvictor.geleia.database.JellyDatabase; @@ -14,6 +17,7 @@ import org.adrianvictor.geleia.util.PreferenceUtil; import org.adrianvictor.geleia.views.shortcuts.DynamicShortcutManager; import com.melegy.redscreenofdeath.RedScreenOfDeath; +import org.conscrypt.Conscrypt; import org.jellyfin.apiclient.interaction.AndroidDevice; import org.jellyfin.apiclient.interaction.ApiClient; import org.jellyfin.apiclient.interaction.VolleyHttpClient; @@ -22,7 +26,16 @@ import org.jellyfin.apiclient.interaction.http.IAsyncHttpClient; import org.jellyfin.apiclient.logging.AndroidLogger; import org.jellyfin.apiclient.logging.ILogger; -public class App extends Application { +import com.bumptech.glide.Glide; +import com.bumptech.glide.Registry; +import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader; +import com.bumptech.glide.load.model.GlideUrl; +import java.io.InputStream; +import okhttp3.OkHttpClient; + +import java.security.Security; + +public class App extends MultiDexApplication { private static App app; private static JellyDatabase database; @@ -30,8 +43,14 @@ public class App extends Application { @Override public void onCreate() { + // Initializing stuff for older Android APIs compatibility + Security.insertProviderAt(Conscrypt.newProvider(), 1); // To have SSL 1.2 on API < 19 + AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); // To load vectors on API < 19 + super.onCreate(); + initializeGlide(this); + if (BuildConfig.DEBUG) { RedScreenOfDeath.init(this); } @@ -91,4 +110,15 @@ public class App extends Application { public static App getInstance() { return app; } + + private void initializeGlide(@NonNull Context context) { + // This OkHttpClient is now created with Conscrypt as the SSL provider. + OkHttpClient client = new OkHttpClient.Builder().build(); + + // Manually create and register Glide's OkHttp component. + OkHttpUrlLoader.Factory factory = new OkHttpUrlLoader.Factory(client); + + // Ensure Glide is initialized and then register the component. + Glide.get(context).getRegistry().replace(GlideUrl.class, InputStream.class, factory); + } } diff --git a/app/src/main/java/org/adrianvictor/geleia/activities/DirectoryPickerActivity.java b/app/src/main/java/org/adrianvictor/geleia/activities/DirectoryPickerActivity.java new file mode 100644 index 00000000..4452596d --- /dev/null +++ b/app/src/main/java/org/adrianvictor/geleia/activities/DirectoryPickerActivity.java @@ -0,0 +1,127 @@ +package org.adrianvictor.geleia.activities; + +import android.content.Intent; +import android.os.Bundle; +import android.os.Environment; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; + +import androidx.appcompat.app.AppCompatActivity; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; + +public class DirectoryPickerActivity extends AppCompatActivity { + + public static final String EXTRA_RESULT_PATH = "result_path"; + + private File rootDir; + private File currentDir; + + private ArrayAdapter adapter; + private TextView pathView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + rootDir = Environment.getExternalStorageDirectory(); + currentDir = rootDir; + + LinearLayout layout = new LinearLayout(this); + layout.setOrientation(LinearLayout.VERTICAL); + + pathView = new TextView(this); + pathView.setPadding(24, 24, 24, 24); + + ListView listView = new ListView(this); + + Button selectButton = new Button(this); + selectButton.setText("Select this folder"); + + layout.addView(pathView); + layout.addView(listView, new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f)); + layout.addView(selectButton); + + setContentView(layout); + + adapter = new ArrayAdapter(this, + android.R.layout.simple_list_item_1, new ArrayList<>()) { + @Override + public View getView(int position, View convertView, ViewGroup parent) { + TextView tv = (TextView) super.getView(position, convertView, parent); + File f = getItem(position); + + File parentFile = currentDir.getParentFile(); + + if (parentFile != null && parentFile.equals(f)) { + tv.setText(".."); + } else { + tv.setText(f.getName()); + } + + return tv; + } + }; + + listView.setAdapter(adapter); + + listView.setOnItemClickListener((p, v, pos, id) -> { + File dir = adapter.getItem(pos); + if (dir != null) openDir(dir); + }); + + selectButton.setOnClickListener(v -> { + Intent result = new Intent(); + result.putExtra(EXTRA_RESULT_PATH, currentDir.getAbsolutePath()); + setResult(RESULT_OK, result); + finish(); + }); + + openDir(rootDir); + } + + private void openDir(File dir) { + if (!dir.exists() || !dir.canRead()) return; + + File[] dirs = dir.listFiles(f -> + f.isDirectory() && f.canRead() + ); + + if (dirs == null) return; + + adapter.clear(); + + File parent = dir.getParentFile(); + if (parent != null && parent.getAbsolutePath().startsWith(rootDir.getAbsolutePath())) { + adapter.add(parent); + } + + Arrays.sort(dirs); + + for (File f : dirs) { + adapter.add(f); + } + + currentDir = dir; + pathView.setText(dir.getAbsolutePath()); + } + + @Override + public void onBackPressed() { + File parent = currentDir.getParentFile(); + if (parent != null && + parent.getAbsolutePath().startsWith(rootDir.getAbsolutePath())) { + openDir(parent); + } else { + super.onBackPressed(); + } + } +} diff --git a/app/src/main/java/org/adrianvictor/geleia/activities/SettingsActivity.java b/app/src/main/java/org/adrianvictor/geleia/activities/SettingsActivity.java index b1406474..ab860b08 100644 --- a/app/src/main/java/org/adrianvictor/geleia/activities/SettingsActivity.java +++ b/app/src/main/java/org/adrianvictor/geleia/activities/SettingsActivity.java @@ -51,6 +51,7 @@ public class SettingsActivity extends AbsBaseActivity { public static class SettingsFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener { private ActivityResultLauncher dirPickerLauncher; + private ActivityResultLauncher legacyDirPickerLauncher; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -71,6 +72,25 @@ public class SettingsActivity extends AbsBaseActivity { } } }); + + legacyDirPickerLauncher = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + result -> { + if (result.getResultCode() == RESULT_OK && result.getData() != null) { + String path = result.getData().getStringExtra( + DirectoryPickerActivity.EXTRA_RESULT_PATH + ); + + if (path != null) { + SharedPreferences.Editor editor = + PreferenceManager.getDefaultSharedPreferences(requireContext()).edit(); + editor.putString(PreferenceUtil.LOCATION_DOWNLOAD, path); + editor.apply(); + invalidateSettings(); + } + } + } + ); } @Override @@ -161,8 +181,13 @@ public class SettingsActivity extends AbsBaseActivity { } private void openDirectoryPicker() { - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); - dirPickerLauncher.launch(intent); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); + dirPickerLauncher.launch(intent); + } else { + Intent intent = new Intent(requireContext(), DirectoryPickerActivity.class); + legacyDirPickerLauncher.launch(intent); + } } @Override 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 041effb2..d64b6bc2 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 @@ -127,8 +127,11 @@ public abstract class AbsBaseActivity extends AbsThemeActivity { private boolean checkBatteryOptimization() { String packageName = getPackageName(); PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); - - return pm.isIgnoringBatteryOptimizations(packageName); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return pm.isIgnoringBatteryOptimizations(packageName); + } else { + return true; + } } @RequiresApi(api = Build.VERSION_CODES.M) @@ -138,9 +141,11 @@ public abstract class AbsBaseActivity extends AbsThemeActivity { @RequiresApi(api = Build.VERSION_CODES.M) private boolean checkPermissions() { - for (String permission : permissions) { - if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { - return false; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + for (String permission : permissions) { + if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { + return false; + } } } 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 e304a40d..ac661493 100644 --- a/app/src/main/java/org/adrianvictor/geleia/service/DownloadService.java +++ b/app/src/main/java/org/adrianvictor/geleia/service/DownloadService.java @@ -4,6 +4,8 @@ import android.app.Service; import android.content.Intent; import android.net.Uri; import android.os.IBinder; +import android.util.Log; + import androidx.documentfile.provider.DocumentFile; import org.adrianvictor.geleia.App; @@ -25,6 +27,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class DownloadService extends Service { + public static final String TAG = DownloadService.class.getSimpleName(); public static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID; public static final String ACTION_START = PACKAGE_NAME + ".action.start"; public static final String ACTION_CANCEL = PACKAGE_NAME + ".action.cancel"; @@ -79,10 +82,10 @@ public class DownloadService extends Service { String location = PreferenceUtil.getInstance(App.getInstance()).getLocationDownload(); DocumentFile root; - if (location.equals(getApplicationContext().getCacheDir().toString())) { - root = DocumentFile.fromFile(new File(location)); - } else { + if (location.startsWith("content://")) { root = DocumentFile.fromTreeUri(this, Uri.parse(location)); + } else { + root = DocumentFile.fromFile(new File(location)); } DocumentFile artist = root.findFile(MusicUtil.ascii(song.artistName)); @@ -117,6 +120,7 @@ public class DownloadService extends Service { App.getDatabase().cacheDao().insertCache(new Cache(song)); notification.stop(song); } catch (Exception e) { + Log.e(TAG, "Failed to download song: " + song.title, e); ErrorNotification.show(this, e.getMessage()); } }); 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 58f12445..014eb570 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 @@ -1,114 +1,134 @@ package org.adrianvictor.geleia.service.notifications; +import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Build; +import android.widget.RemoteViews; -import androidx.annotation.RequiresApi; import androidx.core.app.NotificationCompat; import org.adrianvictor.geleia.R; -import org.adrianvictor.geleia.activities.MainActivity; import org.adrianvictor.geleia.model.Song; import org.adrianvictor.geleia.service.DownloadService; -import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; import java.util.List; -import java.util.stream.Collectors; - -import static android.content.Context.NOTIFICATION_SERVICE; public class DownloadNotification { - private static final String CHANNEL_ID = DownloadNotification.class.getSimpleName(); - private static final int NOTIFICATION_ID = 2; - + private static final String CHANNEL_ID = "download_channel"; + private final int ID = 2; private final Context context; private final NotificationManager notificationManager; + private final List queue = Collections.synchronizedList(new LinkedList<>()); - private final List songs; - - private int current; - private int maximum; + private long totalSize; + private long downloadedSize; + private int lastPercentage = -1; public DownloadNotification(Context context) { - this.notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE); this.context = context; - - this.songs = new ArrayList<>(); + this.notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + createNotificationChannel(); } - public synchronized void start(Song song) { - this.songs.add(song); - + private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createNotificationChannel(); + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, + context.getString(R.string.download_channel_name), + NotificationManager.IMPORTANCE_LOW + ); + notificationManager.createNotificationChannel(channel); } } - public synchronized void update(int current, int maximum) { - this.current += current; - this.maximum += maximum; - - Intent action = new Intent(context, MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - PendingIntent clickIntent = PendingIntent.getActivity(context, 0, action, PendingIntent.FLAG_IMMUTABLE); - - Intent cancel = new Intent(context, DownloadService.class).setAction(DownloadService.ACTION_CANCEL); - PendingIntent pendingCancel = PendingIntent.getService(context, 0, cancel, PendingIntent.FLAG_IMMUTABLE); - - NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); - for (Song item : songs.stream().limit(5).collect(Collectors.toList())) { - style.addLine(item.title); + public void start(Song song) { + queue.add(song); + if (queue.size() == 1) { // This is the first song of a new batch + totalSize = 0; + downloadedSize = 0; + lastPercentage = -1; + notificationManager.notify(ID, getNotification()); + } else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + // For KitKat, update for every new song to show correct count. + notificationManager.notify(ID, getNotification()); } + } + + public void update(long downloaded, long total) { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + return; // No progress updates for KitKat + } + + synchronized (this) { + totalSize += total; + downloadedSize += downloaded; + + int percentage = 0; + if (totalSize > 0) { + percentage = (int) ((downloadedSize * 100) / totalSize); + } + + if (percentage > lastPercentage) { + lastPercentage = percentage; + notificationManager.notify(ID, getNotification()); + } + } + } + + public void stop(Song song) { + queue.remove(song); + if (queue.isEmpty()) { + notificationManager.cancel(ID); + } else { + // Update notification to show new queue size. + // On KitKat, this is the only update after a download finishes. + notificationManager.notify(ID, getNotification()); + } + } + + public void cancelAll() { + queue.clear(); + notificationManager.cancel(ID); + } + + private Notification getNotification() { + Intent intent = new Intent(context, DownloadService.class); + intent.setAction(DownloadService.ACTION_CANCEL); + + int flags = 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + flags |= PendingIntent.FLAG_IMMUTABLE; + } + PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, flags); NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID) - .setSmallIcon(R.drawable.ic_notification) - .setContentIntent(clickIntent) - .setContentTitle(String.format(context.getString(R.string.downloading_x_songs), songs.size())) - .setProgress(this.maximum, this.current, false) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .addAction(R.drawable.ic_close_white_24dp, context.getString(R.string.action_cancel), pendingCancel) - .setStyle(style) - .setShowWhen(false); + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle(context.getString(R.string.downloading_songs)) + .setOngoing(true) + .addAction(android.R.drawable.ic_menu_close_clear_cancel, context.getString(android.R.string.cancel), pendingIntent); - notificationManager.notify(NOTIFICATION_ID, builder.build()); - } + String contentText = context.getResources().getQuantityString(R.plurals.downloading_s_songs, queue.size(), queue.size()); - public synchronized void stop(Song song) { - if (song != null) { - songs.remove(song); - } else { - songs.clear(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_download); + remoteViews.setTextViewText(R.id.notification_download_title, contentText); + int progress = lastPercentage > 0 ? lastPercentage : 0; + remoteViews.setProgressBar(R.id.notification_download_progress, 100, progress, totalSize == 0 && downloadedSize == 0); + builder.setCustomContentView(remoteViews); + } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + int progress = lastPercentage > 0 ? lastPercentage : 0; + builder.setProgress(100, progress, totalSize == 0 && downloadedSize == 0); + builder.setContentText(contentText); + } else { // KitKat + builder.setContentText(contentText); } - if (!songs.isEmpty()) { - return; - } - - current = 0; - maximum = 0; - - notificationManager.cancel(NOTIFICATION_ID); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - notificationManager.deleteNotificationChannel(CHANNEL_ID); - } - } - - @RequiresApi(Build.VERSION_CODES.O) - private void createNotificationChannel() { - NotificationChannel notificationChannel = notificationManager.getNotificationChannel(CHANNEL_ID); - - if (notificationChannel == null) { - notificationChannel = new NotificationChannel(CHANNEL_ID, context.getString(R.string.action_download), NotificationManager.IMPORTANCE_LOW); - - notificationChannel.setDescription(context.getString(R.string.playing_notification_description)); - notificationChannel.enableLights(false); - notificationChannel.enableVibration(false); - - notificationManager.createNotificationChannel(notificationChannel); - } + return builder.build(); } } diff --git a/app/src/main/java/org/adrianvictor/geleia/service/playback/LocalPlayer.java b/app/src/main/java/org/adrianvictor/geleia/service/playback/LocalPlayer.java index 0a63184d..2832d553 100644 --- a/app/src/main/java/org/adrianvictor/geleia/service/playback/LocalPlayer.java +++ b/app/src/main/java/org/adrianvictor/geleia/service/playback/LocalPlayer.java @@ -2,6 +2,7 @@ package org.adrianvictor.geleia.service.playback; import android.content.Context; import android.net.Uri; +import android.os.Build; import android.util.Log; import android.widget.Toast; @@ -141,11 +142,17 @@ public class LocalPlayer implements Playback { private List createMediaItems(List queue) { return queue.stream().map(song -> { - File audio = new File(MusicUtil.getFileUri(song)); - Uri uri = Uri.fromFile(audio); + String fileUri = MusicUtil.getFileUri(song); + Uri uri; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && fileUri.startsWith("content://")) { + uri = Uri.parse(fileUri); + } else { + File audio = new File(fileUri); + uri = Uri.fromFile(audio); - if (!audio.exists()) { - uri = Uri.parse(MusicUtil.getTranscodeUri(song)); + if (!audio.exists()) { + uri = Uri.parse(MusicUtil.getTranscodeUri(song)); + } } List containers = PreferenceUtil.getInstance(context).getDirectPlayCodecs().stream() diff --git a/app/src/main/java/org/adrianvictor/geleia/util/ImageUtil.java b/app/src/main/java/org/adrianvictor/geleia/util/ImageUtil.java index 1388f345..de326d91 100644 --- a/app/src/main/java/org/adrianvictor/geleia/util/ImageUtil.java +++ b/app/src/main/java/org/adrianvictor/geleia/util/ImageUtil.java @@ -14,6 +14,7 @@ import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.content.res.AppCompatResources; import androidx.core.graphics.drawable.DrawableCompat; import androidx.core.content.res.ResourcesCompat; @@ -91,18 +92,22 @@ public class ImageUtil { } public static Drawable getTintedVectorDrawable(@NonNull Context context, @DrawableRes int resId, @ColorInt int color) { - Drawable drawable = getVectorDrawable(context.getResources(), resId, context.getTheme()); + final Drawable drawable = AppCompatResources.getDrawable(context, resId); - DrawableCompat.setTintMode(drawable, PorterDuff.Mode.SRC_IN); - DrawableCompat.setTint(drawable, color); + if (drawable != null) { + Drawable wrappedDrawable = DrawableCompat.wrap(drawable).mutate(); - return drawable; + DrawableCompat.setTintMode(wrappedDrawable, PorterDuff.Mode.SRC_IN); + DrawableCompat.setTint(wrappedDrawable, color); + return wrappedDrawable; + } + return null; } + public static Drawable getVectorDrawable(@NonNull Context context, @DrawableRes int resId) { - return getVectorDrawable(context.getResources(), resId, context.getTheme()); + return AppCompatResources.getDrawable(context, resId); } - public static Drawable resolveDrawable(@NonNull Context context, @AttrRes int drawableAttr) { TypedArray a = context.obtainStyledAttributes(new int[]{drawableAttr}); Drawable drawable = a.getDrawable(0); 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 05f6f8ae..8d62cbbf 100644 --- a/app/src/main/java/org/adrianvictor/geleia/util/MusicUtil.java +++ b/app/src/main/java/org/adrianvictor/geleia/util/MusicUtil.java @@ -3,6 +3,7 @@ package org.adrianvictor.geleia.util; import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.os.Build; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; @@ -82,7 +83,8 @@ public class MusicUtil { public static String getFileUri(Song song) { String location = PreferenceUtil.getInstance(App.getInstance()).getLocationDownload(); File root = new File(location, "music"); - if (!location.equals(App.getInstance().getCacheDir().toString())) { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !location.equals(App.getInstance().getCacheDir().toString())) { Uri uri = Uri.parse(location); return new File(uri.getPath()).getAbsolutePath(); } diff --git a/app/src/main/java/org/adrianvictor/geleia/views/IconImageView.java b/app/src/main/java/org/adrianvictor/geleia/views/IconImageView.java index 557797cf..6af3adc6 100644 --- a/app/src/main/java/org/adrianvictor/geleia/views/IconImageView.java +++ b/app/src/main/java/org/adrianvictor/geleia/views/IconImageView.java @@ -10,23 +10,27 @@ import org.adrianvictor.geleia.util.ThemeUtil; import org.adrianvictor.geleia.R; public class IconImageView extends AppCompatImageView { + public IconImageView(Context context) { super(context); - init(context); } public IconImageView(Context context, AttributeSet attrs) { super(context, attrs); - init(context); } public IconImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - init(context); } - private void init(Context context) { - if (context == null) return; - setColorFilter(ThemeUtil.getColorResource(context, R.attr.iconColor), PorterDuff.Mode.SRC_IN); + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + init(); + } + + private void init() { + if (getContext() == null) return; + setColorFilter(ThemeUtil.getColorResource(getContext(), R.attr.iconColor), PorterDuff.Mode.SRC_IN); } } diff --git a/app/src/main/res/drawable/ic_launcher_nodpi.xml b/app/src/main/res/drawable/ic_launcher_nodpi.xml index 1361edfa..a8b409b1 100644 --- a/app/src/main/res/drawable/ic_launcher_nodpi.xml +++ b/app/src/main/res/drawable/ic_launcher_nodpi.xml @@ -1,14 +1,4 @@ - - - - - - - - - - + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index a807a132..61ff6cc1 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -19,134 +19,122 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> - - - - - - - - - - - - - - - - - - - - - - - + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"> + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/card_server.xml b/app/src/main/res/layout/card_server.xml index 09819218..bb7fcde2 100644 --- a/app/src/main/res/layout/card_server.xml +++ b/app/src/main/res/layout/card_server.xml @@ -5,7 +5,6 @@ android:layout_width="match_parent" android:layout_height="100dp" android:layout_margin="20dp" - android:background="@drawable/card_server" android:elevation="4dp" tools:ignore="UnusedAttribute"> diff --git a/app/src/main/res/layout/notification_download.xml b/app/src/main/res/layout/notification_download.xml new file mode 100644 index 00000000..3b2740bf --- /dev/null +++ b/app/src/main/res/layout/notification_download.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 036d09bc..00000000 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png index c455e356..2db960bf 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png index 1d5f0a3b..f995d681 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png index e8c79820..81b597fe 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index b00f5a28..e258b0af 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index f9b1b953..fc01141f 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2a276e58..bf686d10 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -245,4 +245,11 @@ GNU General Public License v3.0 GNU General Public License v3.0 + Downloads + Downloading songs + + Downloading %d song + Downloading %d songs + +