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
+
+