diff --git a/.gitignore b/.gitignore index 675d3b0ad..b41eb1521 100644 --- a/.gitignore +++ b/.gitignore @@ -1,44 +1,75 @@ -app/src/main/res/values/com_crashlytics_export_strings.xml -app/src/main/assets/crashlytics-build.properties -#app/crashlytics.properties - -# built application files +### Android ### +# Built application files *.apk *.ap_ -# files for the dex VM +# Files for the ART/Dalvik VM *.dex # Java class files *.class -# generated files +# Generated files bin/ gen/ -target/ - -# external libraries -#libs/ +out/ -# gradle files +# Gradle files .gradle/ build/ # Local configuration file (sdk path, etc) local.properties -.gitattributes -# Eclipse project files -.classpath -.project +# Proguard folder generated by Eclipse +proguard/ -# Android Studio -*.iml -.idea -ajcore.*.txt +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder captures/ -# Mac -.DS_Store +# Intellij +*.iml +.idea/ + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +### Android Patch ### +gen-external-apklibs + +### fastlane ### +# fastlane - A streamlined workflow tool for Cocoa deployment +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +# fastlane specific +fastlane/report.xml + +# deliver temporary files +fastlane/Preview.html + +# snapshot generated screenshots +fastlane/screenshots/**/*.png +fastlane/screenshots/screenshots.html + +# scan temporary files +fastlane/test_output + +# Google Play files +fastlane/metadata/ -app/release/ +Gemfile.lock diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..7a118b49b --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem "fastlane" diff --git a/README.md b/README.md index b7fc3cc0c..6afd7fa9f 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,35 @@ -# Phonograph +# Phonograph ([Forked from kabouzeid](https://github.com/kabouzeid/Phonograph)) + [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://github.com/kabouzeid/Phonograph/blob/master/LICENSE.txt) **A material designed local music player for Android.** -![Screenshots](./art/art.jpg?raw=true) +## About this fork +The purpose of this fork is to implement an easy to use and powerful way to manage your playlists. The goals are: +- [x] See whether the selected song is already in a playlist +- [x] See whether multiple songs are already in a playlist +- [x] Do not add songs to playlist which would become duplicates +- [ ] Material Design for the checkmark +- [ ] Be able to remove a song which is already in a playlist +- [ ] Option to remove duplicate songs from existing playlists +- [ ] Sync playlists (with other devices, using textfiles in Google Drive) + + +![image](https://user-images.githubusercontent.com/33571916/52509049-01e1c300-2bf7-11e9-979a-8cf872436ec2.png) + +In the playlist-menu checkmarks indicate whether a song / a number of songs are already in a list. +The brackets around the checkmark show that only some of the songs are part of the playlist. - - Get it on Google Play - +This fork is only different from kabouzeid/master in these commits: +[6bac337](https://github.com/Sogolumbo/Phonograph/commit/6bac3379636d97a68f50ebb1672654ef1aa310fb), +[d91f11a](https://github.com/Sogolumbo/Phonograph/commit/d91f11ad068192806979da79a0d089835d574524), +[e81f655](https://github.com/Sogolumbo/Phonograph/commit/e81f655c802bb2953d6e6d093cc3a1c774b897c4), +[8254527](https://github.com/Sogolumbo/Phonograph/commit/8254527339ba7e8acd5cc522b34e3ee724ba9b5a), +[4512b43](https://github.com/Sogolumbo/Phonograph/commit/4512b43529231a57636b7d62fcbade9fb81329b9). +## Contributing +I love any support, feedback or new ideas. So feel free to contribute in any possible way. I don't have any experience with android programming so I really need help D: +### Getting the App for non-Programmers +If you want the app but don't know how to build it feel free to send me a personal message. We will find a solution :) diff --git a/app/build.gradle b/app/build.gradle index 091bf6ff1..3d19b2fbf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,23 +1,6 @@ -buildscript { - repositories { - maven { url 'https://maven.fabric.io/public' } - mavenCentral() - } - - dependencies { - //noinspection GradleDynamicVersion - classpath 'io.fabric.tools:gradle:1.+' - classpath 'com.jakewharton.hugo:hugo-plugin:1.2.1' - } -} +import javax.annotation.Nullable apply plugin: 'com.android.application' -apply plugin: 'io.fabric' -apply plugin: 'com.jakewharton.hugo' - -repositories { - maven { url 'https://maven.fabric.io/public' } -} static def gitBranch() { def branch = 'GitHub' @@ -37,34 +20,52 @@ static def gitBranch() { branch } -android { - compileSdkVersion 27 - buildToolsVersion '27.0.3' - dataBinding { - enabled = true +def getProperties(String fileName) { + final Properties properties = new Properties() + def file = file(fileName) + if (file.exists()) { + file.withInputStream { stream -> properties.load(stream) } } + return properties +} + +static def getProperty(@Nullable Properties properties, String name) { + return properties.getProperty(name) ?: "$name missing" +} + +android { + compileSdkVersion 28 + buildToolsVersion '28.0.3' defaultConfig { minSdkVersion 16 - targetSdkVersion 27 + targetSdkVersion 28 - renderscriptTargetApi 27 + renderscriptTargetApi 28 vectorDrawables.useSupportLibrary = true applicationId 'com.kabouzeid.gramophone' - versionCode 151 - versionName '0.16.6' + versionCode 175 + versionName '1.3.1' + } + signingConfigs { + release { + Properties properties = getProperties('/Users/karim/Documents/Phonograph/privatekeys/keystore.properties') + storeFile file(getProperty(properties, 'storeFile')) + keyAlias getProperty(properties, 'keyAlias') + storePassword getProperty(properties, 'storePassword') + keyPassword getProperty(properties, 'keyPassword') + } } buildTypes { release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.release } debug { applicationIdSuffix '.debug' versionNameSuffix ' DEBUG (' + gitBranch() + ')' - - ext.enableCrashlytics = false // Disable fabric build ID generation for debug builds } } packagingOptions { @@ -80,56 +81,58 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } -} - -ext { - supportLibVersion = '27.0.2' + // Required for IntroActivity + dataBinding { + enabled = true + } } dependencies { - implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation "com.android.support:support-compat:$supportLibVersion" - implementation "com.android.support:support-core-utils:$supportLibVersion" - implementation "com.android.support:support-core-ui:$supportLibVersion" - implementation "com.android.support:support-media-compat:$supportLibVersion" - implementation "com.android.support:support-fragment:$supportLibVersion" - implementation "com.android.support:support-v13:$supportLibVersion" - implementation "com.android.support:appcompat-v7:$supportLibVersion" - implementation "com.android.support:recyclerview-v7:$supportLibVersion" - implementation "com.android.support:gridlayout-v7:$supportLibVersion" - implementation "com.android.support:cardview-v7:$supportLibVersion" - implementation "com.android.support:palette-v7:$supportLibVersion" - implementation "com.android.support:design:$supportLibVersion" - implementation "com.android.support:support-annotations:$supportLibVersion" - implementation "com.android.support:percent:$supportLibVersion" - implementation "com.android.support:preference-v7:$supportLibVersion" - implementation "com.android.support:preference-v14:$supportLibVersion" - implementation 'com.github.kabouzeid:app-theme-helper:1.3.7' + implementation 'androidx.core:core:1.0.2' + implementation 'androidx.legacy:legacy-support-core-utils:1.0.0' + implementation 'androidx.legacy:legacy-support-core-ui:1.0.0' + implementation 'androidx.media:media:1.0.1' + implementation 'androidx.fragment:fragment:1.0.0' + implementation 'androidx.legacy:legacy-support-v13:1.0.0' + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.recyclerview:recyclerview:1.0.0' + implementation 'androidx.gridlayout:gridlayout:1.0.0' + implementation 'androidx.cardview:cardview:1.0.0' + implementation 'androidx.palette:palette:1.0.0' + implementation 'com.google.android.material:material:1.0.0' + implementation 'androidx.annotation:annotation:1.0.2' + implementation 'androidx.percentlayout:percentlayout:1.0.0' + implementation 'androidx.preference:preference:1.0.0' + implementation 'androidx.legacy:legacy-preference-v14:1.0.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + + implementation 'com.github.kabouzeid:app-theme-helper:1.3.10' implementation 'com.github.kabouzeid:RecyclerView-FastScroll:1.0.16-kmod' implementation 'com.github.kabouzeid:SeekArc:1.2-kmod' - implementation 'com.github.kabouzeid:AndroidSlidingUpPanel:3.3.0-kmod3' + implementation 'com.github.kabouzeid:AndroidSlidingUpPanel:3.3.3-kmod' implementation 'com.afollestad.material-dialogs:core:0.9.6.0' implementation 'com.afollestad.material-dialogs:commons:0.9.6.0' implementation 'com.afollestad:material-cab:0.1.12' implementation 'com.github.ksoichiro:android-observablescrollview:1.6.0' - implementation 'com.squareup.retrofit2:retrofit:2.3.0' - implementation 'com.squareup.retrofit2:converter-gson:2.3.0' - implementation 'com.anjlab.android.iab.v3:library:1.0.44' - implementation 'de.psdev.licensesdialog:licensesdialog:1.8.3' - implementation 'com.github.bumptech.glide:glide:3.8.0' - implementation 'com.github.bumptech.glide:okhttp3-integration:1.5.0' - implementation 'com.heinrichreimersoftware:material-intro:1.6.2' + implementation 'com.heinrichreimersoftware:material-intro:1.6' implementation 'me.zhanghai.android.materialprogressbar:library:1.4.2' - implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5' - implementation 'com.jakewharton:butterknife:8.8.1' implementation('com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:0.11.0@aar') { transitive = true } - implementation 'com.google.code.gson:gson:2.8.2' - implementation 'com.crashlytics.sdk.android:crashlytics:2.8.0' - implementation('com.crashlytics.sdk.android:answers:1.3.13@aar') { - transitive = true - } - testImplementation 'junit:junit:4.12' - annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' + + implementation 'com.jakewharton:butterknife:10.1.0' + annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0' + + implementation 'com.squareup.retrofit2:retrofit:2.5.0' + implementation 'com.squareup.retrofit2:converter-gson:2.5.0' + implementation 'com.google.code.gson:gson:2.8.5' + + implementation 'com.anjlab.android.iab.v3:library:1.0.44' + implementation 'de.psdev.licensesdialog:licensesdialog:2.0.0' + + implementation 'com.github.bumptech.glide:glide:3.8.0' + implementation 'com.github.bumptech.glide:okhttp3-integration:1.5.0' + implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5' + + implementation 'com.github.AdrienPoupa:jaudiotagger:2.2.3' } diff --git a/app/crashlytics.properties b/app/crashlytics.properties deleted file mode 100644 index 094c4eba2..000000000 --- a/app/crashlytics.properties +++ /dev/null @@ -1,3 +0,0 @@ -#Contains API Secret used to validate your application. Commit to internal source control; avoid making secret public. -#Thu Dec 04 21:13:29 CET 2014 -apiSecret=9ac7e05ca7b1630cfd18de098d7ed2db2a8904d1eed9d1569d4e5edf0f740aac diff --git a/app/libs/jaudiotagger-android-2.2.3.jar b/app/libs/jaudiotagger-android-2.2.3.jar deleted file mode 100644 index 11a46d34a..000000000 Binary files a/app/libs/jaudiotagger-android-2.2.3.jar and /dev/null differ diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index ee0e6965c..3500b56c6 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -16,6 +16,9 @@ # public *; #} +-dontwarn +-ignorewarnings + # RetroFit -dontwarn retrofit.** -keep class retrofit.** { *; } @@ -41,6 +44,3 @@ } -keep class !android.support.v7.internal.view.menu.**,** {*;} - --dontwarn --ignorewarnings \ No newline at end of file diff --git a/app/src/androidTest/java/com/kabouzeid/gramophone/ApplicationTest.java b/app/src/androidTest/java/com/kabouzeid/gramophone/ApplicationTest.java deleted file mode 100644 index 57e2b4d6c..000000000 --- a/app/src/androidTest/java/com/kabouzeid/gramophone/ApplicationTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.kabouzeid.gramophone; - -import android.app.Application; -import android.test.ApplicationTestCase; - -/** - * Testing Fundamentals - */ -public class ApplicationTest extends ApplicationTestCase { - public ApplicationTest() { - super(Application.class); - } -} \ No newline at end of file diff --git a/app/src/debug/res/values/donottranslate.xml b/app/src/debug/res/values/donottranslate.xml index 423038e19..d97de35cd 100644 --- a/app/src/debug/res/values/donottranslate.xml +++ b/app/src/debug/res/values/donottranslate.xml @@ -1,4 +1,4 @@ - Phonograph DEBUG + Phonograph \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 44e43518f..ee7d2d366 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ - + @@ -12,13 +12,14 @@ + @@ -121,6 +122,7 @@ android:name=".appshortcuts.AppShortcutLauncherActivity" android:launchMode="singleInstance" android:theme="@android:style/Theme.Translucent.NoTitleBar" /> + - @@ -204,15 +203,14 @@ android:resource="@xml/app_widget_card_info" /> - + android:resource="@xml/provider_paths" /> diff --git a/app/src/main/assets/phonograph-changelog.html b/app/src/main/assets/phonograph-changelog.html index fde3331b7..c975e6a14 100644 --- a/app/src/main/assets/phonograph-changelog.html +++ b/app/src/main/assets/phonograph-changelog.html @@ -27,6 +27,87 @@

You can view the changelog dialog again at any time from the about section.

+

Version 1.3.1

+
    +
  1. NEW: Artist image mosaics
  2. +
  3. FIX: Ringtone sharing
  4. +
+ +

Version 1.3.0

+
    +
  1. NEW: Show more information of songs, albums and artists
  2. +
  3. NEW: Option to finish last song in sleep timer
  4. +
  5. NEW: Support for Android Pie
  6. +
  7. NEW: Option for last 7 days for recently played
  8. +
  9. FIX: Artist images not loading
  10. +
  11. IMPROVEMENT: Sync translations
  12. +
+ +

Version 1.2.0

+
    +
  1. NEW:Support for devices with a notch
  2. +
+ +

Version 1.1.2

+
    +
  1. FIX: Crash with custom artist images
  2. +
+ +

Version 1.1.1

+
    +
  1. NEW: Preference to turn off shuffle mode when selecting new list of songs
  2. +
  3. FIX: Fix app intro crash
  4. +
  5. FIX: Fix a crash for some artist names which contain special characters
  6. +
  7. FIX: Fix loading of very large embedded album art
  8. +
+ +

Version 1.1.0

+
    +
  1. NEW: Select all items in a list
  2. +
  3. NEW: Export multiple playlists at once
  4. +
  5. FIX: Show unknown year consistently everywhere
  6. +
  7. FIX: Also look for png album covers in the folder
  8. +
  9. IMPROVEMENT: Updated translations
  10. +
+ +

Version 1.0.1

+
    +
  1. FIX: Broken layout for super long artist names
  2. +
  3. FIX: Show "-" instead of "0" when the album year is not available
  4. +
  5. FIX: Show "Unknown Artist" when the artist name is unknown
  6. +
  7. FIX: Navigation bar button colors for light themes
  8. +
  9. IMPROVEMENT: Updated translations
  10. +
+ +

Version 1.0.0

+

This is it, Phonograph 1.0.0! We are finally out of beta, therefore many bugs were fixed and the + album and artist pages received a brand new look.

+
    +
  1. NEW: Album page redesign
  2. +
  3. NEW: Artist page redesign
  4. +
  5. NEW: Custom sort orders for titles, albums and artists
  6. +
  7. NEW: Rescan media from the navigation drawer
  8. +
  9. IMPROVEMENT: Updated translations
  10. +
  11. FIX: Fixed many bugs and crashes
  12. +
+ +

Version 0.16.9

+
    +
  1. FIX: Crash on start for some users
  2. +
+ +

Version 0.16.8

+
    +
  1. IMPROVEMENT: Updated translations
  2. +
  3. OTHER: Removed all analytics
  4. +
+ +

Version 0.16.7

+
    +
  1. IMPROVEMENT: Updated translations
  2. +
  3. IMPROVEMENT: Minor improvements
  4. +
+

Version 0.16.6

  1. FIX: Broken dot animation in Playing Screen Preference
  2. diff --git a/app/src/main/java/com/kabouzeid/gramophone/App.java b/app/src/main/java/com/kabouzeid/gramophone/App.java index 7e0b8e1e6..f58bbd886 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/App.java +++ b/app/src/main/java/com/kabouzeid/gramophone/App.java @@ -3,21 +3,18 @@ import android.app.Application; import android.os.Build; +import androidx.annotation.NonNull; + import com.anjlab.android.iab.v3.BillingProcessor; import com.anjlab.android.iab.v3.TransactionDetails; -import com.crashlytics.android.Crashlytics; -import com.crashlytics.android.answers.Answers; -import com.crashlytics.android.core.CrashlyticsCore; import com.kabouzeid.appthemehelper.ThemeStore; import com.kabouzeid.gramophone.appshortcuts.DynamicShortcutManager; -import io.fabric.sdk.android.Fabric; /** * @author Karim Abou Zeid (kabouzeid) */ public class App extends Application { - public static final String TAG = App.class.getSimpleName(); public static final String GOOGLE_PLAY_LICENSE_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjMeADN5Ffnt/ml5SYxNPCn8kGcOYGpHEfNSCts99vVxqmCn6C01E94c17j7rUK2aeHur5uxphZylzopPlQ8P8l1fqty0GPUNRSo18FCJzfGH8HZAwZYOcnRFPaXdaq3InyFJhBiODh2oeAcVK/idH6QraQ4r9HIlzigAg6lgwzxl2wJKDh7X/GMdDntCyzDh8xDQ0wIawFgvgojHwqh2Ci8Gnq6EYRwPA9yHiIIksT8Q30QyM5ewl5QcnWepsls7enNqeHarhpmSibRUDgCsxHoOpny7SyuvZvUI3wuLckDR0ds9hrt614scHHqDOBp/qWCZiAgOPVAEQcURbV09qQIDAQAB"; public static final String PRO_VERSION_PRODUCT_ID = "pro_version"; @@ -34,22 +31,11 @@ public void onCreate() { // default theme if (!ThemeStore.isConfigured(this, 1)) { ThemeStore.editTheme(this) - .activityTheme(R.style.Theme_Phonograph_Light) .primaryColorRes(R.color.md_indigo_500) .accentColorRes(R.color.md_pink_A400) .commit(); } - // Set up Crashlytics, disabled for debug builds - Crashlytics crashlyticsKit = new Crashlytics.Builder() - .core(new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()) - .build(); - if (!BuildConfig.DEBUG) { - Fabric.with(this, crashlyticsKit, new Answers()); - } else { - Fabric.with(this, crashlyticsKit); // crashlytics kit is disabled here - } - // Set up dynamic shortcuts if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { new DynamicShortcutManager(this).initDynamicShortcuts(); @@ -58,7 +44,7 @@ public void onCreate() { // automatically restores purchases billingProcessor = new BillingProcessor(this, App.GOOGLE_PLAY_LICENSE_KEY, new BillingProcessor.IBillingHandler() { @Override - public void onProductPurchased(String productId, TransactionDetails details) { + public void onProductPurchased(@NonNull String productId, TransactionDetails details) { } @Override diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/AlbumCoverPagerAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/AlbumCoverPagerAdapter.java index bcac0e455..b0690df99 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/AlbumCoverPagerAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/AlbumCoverPagerAdapter.java @@ -2,8 +2,9 @@ import android.content.SharedPreferences; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -18,6 +19,7 @@ import com.kabouzeid.gramophone.util.PreferenceUtil; import java.util.ArrayList; +import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; @@ -27,14 +29,13 @@ * @author Karim Abou Zeid (kabouzeid) */ public class AlbumCoverPagerAdapter extends CustomFragmentStatePagerAdapter { - public static final String TAG = AlbumCoverPagerAdapter.class.getSimpleName(); - private ArrayList dataSet; + private List dataSet; private AlbumCoverFragment.ColorReceiver currentColorReceiver; private int currentColorReceiverPosition = -1; - public AlbumCoverPagerAdapter(FragmentManager fm, ArrayList dataSet) { + public AlbumCoverPagerAdapter(FragmentManager fm, List dataSet) { super(fm); this.dataSet = dataSet; } @@ -50,6 +51,7 @@ public int getCount() { } @Override + @NonNull public Object instantiateItem(ViewGroup container, int position) { Object o = super.instantiateItem(container, position); if (currentColorReceiver != null && currentColorReceiverPosition == position) { @@ -102,15 +104,14 @@ public void onCreate(final Bundle savedInstanceState) { } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_album_cover, container, false); unbinder = ButterKnife.bind(this, view); return view; } @Override - public void onViewCreated(View view, Bundle savedInstanceState) { + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); forceSquareAlbumCover(false); // TODO diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/CategoryInfoAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/CategoryInfoAdapter.java index 7dead410b..baeb95937 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/CategoryInfoAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/CategoryInfoAdapter.java @@ -1,8 +1,9 @@ package com.kabouzeid.gramophone.adapter; import android.annotation.SuppressLint; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.helper.ItemTouchHelper; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.ItemTouchHelper; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -16,26 +17,28 @@ import com.kabouzeid.gramophone.util.SwipeAndDragHelper; import java.util.ArrayList; +import java.util.List; public class CategoryInfoAdapter extends RecyclerView.Adapter implements SwipeAndDragHelper.ActionCompletionContract { - private ArrayList categoryInfos; + private List categoryInfos; private ItemTouchHelper touchHelper; - public CategoryInfoAdapter(ArrayList categoryInfos) { + public CategoryInfoAdapter(List categoryInfos) { this.categoryInfos = categoryInfos; SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper(this); touchHelper = new ItemTouchHelper(swipeAndDragHelper); } @Override - public CategoryInfoAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + @NonNull + public CategoryInfoAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.preference_dialog_library_categories_listitem, parent, false); return new ViewHolder(view); } @SuppressLint("ClickableViewAccessibility") @Override - public void onBindViewHolder(CategoryInfoAdapter.ViewHolder holder, int position) { + public void onBindViewHolder(@NonNull CategoryInfoAdapter.ViewHolder holder, int position) { CategoryInfo categoryInfo = categoryInfos.get(position); holder.checkBox.setChecked(categoryInfo.visible); @@ -64,8 +67,7 @@ public int getItemCount() { return categoryInfos.size(); } - - public void setCategoryInfos(ArrayList categoryInfos) { + public void setCategoryInfos(List categoryInfos) { this.categoryInfos = categoryInfos; notifyDataSetChanged(); } @@ -82,7 +84,7 @@ public void attachToRecyclerView(RecyclerView recyclerView) { touchHelper.attachToRecyclerView(recyclerView); } - public ArrayList getCategoryInfos() { + public List getCategoryInfos() { return categoryInfos; } diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/GenreAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/GenreAdapter.java index d21f670f7..53f10d1a0 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/GenreAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/GenreAdapter.java @@ -1,9 +1,9 @@ package com.kabouzeid.gramophone.adapter; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -15,27 +15,26 @@ import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; import java.util.ArrayList; +import java.util.List; public class GenreAdapter extends RecyclerView.Adapter implements FastScrollRecyclerView.SectionedAdapter { - public static final String TAG = GenreAdapter.class.getSimpleName(); - @NonNull private final AppCompatActivity activity; - private ArrayList dataSet; + private List dataSet; private int itemLayoutRes; - public GenreAdapter(@NonNull AppCompatActivity activity, ArrayList dataSet, @LayoutRes int itemLayoutRes) { + public GenreAdapter(@NonNull AppCompatActivity activity, List dataSet, @LayoutRes int itemLayoutRes) { this.activity = activity; this.dataSet = dataSet; this.itemLayoutRes = itemLayoutRes; } - public ArrayList getDataSet() { + public List getDataSet() { return dataSet; } - public void swapDataSet(ArrayList dataSet) { + public void swapDataSet(List dataSet) { this.dataSet = dataSet; notifyDataSetChanged(); } @@ -47,7 +46,7 @@ public long getItemId(int position) { @NonNull @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false); return new ViewHolder(view); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/MusicLibraryPagerAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/MusicLibraryPagerAdapter.java index 9929a0c66..6c9b45f69 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/MusicLibraryPagerAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/MusicLibraryPagerAdapter.java @@ -2,10 +2,10 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; import android.util.SparseArray; import android.view.ViewGroup; @@ -38,7 +38,7 @@ public MusicLibraryPagerAdapter(@NonNull final Context context, final FragmentMa setCategoryInfos(PreferenceUtil.getInstance(context).getLibraryCategoryInfos()); } - public void setCategoryInfos(@NonNull ArrayList categoryInfos) { + public void setCategoryInfos(@NonNull List categoryInfos) { mHolderList.clear(); for (CategoryInfo categoryInfo : categoryInfos) { @@ -165,9 +165,9 @@ public Class getFragmentClass() { public static MusicFragments of(Class cl) { MusicFragments[] fragments = All.FRAGMENTS; - for (int i = 0; i < fragments.length; i++) { - if (cl.equals(fragments[i].mFragmentClass)) - return fragments[i]; + for (MusicFragments fragment : fragments) { + if (cl.equals(fragment.mFragmentClass)) + return fragment; } throw new IllegalArgumentException("Unknown music fragment " + cl); diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/PlaylistAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/PlaylistAdapter.java index c37b966e5..01127601d 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/PlaylistAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/PlaylistAdapter.java @@ -1,18 +1,21 @@ package com.kabouzeid.gramophone.adapter; +import android.content.Context; import android.graphics.PorterDuff; import android.os.Build; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.PopupMenu; +import android.widget.Toast; import com.kabouzeid.appthemehelper.util.ATHUtil; +import com.kabouzeid.gramophone.App; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.adapter.base.AbsMultiSelectAdapter; import com.kabouzeid.gramophone.adapter.base.MediaEntryViewHolder; @@ -22,6 +25,7 @@ import com.kabouzeid.gramophone.helper.menu.SongsMenuHelper; import com.kabouzeid.gramophone.interfaces.CabHolder; import com.kabouzeid.gramophone.loader.PlaylistSongLoader; +import com.kabouzeid.gramophone.misc.WeakContextAsyncTask; import com.kabouzeid.gramophone.model.AbsCustomPlaylist; import com.kabouzeid.gramophone.model.Playlist; import com.kabouzeid.gramophone.model.Song; @@ -29,7 +33,9 @@ import com.kabouzeid.gramophone.model.smartplaylist.LastAddedPlaylist; import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.NavigationUtil; +import com.kabouzeid.gramophone.util.PlaylistsUtil; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -38,16 +44,14 @@ */ public class PlaylistAdapter extends AbsMultiSelectAdapter { - public static final String TAG = PlaylistAdapter.class.getSimpleName(); - private static final int SMART_PLAYLIST = 0; private static final int DEFAULT_PLAYLIST = 1; protected final AppCompatActivity activity; - protected ArrayList dataSet; + protected List dataSet; protected int itemLayoutRes; - public PlaylistAdapter(AppCompatActivity activity, ArrayList dataSet, @LayoutRes int itemLayoutRes, @Nullable CabHolder cabHolder) { + public PlaylistAdapter(AppCompatActivity activity, List dataSet, @LayoutRes int itemLayoutRes, @Nullable CabHolder cabHolder) { super(activity, cabHolder, R.menu.menu_playlists_selection); this.activity = activity; this.dataSet = dataSet; @@ -55,11 +59,11 @@ public PlaylistAdapter(AppCompatActivity activity, ArrayList dataSet, setHasStableIds(true); } - public ArrayList getDataSet() { + public List getDataSet() { return dataSet; } - public void swapDataSet(ArrayList dataSet) { + public void swapDataSet(List dataSet) { this.dataSet = dataSet; notifyDataSetChanged(); } @@ -69,9 +73,9 @@ public long getItemId(int position) { return dataSet.get(position).id; } - @NonNull @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false); return createViewHolder(view, viewType); } @@ -133,7 +137,7 @@ protected String getName(Playlist playlist) { } @Override - protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList selection) { + protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull List selection) { switch (menuItem.getItemId()) { case R.id.action_delete_playlist: for (int i = 0; i < selection.size(); i++) { @@ -149,15 +153,59 @@ protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayLi DeletePlaylistDialog.create(selection).show(activity.getSupportFragmentManager(), "DELETE_PLAYLIST"); } break; + case R.id.action_save_playlist: + if (selection.size() == 1) { + PlaylistMenuHelper.handleMenuClick(activity, selection.get(0), menuItem); + } else { + new SavePlaylistsAsyncTask(activity).execute(selection); + } + break; default: SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.getItemId()); break; } } + private static class SavePlaylistsAsyncTask extends WeakContextAsyncTask, String, String> { + public SavePlaylistsAsyncTask(Context context) { + super(context); + } + + @Override + protected String doInBackground(List... params) { + int successes = 0; + int failures = 0; + + String dir = ""; + + for (Playlist playlist : params[0]) { + try { + dir = PlaylistsUtil.savePlaylist(App.getInstance().getApplicationContext(), playlist).getParent(); + successes++; + } catch (IOException e) { + failures++; + e.printStackTrace(); + } + } + + return failures == 0 + ? String.format(App.getInstance().getApplicationContext().getString(R.string.saved_x_playlists_to_x), successes, dir) + : String.format(App.getInstance().getApplicationContext().getString(R.string.saved_x_playlists_to_x_failed_to_save_x), successes, dir, failures); + } + + @Override + protected void onPostExecute(String string) { + super.onPostExecute(string); + Context context = getContext(); + if (context != null) { + Toast.makeText(context, string, Toast.LENGTH_LONG).show(); + } + } + } + @NonNull - private ArrayList getSongList(@NonNull List playlists) { - final ArrayList songs = new ArrayList<>(); + private List getSongList(@NonNull List playlists) { + final List songs = new ArrayList<>(); for (Playlist playlist : playlists) { if (playlist instanceof AbsCustomPlaylist) { songs.addAll(((AbsCustomPlaylist) playlist).getSongs(activity)); diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/SearchAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/SearchAdapter.java index 225423bf6..d8bc49478 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/SearchAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/SearchAdapter.java @@ -1,10 +1,10 @@ package com.kabouzeid.gramophone.adapter; import android.os.Build; -import android.support.annotation.NonNull; -import android.support.v4.util.Pair; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.NonNull; +import androidx.core.util.Pair; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -57,9 +57,9 @@ public int getItemViewType(int position) { return HEADER; } - @NonNull @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { if (viewType == HEADER) return new ViewHolder(LayoutInflater.from(activity).inflate(R.layout.sub_header, parent, false), viewType); return new ViewHolder(LayoutInflater.from(activity).inflate(R.layout.item_list, parent, false), viewType); @@ -72,7 +72,7 @@ public void onBindViewHolder(@NonNull final ViewHolder holder, int position) { case ALBUM: final Album album = (Album) dataSet.get(position); holder.title.setText(album.getTitle()); - holder.text.setText(album.getArtistName()); + holder.text.setText(MusicUtil.getAlbumInfoString(activity, album)); SongGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong()) .checkIgnoreMediaStore(activity).build() .into(holder.image); @@ -87,7 +87,7 @@ public void onBindViewHolder(@NonNull final ViewHolder holder, int position) { case SONG: final Song song = (Song) dataSet.get(position); holder.title.setText(song.title); - holder.text.setText(song.albumName); + holder.text.setText(MusicUtil.getSongInfoString(song)); break; default: holder.title.setText(dataSet.get(position).toString()); @@ -164,7 +164,7 @@ public void onClick(View view) { )); break; case SONG: - ArrayList playList = new ArrayList<>(); + List playList = new ArrayList<>(); playList.add((Song) item); MusicPlayerRemote.openQueue(playList, 0, true); break; diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/SongFileAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/SongFileAdapter.java index 2745423fb..a067e478f 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/SongFileAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/SongFileAdapter.java @@ -2,10 +2,10 @@ import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -20,7 +20,7 @@ import com.kabouzeid.gramophone.adapter.base.MediaEntryViewHolder; import com.kabouzeid.gramophone.glide.audiocover.AudioFileCover; import com.kabouzeid.gramophone.interfaces.CabHolder; -import com.kabouzeid.gramophone.util.Util; +import com.kabouzeid.gramophone.util.ImageUtil; import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; import java.io.File; @@ -64,12 +64,13 @@ public void swapDataSet(@NonNull List songFiles) { } @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new ViewHolder(LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)); } @Override - public void onBindViewHolder(final ViewHolder holder, int index) { + public void onBindViewHolder(@NonNull ViewHolder holder, int index) { final File file = dataSet.get(index); holder.itemView.setActivated(isChecked(file)); @@ -115,7 +116,7 @@ protected void loadFileImage(File file, final ViewHolder holder) { holder.image.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN); holder.image.setImageResource(R.drawable.ic_folder_white_24dp); } else { - Drawable error = Util.getTintedVectorDrawable(activity, R.drawable.ic_file_music_white_24dp, iconColor); + Drawable error = ImageUtil.getTintedVectorDrawable(activity, R.drawable.ic_file_music_white_24dp, iconColor); Glide.with(activity) .load(new AudioFileCover(file.getPath())) .diskCacheStrategy(DiskCacheStrategy.NONE) @@ -150,7 +151,7 @@ protected String getName(File object) { } @Override - protected void onMultipleItemAction(MenuItem menuItem, ArrayList selection) { + protected void onMultipleItemAction(MenuItem menuItem, List selection) { if (callbacks == null) return; callbacks.onMultipleItemAction(menuItem, selection); } @@ -205,6 +206,6 @@ public interface Callbacks { void onFileMenuClicked(File file, View view); - void onMultipleItemAction(MenuItem item, ArrayList files); + void onMultipleItemAction(MenuItem item, List files); } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/album/AlbumAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/album/AlbumAdapter.java index dfb79b953..fc34217e1 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/album/AlbumAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/album/AlbumAdapter.java @@ -1,11 +1,11 @@ package com.kabouzeid.gramophone.adapter.album; import android.graphics.drawable.Drawable; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.util.Pair; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.util.Pair; +import androidx.appcompat.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -19,12 +19,15 @@ import com.kabouzeid.gramophone.adapter.base.MediaEntryViewHolder; import com.kabouzeid.gramophone.glide.PhonographColoredTarget; import com.kabouzeid.gramophone.glide.SongGlideRequest; +import com.kabouzeid.gramophone.helper.SortOrder; import com.kabouzeid.gramophone.helper.menu.SongsMenuHelper; import com.kabouzeid.gramophone.interfaces.CabHolder; import com.kabouzeid.gramophone.model.Album; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.NavigationUtil; +import com.kabouzeid.gramophone.util.PreferenceUtil; + import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; import java.util.ArrayList; @@ -35,16 +38,14 @@ */ public class AlbumAdapter extends AbsMultiSelectAdapter implements FastScrollRecyclerView.SectionedAdapter { - public static final String TAG = AlbumAdapter.class.getSimpleName(); - protected final AppCompatActivity activity; - protected ArrayList dataSet; + protected List dataSet; protected int itemLayoutRes; protected boolean usePalette = false; - public AlbumAdapter(@NonNull AppCompatActivity activity, ArrayList dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { + public AlbumAdapter(@NonNull AppCompatActivity activity, List dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { super(activity, cabHolder, R.menu.menu_media_selection); this.activity = activity; this.dataSet = dataSet; @@ -59,18 +60,18 @@ public void usePalette(boolean usePalette) { notifyDataSetChanged(); } - public void swapDataSet(ArrayList dataSet) { + public void swapDataSet(List dataSet) { this.dataSet = dataSet; notifyDataSetChanged(); } - public ArrayList getDataSet() { + public List getDataSet() { return dataSet; } - @NonNull @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false); return createViewHolder(view, viewType); } @@ -84,7 +85,10 @@ protected String getAlbumTitle(Album album) { } protected String getAlbumText(Album album) { - return album.getArtistName(); + return MusicUtil.buildInfoString( + album.getArtistName(), + MusicUtil.getSongCountString(activity, album.songs.size()) + ); } @Override @@ -170,13 +174,13 @@ protected String getName(Album album) { } @Override - protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList selection) { + protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull List selection) { SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.getItemId()); } @NonNull - private ArrayList getSongList(@NonNull List albums) { - final ArrayList songs = new ArrayList<>(); + private List getSongList(@NonNull List albums) { + final List songs = new ArrayList<>(); for (Album album : albums) { songs.addAll(album.songs); } @@ -186,7 +190,20 @@ private ArrayList getSongList(@NonNull List albums) { @NonNull @Override public String getSectionName(int position) { - return MusicUtil.getSectionName(dataSet.get(position).getTitle()); + @Nullable String sectionName = null; + switch (PreferenceUtil.getInstance(activity).getAlbumSortOrder()) { + case SortOrder.AlbumSortOrder.ALBUM_A_Z: + case SortOrder.AlbumSortOrder.ALBUM_Z_A: + sectionName = dataSet.get(position).getTitle(); + break; + case SortOrder.AlbumSortOrder.ALBUM_ARTIST: + sectionName = dataSet.get(position).getArtistName(); + break; + case SortOrder.AlbumSortOrder.ALBUM_YEAR: + return MusicUtil.getYearString(dataSet.get(position).getYear()); + } + + return MusicUtil.getSectionName(sectionName); } public class ViewHolder extends MediaEntryViewHolder { diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/album/HorizontalAlbumAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/album/HorizontalAlbumAdapter.java index 911bca19c..4bc42cf24 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/album/HorizontalAlbumAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/album/HorizontalAlbumAdapter.java @@ -1,10 +1,10 @@ package com.kabouzeid.gramophone.adapter.album; import android.graphics.drawable.Drawable; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.CardView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.cardview.widget.CardView; import android.view.View; import android.view.ViewGroup; @@ -16,16 +16,17 @@ import com.kabouzeid.gramophone.helper.HorizontalAdapterHelper; import com.kabouzeid.gramophone.interfaces.CabHolder; import com.kabouzeid.gramophone.model.Album; +import com.kabouzeid.gramophone.util.MusicUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ public class HorizontalAlbumAdapter extends AlbumAdapter { - public static final String TAG = AlbumAdapter.class.getSimpleName(); - public HorizontalAlbumAdapter(@NonNull AppCompatActivity activity, ArrayList dataSet, boolean usePalette, @Nullable CabHolder cabHolder) { + public HorizontalAlbumAdapter(@NonNull AppCompatActivity activity, List dataSet, boolean usePalette, @Nullable CabHolder cabHolder) { super(activity, dataSet, HorizontalAdapterHelper.LAYOUT_RES, usePalette, cabHolder); } @@ -76,11 +77,7 @@ public void onColorReady(int color) { @Override protected String getAlbumText(Album album) { - int year = album.getYear(); - if(year > 0) { - return String.valueOf(year); - } - return "-"; + return MusicUtil.getYearString(album.getYear()); } @Override diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/artist/ArtistAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/artist/ArtistAdapter.java index 89c69df9e..8c62dd633 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/artist/ArtistAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/artist/ArtistAdapter.java @@ -1,11 +1,11 @@ package com.kabouzeid.gramophone.adapter.artist; import android.graphics.drawable.Drawable; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.util.Pair; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.util.Pair; +import androidx.appcompat.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -19,12 +19,14 @@ import com.kabouzeid.gramophone.adapter.base.MediaEntryViewHolder; import com.kabouzeid.gramophone.glide.ArtistGlideRequest; import com.kabouzeid.gramophone.glide.PhonographColoredTarget; +import com.kabouzeid.gramophone.helper.SortOrder; import com.kabouzeid.gramophone.helper.menu.SongsMenuHelper; import com.kabouzeid.gramophone.interfaces.CabHolder; import com.kabouzeid.gramophone.model.Artist; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.NavigationUtil; +import com.kabouzeid.gramophone.util.PreferenceUtil; import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; import java.util.ArrayList; @@ -36,13 +38,13 @@ public class ArtistAdapter extends AbsMultiSelectAdapter implements FastScrollRecyclerView.SectionedAdapter { protected final AppCompatActivity activity; - protected ArrayList dataSet; + protected List dataSet; protected int itemLayoutRes; protected boolean usePalette = false; - public ArtistAdapter(@NonNull AppCompatActivity activity, ArrayList dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { + public ArtistAdapter(@NonNull AppCompatActivity activity, List dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { super(activity, cabHolder, R.menu.menu_media_selection); this.activity = activity; this.dataSet = dataSet; @@ -51,12 +53,12 @@ public ArtistAdapter(@NonNull AppCompatActivity activity, ArrayList data setHasStableIds(true); } - public void swapDataSet(ArrayList dataSet) { + public void swapDataSet(List dataSet) { this.dataSet = dataSet; notifyDataSetChanged(); } - public ArrayList getDataSet() { + public List getDataSet() { return dataSet; } @@ -71,7 +73,8 @@ public long getItemId(int position) { } @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false); return createViewHolder(view); } @@ -157,13 +160,13 @@ protected String getName(Artist artist) { } @Override - protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList selection) { + protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull List selection) { SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.getItemId()); } @NonNull - private ArrayList getSongList(@NonNull List artists) { - final ArrayList songs = new ArrayList<>(); + private List getSongList(@NonNull List artists) { + final List songs = new ArrayList<>(); for (Artist artist : artists) { songs.addAll(artist.getSongs()); // maybe async in future? } @@ -173,7 +176,15 @@ private ArrayList getSongList(@NonNull List artists) { @NonNull @Override public String getSectionName(int position) { - return MusicUtil.getSectionName(dataSet.get(position).getName()); + @Nullable String sectionName = null; + switch (PreferenceUtil.getInstance(activity).getArtistSortOrder()) { + case SortOrder.ArtistSortOrder.ARTIST_A_Z: + case SortOrder.ArtistSortOrder.ARTIST_Z_A: + sectionName = dataSet.get(position).getName(); + break; + } + + return MusicUtil.getSectionName(sectionName); } public class ViewHolder extends MediaEntryViewHolder { diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/base/AbsMultiSelectAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/base/AbsMultiSelectAdapter.java index f7e15b8d9..1ea151895 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/base/AbsMultiSelectAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/base/AbsMultiSelectAdapter.java @@ -1,9 +1,9 @@ package com.kabouzeid.gramophone.adapter.base; import android.content.Context; -import android.support.annotation.MenuRes; -import android.support.annotation.Nullable; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.MenuRes; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; import android.view.Menu; import android.view.MenuItem; @@ -12,6 +12,7 @@ import com.kabouzeid.gramophone.interfaces.CabHolder; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) @@ -20,7 +21,7 @@ public abstract class AbsMultiSelectAdapter checked; + private List checked; private int menuRes; private final Context context; @@ -31,37 +32,51 @@ public AbsMultiSelectAdapter(Context context, @Nullable CabHolder cabHolder, @Me this.context = context; } - protected void overrideMultiSelectMenuRes(@MenuRes int menuRes) { + protected void setMultiSelectMenuRes(@MenuRes int menuRes) { this.menuRes = menuRes; } protected boolean toggleChecked(final int position) { if (cabHolder != null) { - openCabIfNecessary(); - I identifier = getIdentifier(position); - if (!checked.remove(identifier)) checked.add(identifier); - notifyItemChanged(position); + if (identifier == null) return false; - final int size = checked.size(); - if (size <= 0) cab.finish(); - else if (size == 1) cab.setTitle(getName(checked.get(0))); - else if (size > 1) cab.setTitle(context.getString(R.string.x_selected, size)); + if (!checked.remove(identifier)) checked.add(identifier); + notifyItemChanged(position); + updateCab(); return true; } return false; } - private void openCabIfNecessary() { + protected void checkAll() { + if (cabHolder != null) { + checked.clear(); + for (int i = 0; i < getItemCount(); i++) { + I identifier = getIdentifier(i); + if (identifier != null) { + checked.add(identifier); + } + } + notifyDataSetChanged(); + updateCab(); + } + } + + private void updateCab() { if (cabHolder != null) { if (cab == null || !cab.isActive()) { cab = cabHolder.openCab(menuRes, this); } + final int size = checked.size(); + if (size <= 0) cab.finish(); + else if (size == 1) cab.setTitle(getName(checked.get(0))); + else cab.setTitle(context.getString(R.string.x_selected, size)); } } - private void unCheckAll() { + private void clearChecked() { checked.clear(); notifyDataSetChanged(); } @@ -81,15 +96,19 @@ public boolean onCabCreated(MaterialCab materialCab, Menu menu) { @Override public boolean onCabItemClicked(MenuItem menuItem) { - onMultipleItemAction(menuItem, new ArrayList<>(checked)); - cab.finish(); - unCheckAll(); + if (menuItem.getItemId() == R.id.action_multi_select_adapter_check_all) { + checkAll(); + } else { + onMultipleItemAction(menuItem, new ArrayList<>(checked)); + cab.finish(); + clearChecked(); + } return true; } @Override public boolean onCabFinished(MaterialCab materialCab) { - unCheckAll(); + clearChecked(); return true; } @@ -97,7 +116,8 @@ protected String getName(I object) { return object.toString(); } + @Nullable protected abstract I getIdentifier(int position); - protected abstract void onMultipleItemAction(MenuItem menuItem, ArrayList selection); + protected abstract void onMultipleItemAction(MenuItem menuItem, List selection); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/base/MediaEntryViewHolder.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/base/MediaEntryViewHolder.java index 8ad2a3f10..63bc7dc18 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/base/MediaEntryViewHolder.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/base/MediaEntryViewHolder.java @@ -1,9 +1,9 @@ package com.kabouzeid.gramophone.adapter.base; import android.os.Build; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; import android.view.View; import android.widget.ImageView; import android.widget.TextView; diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/AbsOffsetSongAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/AbsOffsetSongAdapter.java index b75170dde..6b615ade8 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/AbsOffsetSongAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/AbsOffsetSongAdapter.java @@ -1,9 +1,9 @@ package com.kabouzeid.gramophone.adapter.song; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -14,6 +14,7 @@ import com.kabouzeid.gramophone.model.Song; import java.util.ArrayList; +import java.util.List; /** * @author Eugene Cheung (arkon) @@ -23,11 +24,11 @@ public abstract class AbsOffsetSongAdapter extends SongAdapter { protected static final int OFFSET_ITEM = 0; protected static final int SONG = 1; - public AbsOffsetSongAdapter(AppCompatActivity activity, ArrayList dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { + public AbsOffsetSongAdapter(AppCompatActivity activity, List dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { super(activity, dataSet, itemLayoutRes, usePalette, cabHolder); } - public AbsOffsetSongAdapter(AppCompatActivity activity, ArrayList dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder, boolean showSectionName) { + public AbsOffsetSongAdapter(AppCompatActivity activity, List dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder, boolean showSectionName) { super(activity, dataSet, itemLayoutRes, usePalette, cabHolder, showSectionName); } @@ -53,10 +54,11 @@ public long getItemId(int position) { return super.getItemId(position); } + @Nullable @Override protected Song getIdentifier(int position) { position--; - if (position < 0) return Song.EMPTY_SONG; + if (position < 0) return null; return super.getIdentifier(position); } @@ -87,7 +89,8 @@ public ViewHolder(@NonNull View itemView) { @Override protected Song getSong() { - if (getItemViewType() == OFFSET_ITEM) return Song.EMPTY_SONG; + if (getItemViewType() == OFFSET_ITEM) + return Song.EMPTY_SONG; // could also return null, just to be safe return empty song return dataSet.get(getAdapterPosition() - 1); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/AlbumSongAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/AlbumSongAdapter.java index 8742c34b9..586f513e1 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/AlbumSongAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/AlbumSongAdapter.java @@ -1,9 +1,9 @@ package com.kabouzeid.gramophone.adapter.song; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; import android.view.View; import com.kabouzeid.gramophone.interfaces.CabHolder; @@ -11,15 +11,14 @@ import com.kabouzeid.gramophone.util.MusicUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ public class AlbumSongAdapter extends SongAdapter { - public static final String TAG = AlbumSongAdapter.class.getSimpleName(); - - public AlbumSongAdapter(AppCompatActivity activity, ArrayList dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { + public AlbumSongAdapter(AppCompatActivity activity, List dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { super(activity, dataSet, itemLayoutRes, usePalette, cabHolder); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/ArtistSongAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/ArtistSongAdapter.java index 3f4f73779..886f642a6 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/ArtistSongAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/ArtistSongAdapter.java @@ -1,10 +1,10 @@ package com.kabouzeid.gramophone.adapter.song; import android.os.Build; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.util.Pair; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.util.Pair; +import androidx.appcompat.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -26,6 +26,7 @@ import com.kabouzeid.gramophone.util.NavigationUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) @@ -34,13 +35,13 @@ public class ArtistSongAdapter extends ArrayAdapter implements MaterialCab @Nullable private final CabHolder cabHolder; private MaterialCab cab; - private ArrayList dataSet; - private ArrayList checked; + private List dataSet; + private List checked; @NonNull private final AppCompatActivity activity; - public ArtistSongAdapter(@NonNull AppCompatActivity activity, @NonNull ArrayList dataSet, @Nullable CabHolder cabHolder) { + public ArtistSongAdapter(@NonNull AppCompatActivity activity, @NonNull List dataSet, @Nullable CabHolder cabHolder) { super(activity, R.layout.item_list, dataSet); this.activity = activity; this.cabHolder = cabHolder; @@ -48,20 +49,20 @@ public ArtistSongAdapter(@NonNull AppCompatActivity activity, @NonNull ArrayList checked = new ArrayList<>(); } - public ArrayList getDataSet() { + public List getDataSet() { return dataSet; } - public void swapDataSet(ArrayList dataSet) { + public void swapDataSet(List dataSet) { this.dataSet = dataSet; clear(); addAll(dataSet); notifyDataSetChanged(); } - @Nullable @Override - public View getView(final int position, @Nullable View convertView, ViewGroup parent) { + @NonNull + public View getView(final int position, View convertView, @NonNull ViewGroup parent) { final Song song = getItem(position); if (convertView == null) { convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_list, parent, false); @@ -129,7 +130,7 @@ public boolean onMenuItemClick(MenuItem item) { return convertView; } - private void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList selection) { + private void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull List selection) { SongsMenuHelper.handleMenuClick(activity, selection, menuItem.getItemId()); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/OrderablePlaylistSongAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/OrderablePlaylistSongAdapter.java index 18d141676..4a5220b93 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/OrderablePlaylistSongAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/OrderablePlaylistSongAdapter.java @@ -1,9 +1,9 @@ package com.kabouzeid.gramophone.adapter.song; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; import android.view.MenuItem; import android.view.View; @@ -27,13 +27,11 @@ @SuppressWarnings("unchecked") public class OrderablePlaylistSongAdapter extends PlaylistSongAdapter implements DraggableItemAdapter { - public static final String TAG = OrderablePlaylistSongAdapter.class.getSimpleName(); - private OnMoveItemListener onMoveItemListener; - public OrderablePlaylistSongAdapter(@NonNull AppCompatActivity activity, @NonNull ArrayList dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder, @Nullable OnMoveItemListener onMoveItemListener) { - super(activity, (ArrayList) (List) dataSet, itemLayoutRes, usePalette, cabHolder); - overrideMultiSelectMenuRes(R.menu.menu_playlists_songs_selection); + public OrderablePlaylistSongAdapter(@NonNull AppCompatActivity activity, @NonNull List dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder, @Nullable OnMoveItemListener onMoveItemListener) { + super(activity, (List) (List) dataSet, itemLayoutRes, usePalette, cabHolder); + setMultiSelectMenuRes(R.menu.menu_playlists_songs_selection); this.onMoveItemListener = onMoveItemListener; } @@ -46,14 +44,14 @@ protected SongAdapter.ViewHolder createViewHolder(View view) { public long getItemId(int position) { position--; if (position < 0) return -2; - return ((ArrayList) (List) dataSet).get(position).idInPlayList; // important! + return ((List) (List) dataSet).get(position).idInPlayList; // important! } @Override - protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList selection) { + protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull List selection) { switch (menuItem.getItemId()) { case R.id.action_remove_from_playlist: - RemoveFromPlaylistDialog.create((ArrayList) (List) selection).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST"); + RemoveFromPlaylistDialog.create((List) (List) selection).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST"); return; } super.onMultipleItemAction(menuItem, selection); diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/PlayingQueueAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/PlayingQueueAdapter.java index 51785b2a0..2314bec12 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/PlayingQueueAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/PlayingQueueAdapter.java @@ -1,9 +1,9 @@ package com.kabouzeid.gramophone.adapter.song; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; import android.view.MenuItem; import android.view.View; @@ -18,6 +18,7 @@ import com.kabouzeid.gramophone.util.ViewUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) @@ -30,7 +31,7 @@ public class PlayingQueueAdapter extends SongAdapter implements DraggableItemAda private int current; - public PlayingQueueAdapter(AppCompatActivity activity, ArrayList dataSet, int current, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { + public PlayingQueueAdapter(AppCompatActivity activity, List dataSet, int current, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { super(activity, dataSet, itemLayoutRes, usePalette, cabHolder); this.current = current; } @@ -66,7 +67,7 @@ protected void loadAlbumCover(Song song, SongAdapter.ViewHolder holder) { // We don't want to load it in this adapter } - public void swapDataSet(ArrayList dataSet, int position) { + public void swapDataSet(List dataSet, int position) { this.dataSet = dataSet; current = position; notifyDataSetChanged(); diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/PlaylistSongAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/PlaylistSongAdapter.java index 938221f49..ad621979a 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/PlaylistSongAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/PlaylistSongAdapter.java @@ -1,10 +1,10 @@ package com.kabouzeid.gramophone.adapter.song; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.util.Pair; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.util.Pair; +import androidx.appcompat.app.AppCompatActivity; import android.view.MenuItem; import android.view.View; @@ -16,17 +16,16 @@ import com.kabouzeid.gramophone.util.NavigationUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ public class PlaylistSongAdapter extends AbsOffsetSongAdapter { - public static final String TAG = PlaylistSongAdapter.class.getSimpleName(); - - public PlaylistSongAdapter(AppCompatActivity activity, @NonNull ArrayList dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { + public PlaylistSongAdapter(AppCompatActivity activity, @NonNull List dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { super(activity, dataSet, itemLayoutRes, usePalette, cabHolder, false); - overrideMultiSelectMenuRes(R.menu.menu_cannot_delete_single_songs_playlist_songs_selection); + setMultiSelectMenuRes(R.menu.menu_cannot_delete_single_songs_playlist_songs_selection); } @Override @@ -90,4 +89,4 @@ protected boolean onSongMenuItemClick(MenuItem item) { return super.onSongMenuItemClick(item); } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/ShuffleButtonSongAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/ShuffleButtonSongAdapter.java index 7fd6e85b8..625494b47 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/ShuffleButtonSongAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/ShuffleButtonSongAdapter.java @@ -1,10 +1,10 @@ package com.kabouzeid.gramophone.adapter.song; import android.graphics.Typeface; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; import android.view.View; import com.kabouzeid.appthemehelper.ThemeStore; @@ -14,13 +14,14 @@ import com.kabouzeid.gramophone.model.Song; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ public class ShuffleButtonSongAdapter extends AbsOffsetSongAdapter { - public ShuffleButtonSongAdapter(AppCompatActivity activity, ArrayList dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { + public ShuffleButtonSongAdapter(AppCompatActivity activity, List dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { super(activity, dataSet, itemLayoutRes, usePalette, cabHolder); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/SongAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/SongAdapter.java index ea1094469..79782d324 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/song/SongAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/song/SongAdapter.java @@ -1,11 +1,11 @@ package com.kabouzeid.gramophone.adapter.song; import android.graphics.drawable.Drawable; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.util.Pair; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.util.Pair; +import androidx.appcompat.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -21,36 +21,37 @@ import com.kabouzeid.gramophone.glide.PhonographColoredTarget; import com.kabouzeid.gramophone.glide.SongGlideRequest; import com.kabouzeid.gramophone.helper.MusicPlayerRemote; +import com.kabouzeid.gramophone.helper.SortOrder; import com.kabouzeid.gramophone.helper.menu.SongMenuHelper; import com.kabouzeid.gramophone.helper.menu.SongsMenuHelper; import com.kabouzeid.gramophone.interfaces.CabHolder; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.NavigationUtil; +import com.kabouzeid.gramophone.util.PreferenceUtil; import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ public class SongAdapter extends AbsMultiSelectAdapter implements MaterialCab.Callback, FastScrollRecyclerView.SectionedAdapter { - public static final String TAG = AlbumSongAdapter.class.getSimpleName(); - protected final AppCompatActivity activity; - protected ArrayList dataSet; + protected List dataSet; protected int itemLayoutRes; protected boolean usePalette = false; protected boolean showSectionName = true; - public SongAdapter(AppCompatActivity activity, ArrayList dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { + public SongAdapter(AppCompatActivity activity, List dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) { this(activity, dataSet, itemLayoutRes, usePalette, cabHolder, true); } - public SongAdapter(AppCompatActivity activity, ArrayList dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder, boolean showSectionName) { + public SongAdapter(AppCompatActivity activity, List dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder, boolean showSectionName) { super(activity, cabHolder, R.menu.menu_media_selection); this.activity = activity; this.dataSet = dataSet; @@ -60,7 +61,7 @@ public SongAdapter(AppCompatActivity activity, ArrayList dataSet, @LayoutR setHasStableIds(true); } - public void swapDataSet(ArrayList dataSet) { + public void swapDataSet(List dataSet) { this.dataSet = dataSet; notifyDataSetChanged(); } @@ -70,7 +71,7 @@ public void usePalette(boolean usePalette) { notifyDataSetChanged(); } - public ArrayList getDataSet() { + public List getDataSet() { return dataSet; } @@ -79,9 +80,9 @@ public long getItemId(int position) { return dataSet.get(position).id; } - @NonNull @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + @NonNull + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false); return createViewHolder(view); } @@ -158,7 +159,7 @@ protected String getSongTitle(Song song) { } protected String getSongText(Song song) { - return song.artistName; + return MusicUtil.getSongInfoString(song); } @Override @@ -177,14 +178,34 @@ protected String getName(Song song) { } @Override - protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList selection) { + protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull List selection) { SongsMenuHelper.handleMenuClick(activity, selection, menuItem.getItemId()); } @NonNull @Override public String getSectionName(int position) { - return showSectionName ? MusicUtil.getSectionName(dataSet.get(position).title) : ""; + if (!showSectionName) { + return ""; + } + + @Nullable String sectionName = null; + switch (PreferenceUtil.getInstance(activity).getSongSortOrder()) { + case SortOrder.SongSortOrder.SONG_A_Z: + case SortOrder.SongSortOrder.SONG_Z_A: + sectionName = dataSet.get(position).title; + break; + case SortOrder.SongSortOrder.SONG_ALBUM: + sectionName = dataSet.get(position).albumName; + break; + case SortOrder.SongSortOrder.SONG_ARTIST: + sectionName = dataSet.get(position).artistName; + break; + case SortOrder.SongSortOrder.SONG_YEAR: + return MusicUtil.getYearString(dataSet.get(position).year); + } + + return MusicUtil.getSectionName(sectionName); } public class ViewHolder extends MediaEntryViewHolder { diff --git a/app/src/main/java/com/kabouzeid/gramophone/appshortcuts/AppShortcutIconGenerator.java b/app/src/main/java/com/kabouzeid/gramophone/appshortcuts/AppShortcutIconGenerator.java index d4ea4a34e..6eda73c5a 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/appshortcuts/AppShortcutIconGenerator.java +++ b/app/src/main/java/com/kabouzeid/gramophone/appshortcuts/AppShortcutIconGenerator.java @@ -1,27 +1,26 @@ package com.kabouzeid.gramophone.appshortcuts; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.graphics.drawable.LayerDrawable; import android.os.Build; -import android.support.annotation.RequiresApi; -import android.support.v4.graphics.drawable.IconCompat; +import androidx.annotation.RequiresApi; +import androidx.core.graphics.drawable.IconCompat; import android.util.TypedValue; import com.kabouzeid.appthemehelper.ThemeStore; import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.util.ImageUtil; import com.kabouzeid.gramophone.util.PreferenceUtil; -import com.kabouzeid.gramophone.util.Util; /** * @author Adrian Campos */ @RequiresApi(Build.VERSION_CODES.N_MR1) public final class AppShortcutIconGenerator { + public static Icon generateThemedIcon(Context context, int iconId) { if (PreferenceUtil.getInstance(context).coloredAppShortcuts()) { return generateUserThemedIcon(context, iconId).toIcon(); @@ -52,26 +51,19 @@ private static IconCompat generateUserThemedIcon(Context context, int iconId) { private static IconCompat generateThemedIcon(Context context, int iconId, int foregroundColor, int backgroundColor) { // Get and tint foreground and background drawables - Drawable vectorDrawable = Util.getTintedVectorDrawable(context, iconId, foregroundColor); - Drawable backgroundDrawable = Util.getTintedVectorDrawable(context, R.drawable.ic_app_shortcut_background, backgroundColor); + Drawable vectorDrawable = ImageUtil.getTintedVectorDrawable(context, iconId, foregroundColor); + Drawable backgroundDrawable = ImageUtil.getTintedVectorDrawable(context, R.drawable.ic_app_shortcut_background, backgroundColor); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { AdaptiveIconDrawable adaptiveIconDrawable = new AdaptiveIconDrawable(backgroundDrawable, vectorDrawable); - return IconCompat.createWithAdaptiveBitmap(drawableToBitmap(adaptiveIconDrawable)); + return IconCompat.createWithAdaptiveBitmap(ImageUtil.createBitmap(adaptiveIconDrawable)); } else { // Squash the two drawables together LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{backgroundDrawable, vectorDrawable}); // Return as an Icon - return IconCompat.createWithBitmap(drawableToBitmap(layerDrawable)); + return IconCompat.createWithBitmap(ImageUtil.createBitmap(layerDrawable)); } } - private static Bitmap drawableToBitmap(Drawable drawable) { - Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - return bitmap; - } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetBig.java b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetBig.java index c2833facb..f13909048 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetBig.java +++ b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetBig.java @@ -7,7 +7,7 @@ import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.drawable.Drawable; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.text.TextUtils; import android.view.View; import android.widget.RemoteViews; @@ -23,6 +23,7 @@ import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.service.MusicService; import com.kabouzeid.gramophone.ui.activities.MainActivity; +import com.kabouzeid.gramophone.util.ImageUtil; import com.kabouzeid.gramophone.util.Util; public class AppWidgetBig extends BaseAppWidget { @@ -47,9 +48,9 @@ protected void defaultAppWidget(final Context context, final int[] appWidgetIds) appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art); - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(Util.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getPrimaryTextColor(context, false)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(Util.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getPrimaryTextColor(context, false)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(Util.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp, MaterialValueHelper.getPrimaryTextColor(context, false)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_next, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getPrimaryTextColor(context, false)))); + appWidgetView.setImageViewBitmap(R.id.button_prev, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getPrimaryTextColor(context, false)))); + appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp, MaterialValueHelper.getPrimaryTextColor(context, false)))); linkButtons(context, appWidgetView); pushUpdate(context, appWidgetIds, appWidgetView); @@ -75,11 +76,11 @@ public void performUpdate(final MusicService service, final int[] appWidgetIds) // Set correct drawable for pause state int playPauseRes = isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp; - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(Util.getTintedVectorDrawable(service, playPauseRes, MaterialValueHelper.getPrimaryTextColor(service, false)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, playPauseRes, MaterialValueHelper.getPrimaryTextColor(service, false)))); // Set prev/next button drawables - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(Util.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getPrimaryTextColor(service, false)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(Util.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getPrimaryTextColor(service, false)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_next, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getPrimaryTextColor(service, false)))); + appWidgetView.setImageViewBitmap(R.id.button_prev, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getPrimaryTextColor(service, false)))); // Link actions buttons to intents linkButtons(service, appWidgetView); diff --git a/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetCard.java b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetCard.java index e8bd183fd..2deba464c 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetCard.java +++ b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetCard.java @@ -6,8 +6,8 @@ import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; -import android.support.annotation.Nullable; -import android.support.v7.graphics.Palette; +import androidx.annotation.Nullable; +import androidx.palette.graphics.Palette; import android.text.TextUtils; import android.view.View; import android.widget.RemoteViews; @@ -24,7 +24,7 @@ import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.service.MusicService; import com.kabouzeid.gramophone.ui.activities.MainActivity; -import com.kabouzeid.gramophone.util.Util; +import com.kabouzeid.gramophone.util.ImageUtil; public class AppWidgetCard extends BaseAppWidget { public static final String NAME = "app_widget_card"; @@ -50,9 +50,9 @@ protected void defaultAppWidget(final Context context, final int[] appWidgetIds) appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art); - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(Util.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(Util.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(Util.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_next, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)))); + appWidgetView.setImageViewBitmap(R.id.button_prev, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)))); + appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)))); linkButtons(context, appWidgetView); pushUpdate(context, appWidgetIds, appWidgetView); @@ -78,11 +78,11 @@ public void performUpdate(final MusicService service, final int[] appWidgetIds) // Set correct drawable for pause state int playPauseRes = isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp; - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(Util.getTintedVectorDrawable(service, playPauseRes, MaterialValueHelper.getSecondaryTextColor(service, true)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, playPauseRes, MaterialValueHelper.getSecondaryTextColor(service, true)))); // Set prev/next button drawables - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(Util.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getSecondaryTextColor(service, true)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(Util.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getSecondaryTextColor(service, true)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_next, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getSecondaryTextColor(service, true)))); + appWidgetView.setImageViewBitmap(R.id.button_prev, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getSecondaryTextColor(service, true)))); // Link actions buttons to intents linkButtons(service, appWidgetView); @@ -119,11 +119,11 @@ public void onLoadFailed(Exception e, Drawable errorDrawable) { private void update(@Nullable Bitmap bitmap, int color) { // Set correct drawable for pause state int playPauseRes = isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp; - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(Util.getTintedVectorDrawable(service, playPauseRes, color), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, playPauseRes, color))); // Set prev/next button drawables - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(Util.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, color), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(Util.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, color), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_next, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, color))); + appWidgetView.setImageViewBitmap(R.id.button_prev, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, color))); final Drawable image = getAlbumArtDrawable(service.getResources(), bitmap); final Bitmap roundedBitmap = createRoundedBitmap(image, imageSize, imageSize, cardRadius, 0, cardRadius, 0); diff --git a/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetClassic.java b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetClassic.java index 4412485e0..e99e517ad 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetClassic.java +++ b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetClassic.java @@ -6,8 +6,8 @@ import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; -import android.support.annotation.Nullable; -import android.support.v7.graphics.Palette; +import androidx.annotation.Nullable; +import androidx.palette.graphics.Palette; import android.text.TextUtils; import android.view.View; import android.widget.RemoteViews; @@ -24,7 +24,7 @@ import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.service.MusicService; import com.kabouzeid.gramophone.ui.activities.MainActivity; -import com.kabouzeid.gramophone.util.Util; +import com.kabouzeid.gramophone.util.ImageUtil; public class AppWidgetClassic extends BaseAppWidget { public static final String NAME = "app_widget_classic"; @@ -50,9 +50,9 @@ protected void defaultAppWidget(final Context context, final int[] appWidgetIds) appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art); - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(Util.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(Util.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(Util.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_next, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)))); + appWidgetView.setImageViewBitmap(R.id.button_prev, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)))); + appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)))); linkButtons(context, appWidgetView); pushUpdate(context, appWidgetIds, appWidgetView); @@ -112,11 +112,11 @@ public void onLoadFailed(Exception e, Drawable errorDrawable) { private void update(@Nullable Bitmap bitmap, int color) { // Set correct drawable for pause state int playPauseRes = isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp; - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(Util.getTintedVectorDrawable(service, playPauseRes, color), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, playPauseRes, color))); // Set prev/next button drawables - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(Util.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, color), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(Util.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, color), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_next, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, color))); + appWidgetView.setImageViewBitmap(R.id.button_prev, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, color))); final Drawable image = getAlbumArtDrawable(service.getResources(), bitmap); final Bitmap roundedBitmap = createRoundedBitmap(image, imageSize, imageSize, cardRadius, 0, cardRadius, 0); diff --git a/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetSmall.java b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetSmall.java index 7d63c6ffa..e5d2011fd 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetSmall.java +++ b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/AppWidgetSmall.java @@ -6,8 +6,8 @@ import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; -import android.support.annotation.Nullable; -import android.support.v7.graphics.Palette; +import androidx.annotation.Nullable; +import androidx.palette.graphics.Palette; import android.text.TextUtils; import android.view.View; import android.widget.RemoteViews; @@ -24,7 +24,7 @@ import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.service.MusicService; import com.kabouzeid.gramophone.ui.activities.MainActivity; -import com.kabouzeid.gramophone.util.Util; +import com.kabouzeid.gramophone.util.ImageUtil; public class AppWidgetSmall extends BaseAppWidget { public static final String NAME = "app_widget_small"; @@ -50,9 +50,9 @@ protected void defaultAppWidget(final Context context, final int[] appWidgetIds) appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE); appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art); - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(Util.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(Util.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(Util.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_next, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)))); + appWidgetView.setImageViewBitmap(R.id.button_prev, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)))); + appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true)))); linkButtons(context, appWidgetView); pushUpdate(context, appWidgetIds, appWidgetView); @@ -118,11 +118,11 @@ public void onLoadFailed(Exception e, Drawable errorDrawable) { private void update(@Nullable Bitmap bitmap, int color) { // Set correct drawable for pause state int playPauseRes = isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp; - appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(Util.getTintedVectorDrawable(service, playPauseRes, color), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, playPauseRes, color))); // Set prev/next button drawables - appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(Util.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, color), 1f)); - appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(Util.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, color), 1f)); + appWidgetView.setImageViewBitmap(R.id.button_next, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, color))); + appWidgetView.setImageViewBitmap(R.id.button_prev, ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, color))); final Drawable image = getAlbumArtDrawable(service.getResources(), bitmap); final Bitmap roundedBitmap = createRoundedBitmap(image, imageSize, imageSize, cardRadius, 0, 0, 0); diff --git a/app/src/main/java/com/kabouzeid/gramophone/appwidgets/base/BaseAppWidget.java b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/base/BaseAppWidget.java index 8ea1a3458..e54b73521 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/appwidgets/base/BaseAppWidget.java +++ b/app/src/main/java/com/kabouzeid/gramophone/appwidgets/base/BaseAppWidget.java @@ -16,12 +16,12 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; -import android.text.TextUtils; import android.widget.RemoteViews; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.service.MusicService; +import com.kabouzeid.gramophone.util.MusicUtil; public abstract class BaseAppWidget extends AppWidgetProvider { public static final String NAME = "app_widget"; @@ -82,14 +82,6 @@ protected PendingIntent buildPendingIntent(Context context, final String action, } } - protected static Bitmap createBitmap(Drawable drawable, float sizeMultiplier) { - Bitmap bitmap = Bitmap.createBitmap((int) (drawable.getIntrinsicWidth() * sizeMultiplier), (int) (drawable.getIntrinsicHeight() * sizeMultiplier), Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(bitmap); - drawable.setBounds(0, 0, c.getWidth(), c.getHeight()); - drawable.draw(c); - return bitmap; - } - protected static Bitmap createRoundedBitmap(Drawable drawable, int width, int height, float tl, float tr, float bl, float br) { if (drawable == null) return null; @@ -145,12 +137,6 @@ protected Drawable getAlbumArtDrawable(final Resources resources, final Bitmap b } protected String getSongArtistAndAlbum(final Song song) { - final StringBuilder builder = new StringBuilder(); - builder.append(song.artistName); - if (!TextUtils.isEmpty(song.artistName) && !TextUtils.isEmpty(song.albumName)) { - builder.append(" • "); - } - builder.append(song.albumName); - return builder.toString(); + return MusicUtil.getSongInfoString(song); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/AddToPlaylistDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/AddToPlaylistDialog.java index 92af5db2d..4bfc227eb 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/AddToPlaylistDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/AddToPlaylistDialog.java @@ -2,9 +2,8 @@ import android.app.Dialog; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; -import android.view.View; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; import com.afollestad.materialdialogs.MaterialDialog; import com.kabouzeid.gramophone.R; @@ -24,16 +23,16 @@ public class AddToPlaylistDialog extends DialogFragment { @NonNull public static AddToPlaylistDialog create(Song song) { - ArrayList list = new ArrayList<>(); + List list = new ArrayList<>(); list.add(song); return create(list); } @NonNull - public static AddToPlaylistDialog create(ArrayList songs) { + public static AddToPlaylistDialog create(List songs) { AddToPlaylistDialog dialog = new AddToPlaylistDialog(); Bundle args = new Bundle(); - args.putParcelableArrayList("songs", songs); + args.putParcelableArrayList("songs", new ArrayList<>(songs)); dialog.setArguments(args); return dialog; } @@ -49,24 +48,41 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { } final ArrayList songs = getArguments().getParcelableArrayList("songs"); - if(songs != null && songs.size() == 1) + int[] songIds = new int[songs.size()]; + if(songs != null) { - //TODO: display checkboxes instead of checkmark + for(int i = 0; i < songs.size(); i++){ + songIds[i] = songs.get(i).id; + } + + for (int i = 0; i < playlists.size(); i++) { + int playlistId = playlists.get(i).id; - long startTime = System.currentTimeMillis();//TEMP + long startTime = System.currentTimeMillis();//TODO: remove stopwatch - Boolean[] songIsInPlaylist = new Boolean[playlists.size()]; - for(int i = 0; i < playlists.size(); i++){ - songIsInPlaylist[i] = PlaylistsUtil.doPlaylistContains(getActivity(), playlists.get(i).id, songs.get(0).id); + boolean isAnySongInPlaylist = PlaylistsUtil.doPlaylistContainsAnySong(getActivity(), playlistId, songIds); + long stopTime1 = System.currentTimeMillis();//TODO: remove stopwatch + boolean areAllSongsInPlaylist = PlaylistsUtil.doPlaylistContainsAllSongs(getActivity(), playlistId, songIds); + long stopTime2 = System.currentTimeMillis();//TODO: remove stopwatch - //TEMP - if(songIsInPlaylist[i]) { - playlistNames[i + 1] = playlists.get(i).name + " \u2713"; + //TODO: display checkboxes instead of checkmark + if (isAnySongInPlaylist) { + if(areAllSongsInPlaylist){ + playlistNames[i + 1] = playlists.get(i).name + " \u2713"; //Add checkmark + } + else{ + playlistNames[i + 1] = playlists.get(i).name + " (\u2713)"; //Add checkmark in brackets + } } - } - long difference = System.currentTimeMillis() - startTime; - long endTime = difference + startTime; + long stopTimeTotal = System.currentTimeMillis();//TODO: remove stopwatch + long Time1 = stopTime1 - startTime; + long Time2 = stopTime2 - stopTime1; + long Time3 = stopTimeTotal - stopTime2; + long TotalTime = stopTimeTotal - startTime;//TODO: remove stopwatch + int uselessAssignmentForDebugging = 7;//TODO: remove stopwatch + int c = uselessAssignmentForDebugging;//TODO: remove stopwatch + } } return new MaterialDialog.Builder(getActivity()) @@ -80,9 +96,9 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { CreatePlaylistDialog.create(songs).show(getActivity().getSupportFragmentManager(), "ADD_TO_PLAYLIST"); } else { materialDialog.dismiss(); - PlaylistsUtil.addToPlaylist(getActivity(), songs, playlists.get(i - 1).id, true); + PlaylistsUtil.addToPlaylistWithoutDuplicates(getActivity(), songs, songIds, playlists.get(i - 1).id, true); } }) .build(); } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/BlacklistFolderChooserDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/BlacklistFolderChooserDialog.java index 4883e6c48..46830c5e9 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/BlacklistFolderChooserDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/BlacklistFolderChooserDialog.java @@ -6,12 +6,11 @@ import android.os.Build; import android.os.Bundle; import android.os.Environment; -import android.support.annotation.NonNull; -import android.support.v4.app.ActivityCompat; -import android.support.v4.app.DialogFragment; +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; +import androidx.fragment.app.DialogFragment; import android.view.View; -import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.kabouzeid.gramophone.R; @@ -105,9 +104,6 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { .onNegative((materialDialog, dialogAction) -> dismiss()) .positiveText(R.string.add_action) .negativeText(android.R.string.cancel); - if (File.pathSeparator.equals(initialPath)) { - canGoUp = false; - } return builder.build(); } @@ -118,7 +114,7 @@ public void onSelection(MaterialDialog materialDialog, View view, int i, CharSeq if (parentFolder.getAbsolutePath().equals("/storage/emulated")) { parentFolder = parentFolder.getParentFile(); } - canGoUp = parentFolder.getParent() != null; + checkIfCanGoUp(); } else { parentFolder = parentContents[canGoUp ? i - 1 : i]; canGoUp = true; @@ -130,11 +126,7 @@ public void onSelection(MaterialDialog materialDialog, View view, int i, CharSeq } private void checkIfCanGoUp() { - try { - canGoUp = parentFolder.getPath().split(File.pathSeparator).length > 1; - } catch (IndexOutOfBoundsException e) { - canGoUp = false; - } + canGoUp = parentFolder.getParent() != null; } private void reload() { diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/ChangelogDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/ChangelogDialog.java index 34293c90f..96f24f5b7 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/ChangelogDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/ChangelogDialog.java @@ -3,13 +3,12 @@ import android.annotation.SuppressLint; import android.app.Dialog; import android.content.Context; -import android.content.DialogInterface; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.graphics.Color; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; import android.view.InflateException; import android.view.LayoutInflater; import android.view.View; diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/ClearSmartPlaylistDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/ClearSmartPlaylistDialog.java index 74b97063d..8f5017d1e 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/ClearSmartPlaylistDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/ClearSmartPlaylistDialog.java @@ -2,11 +2,10 @@ import android.app.Dialog; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; import android.text.Html; -import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.model.smartplaylist.AbsSmartPlaylist; @@ -47,4 +46,4 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { }) .build(); } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/CreatePlaylistDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/CreatePlaylistDialog.java index 1d2c3f046..908f5be8b 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/CreatePlaylistDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/CreatePlaylistDialog.java @@ -2,9 +2,9 @@ import android.app.Dialog; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.DialogFragment; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; import android.text.InputType; import android.widget.Toast; @@ -14,6 +14,7 @@ import com.kabouzeid.gramophone.util.PlaylistsUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid), Aidan Follestad (afollestad) @@ -29,17 +30,17 @@ public static CreatePlaylistDialog create() { @NonNull public static CreatePlaylistDialog create(@Nullable Song song) { - ArrayList list = new ArrayList<>(); + List list = new ArrayList<>(); if (song != null) list.add(song); return create(list); } @NonNull - public static CreatePlaylistDialog create(ArrayList songs) { + public static CreatePlaylistDialog create(List songs) { CreatePlaylistDialog dialog = new CreatePlaylistDialog(); Bundle args = new Bundle(); - args.putParcelableArrayList(SONGS, songs); + args.putParcelableArrayList(SONGS, new ArrayList<>(songs)); dialog.setArguments(args); return dialog; } @@ -63,7 +64,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { final int playlistId = PlaylistsUtil.createPlaylist(getActivity(), name); if (getActivity() != null) { //noinspection unchecked - ArrayList songs = getArguments().getParcelableArrayList(SONGS); + List songs = getArguments().getParcelableArrayList(SONGS); if (songs != null && !songs.isEmpty()) { PlaylistsUtil.addToPlaylist(getActivity(), songs, playlistId, true); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeletePlaylistDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeletePlaylistDialog.java index 397a5d925..3b2e21443 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeletePlaylistDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeletePlaylistDialog.java @@ -2,17 +2,17 @@ import android.app.Dialog; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; import android.text.Html; -import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.model.Playlist; import com.kabouzeid.gramophone.util.PlaylistsUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) @@ -21,16 +21,16 @@ public class DeletePlaylistDialog extends DialogFragment { @NonNull public static DeletePlaylistDialog create(Playlist playlist) { - ArrayList list = new ArrayList<>(); + List list = new ArrayList<>(); list.add(playlist); return create(list); } @NonNull - public static DeletePlaylistDialog create(ArrayList playlists) { + public static DeletePlaylistDialog create(List playlists) { DeletePlaylistDialog dialog = new DeletePlaylistDialog(); Bundle args = new Bundle(); - args.putParcelableArrayList("playlists", playlists); + args.putParcelableArrayList("playlists", new ArrayList<>(playlists)); dialog.setArguments(args); return dialog; } @@ -39,7 +39,7 @@ public static DeletePlaylistDialog create(ArrayList playlists) { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { //noinspection unchecked - final ArrayList playlists = getArguments().getParcelableArrayList("playlists"); + final List playlists = getArguments().getParcelableArrayList("playlists"); int title; CharSequence content; //noinspection ConstantConditions @@ -62,4 +62,4 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { }) .build(); } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeleteSongsDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeleteSongsDialog.java index 602cbe80a..1dc2f482f 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeleteSongsDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/DeleteSongsDialog.java @@ -2,17 +2,17 @@ import android.app.Dialog; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; import android.text.Html; -import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.util.MusicUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid), Aidan Follestad (afollestad) @@ -21,16 +21,16 @@ public class DeleteSongsDialog extends DialogFragment { @NonNull public static DeleteSongsDialog create(Song song) { - ArrayList list = new ArrayList<>(); + List list = new ArrayList<>(); list.add(song); return create(list); } @NonNull - public static DeleteSongsDialog create(ArrayList songs) { + public static DeleteSongsDialog create(List songs) { DeleteSongsDialog dialog = new DeleteSongsDialog(); Bundle args = new Bundle(); - args.putParcelableArrayList("songs", songs); + args.putParcelableArrayList("songs", new ArrayList<>(songs)); dialog.setArguments(args); return dialog; } @@ -39,7 +39,7 @@ public static DeleteSongsDialog create(ArrayList songs) { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { //noinspection unchecked - final ArrayList songs = getArguments().getParcelableArrayList("songs"); + final List songs = getArguments().getParcelableArrayList("songs"); int title; CharSequence content; if (songs.size() > 1) { @@ -61,4 +61,4 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { }) .build(); } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/DonationsDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/DonationsDialog.java index a1afce061..89e796e80 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/DonationsDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/DonationsDialog.java @@ -6,12 +6,11 @@ import android.graphics.Paint; import android.os.AsyncTask; import android.os.Bundle; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; import android.util.Log; import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; @@ -62,7 +61,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { @SuppressLint("InflateParams") View customView = LayoutInflater.from(getContext()).inflate(R.layout.dialog_donation, null); - ProgressBar progressBar = ButterKnife.findById(customView, R.id.progress); + ProgressBar progressBar = customView.findViewById(R.id.progress); MDTintHelper.setTint(progressBar, ThemeSingleton.get().positiveColor.getDefaultColor()); return new MaterialDialog.Builder(getContext()) @@ -84,7 +83,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { } @Override - public void onProductPurchased(String productId, TransactionDetails details) { + public void onProductPurchased(@NonNull String productId, TransactionDetails details) { loadSkuDetails(); Toast.makeText(getContext(), R.string.thank_you, Toast.LENGTH_SHORT).show(); } @@ -166,7 +165,7 @@ protected void onPostExecute(List skuDetails) { View customView = ((MaterialDialog) dialog.getDialog()).getCustomView(); //noinspection ConstantConditions customView.findViewById(R.id.progress_container).setVisibility(View.GONE); - ListView listView = ButterKnife.findById(customView, R.id.list); + ListView listView = customView.findViewById(R.id.list); listView.setAdapter(new SkuDetailsAdapter(dialog, skuDetails)); listView.setVisibility(View.VISIBLE); } @@ -184,7 +183,8 @@ public SkuDetailsAdapter(@NonNull DonationsDialog donationsDialog, @NonNull List } @Override - public View getView(final int position, View convertView, ViewGroup parent) { + @NonNull + public View getView(final int position, View convertView, @NonNull ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(getContext()).inflate(LAYOUT_RES_ID, parent, false); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/LyricsDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/LyricsDialog.java index ed8cc2ce8..18d6d5f07 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/LyricsDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/LyricsDialog.java @@ -2,8 +2,8 @@ import android.app.Dialog; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; import com.afollestad.materialdialogs.MaterialDialog; import com.kabouzeid.gramophone.model.lyrics.Lyrics; diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/RemoveFromPlaylistDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/RemoveFromPlaylistDialog.java index 376fa5127..7c8060b05 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/RemoveFromPlaylistDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/RemoveFromPlaylistDialog.java @@ -2,17 +2,17 @@ import android.app.Dialog; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; import android.text.Html; -import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.model.PlaylistSong; import com.kabouzeid.gramophone.util.PlaylistsUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) @@ -21,16 +21,16 @@ public class RemoveFromPlaylistDialog extends DialogFragment { @NonNull public static RemoveFromPlaylistDialog create(PlaylistSong song) { - ArrayList list = new ArrayList<>(); + List list = new ArrayList<>(); list.add(song); return create(list); } @NonNull - public static RemoveFromPlaylistDialog create(ArrayList songs) { + public static RemoveFromPlaylistDialog create(List songs) { RemoveFromPlaylistDialog dialog = new RemoveFromPlaylistDialog(); Bundle args = new Bundle(); - args.putParcelableArrayList("songs", songs); + args.putParcelableArrayList("songs", new ArrayList<>(songs)); dialog.setArguments(args); return dialog; } @@ -39,7 +39,7 @@ public static RemoveFromPlaylistDialog create(ArrayList songs) { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { //noinspection unchecked - final ArrayList songs = getArguments().getParcelableArrayList("songs"); + final List songs = getArguments().getParcelableArrayList("songs"); int title; CharSequence content; if (songs.size() > 1) { @@ -61,4 +61,4 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { }) .build(); } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/RenamePlaylistDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/RenamePlaylistDialog.java index 340af6e10..b09887f90 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/RenamePlaylistDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/RenamePlaylistDialog.java @@ -2,8 +2,8 @@ import android.app.Dialog; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; import android.text.InputType; import com.afollestad.materialdialogs.MaterialDialog; diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/ScanMediaFolderChooserDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/ScanMediaFolderChooserDialog.java new file mode 100644 index 000000000..09d5f7f98 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/ScanMediaFolderChooserDialog.java @@ -0,0 +1,169 @@ +package com.kabouzeid.gramophone.dialogs; + +import android.Manifest; +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.pm.PackageManager; +import android.media.MediaScannerConnection; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; +import androidx.fragment.app.DialogFragment; +import android.view.View; +import android.widget.Toast; + +import com.afollestad.materialdialogs.MaterialDialog; +import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.misc.UpdateToastMediaScannerCompletionListener; +import com.kabouzeid.gramophone.ui.fragments.mainactivity.folders.FoldersFragment; +import com.kabouzeid.gramophone.util.PreferenceUtil; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * @author Aidan Follestad (afollestad), modified by Karim Abou Zeid + */ +public class ScanMediaFolderChooserDialog extends DialogFragment implements MaterialDialog.ListCallback { + + String initialPath = PreferenceUtil.getInstance(getContext()).getStartDirectory().getAbsolutePath(); + private File parentFolder; + private File[] parentContents; + private boolean canGoUp = false; + + public static ScanMediaFolderChooserDialog create() { + return new ScanMediaFolderChooserDialog(); + } + + private static void scanPaths(@NonNull WeakReference activityWeakReference, @NonNull Context applicationContext, @Nullable String[] toBeScanned) { + Activity activity = activityWeakReference.get(); + if (toBeScanned == null || toBeScanned.length < 1) { + Toast.makeText(applicationContext, R.string.nothing_to_scan, Toast.LENGTH_SHORT).show(); + } else { + MediaScannerConnection.scanFile(applicationContext, toBeScanned, null, activity != null ? new UpdateToastMediaScannerCompletionListener(activity, toBeScanned) : null); + } + } + + private String[] getContentsArray() { + if (parentContents == null) { + if (canGoUp) { + return new String[]{".."}; + } + return new String[]{}; + } + String[] results = new String[parentContents.length + (canGoUp ? 1 : 0)]; + if (canGoUp) { + results[0] = ".."; + } + for (int i = 0; i < parentContents.length; i++) { + results[canGoUp ? i + 1 : i] = parentContents[i].getName(); + } + return results; + } + + private File[] listFiles() { + File[] contents = parentFolder.listFiles(); + List results = new ArrayList<>(); + if (contents != null) { + for (File fi : contents) { + if (fi.isDirectory()) { + results.add(fi); + } + } + Collections.sort(results, new FolderSorter()); + return results.toArray(new File[results.size()]); + } + return null; + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M + && ActivityCompat.checkSelfPermission( + getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED) { + return new MaterialDialog.Builder(getActivity()) + .title(R.string.md_error_label) + .content(R.string.md_storage_perm_error) + .positiveText(android.R.string.ok) + .build(); + } + if (savedInstanceState == null) { + savedInstanceState = new Bundle(); + } + if (!savedInstanceState.containsKey("current_path")) { + savedInstanceState.putString("current_path", initialPath); + } + parentFolder = new File(savedInstanceState.getString("current_path", File.pathSeparator)); + checkIfCanGoUp(); + parentContents = listFiles(); + MaterialDialog.Builder builder = + new MaterialDialog.Builder(getActivity()) + .title(parentFolder.getAbsolutePath()) + .items((CharSequence[]) getContentsArray()) + .itemsCallback(this) + .autoDismiss(false) + .onPositive((dialog, which) -> { + final Context applicationContext = getActivity().getApplicationContext(); + final WeakReference activityWeakReference = new WeakReference<>(getActivity()); + dismiss(); + new FoldersFragment.ArrayListPathsAsyncTask(getActivity(), paths -> scanPaths(activityWeakReference, applicationContext, paths)).execute(new FoldersFragment.ArrayListPathsAsyncTask.LoadingInfo(parentFolder, FoldersFragment.AUDIO_FILE_FILTER)); + }) + .onNegative((materialDialog, dialogAction) -> dismiss()) + .positiveText(R.string.action_scan_directory) + .negativeText(android.R.string.cancel); + return builder.build(); + } + + @Override + public void onSelection(MaterialDialog materialDialog, View view, int i, CharSequence s) { + if (canGoUp && i == 0) { + parentFolder = parentFolder.getParentFile(); + if (parentFolder.getAbsolutePath().equals("/storage/emulated")) { + parentFolder = parentFolder.getParentFile(); + } + checkIfCanGoUp(); + } else { + parentFolder = parentContents[canGoUp ? i - 1 : i]; + canGoUp = true; + if (parentFolder.getAbsolutePath().equals("/storage/emulated")) { + parentFolder = Environment.getExternalStorageDirectory(); + } + } + reload(); + } + + private void checkIfCanGoUp() { + canGoUp = parentFolder.getParent() != null; + } + + private void reload() { + parentContents = listFiles(); + MaterialDialog dialog = (MaterialDialog) getDialog(); + dialog.setTitle(parentFolder.getAbsolutePath()); + dialog.setItems((CharSequence[]) getContentsArray()); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString("current_path", parentFolder.getAbsolutePath()); + } + + private static class FolderSorter implements Comparator { + + @Override + public int compare(File lhs, File rhs) { + return lhs.getName().compareTo(rhs.getName()); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/SleepTimerDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/SleepTimerDialog.java index b7d452c55..8c953e7f1 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/SleepTimerDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/SleepTimerDialog.java @@ -9,8 +9,9 @@ import android.os.Bundle; import android.os.CountDownTimer; import android.os.SystemClock; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; +import android.widget.CheckBox; import android.widget.FrameLayout; import android.widget.TextView; import android.widget.Toast; @@ -20,6 +21,7 @@ import com.afollestad.materialdialogs.internal.ThemeSingleton; import com.kabouzeid.gramophone.App; import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.helper.MusicPlayerRemote; import com.kabouzeid.gramophone.service.MusicService; import com.kabouzeid.gramophone.ui.activities.PurchaseActivity; import com.kabouzeid.gramophone.util.MusicUtil; @@ -37,6 +39,8 @@ public class SleepTimerDialog extends DialogFragment { SeekArc seekArc; @BindView(R.id.timer_display) TextView timerDisplay; + @BindView(R.id.should_finish_last_song) + CheckBox shouldFinishLastSong; private int seekArcProgress; private MaterialDialog materialDialog; @@ -65,6 +69,8 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { return; } + PreferenceUtil.getInstance(getActivity()).setSleepTimerFinishMusic(shouldFinishLastSong.isChecked()); + final int minutes = seekArcProgress; PendingIntent pi = makeTimerPendingIntent(PendingIntent.FLAG_CANCEL_CURRENT); @@ -87,6 +93,12 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { previous.cancel(); Toast.makeText(getActivity(), getActivity().getResources().getString(R.string.sleep_timer_canceled), Toast.LENGTH_SHORT).show(); } + + MusicService musicService = MusicPlayerRemote.musicService; + if (musicService != null && musicService.pendingQuit) { + musicService.pendingQuit = false; + Toast.makeText(getActivity(), getActivity().getResources().getString(R.string.sleep_timer_canceled), Toast.LENGTH_SHORT).show(); + } }) .showListener(dialog -> { if (makeTimerPendingIntent(PendingIntent.FLAG_NO_CREATE) != null) { @@ -102,6 +114,9 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { ButterKnife.bind(this, materialDialog.getCustomView()); + boolean finishMusic = PreferenceUtil.getInstance(getActivity()).getSleepTimerFinishMusic(); + shouldFinishLastSong.setChecked(finishMusic); + seekArc.setProgressColor(ThemeSingleton.get().positiveColor.getDefaultColor()); seekArc.setThumbColor(ThemeSingleton.get().positiveColor.getDefaultColor()); @@ -153,8 +168,20 @@ private PendingIntent makeTimerPendingIntent(int flag) { } private Intent makeTimerIntent() { - return new Intent(getActivity(), MusicService.class) - .setAction(MusicService.ACTION_QUIT); + Intent intent = new Intent(getActivity(), MusicService.class); + if (shouldFinishLastSong.isChecked()) { + return intent.setAction(MusicService.ACTION_PENDING_QUIT); + } + return intent.setAction(MusicService.ACTION_QUIT); + } + + private void updateCancelButton() { + MusicService musicService = MusicPlayerRemote.musicService; + if (musicService != null && musicService.pendingQuit) { + materialDialog.setActionButton(DialogAction.NEUTRAL, materialDialog.getContext().getString(R.string.cancel_current_timer)); + } else { + materialDialog.setActionButton(DialogAction.NEUTRAL, null); + } } private class TimerUpdater extends CountDownTimer { @@ -169,7 +196,7 @@ public void onTick(long millisUntilFinished) { @Override public void onFinish() { - materialDialog.setActionButton(DialogAction.NEUTRAL, null); + updateCancelButton(); } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/SongDetailDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/SongDetailDialog.java index f01afdb5b..8416e3a85 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/SongDetailDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/SongDetailDialog.java @@ -4,8 +4,8 @@ import android.app.Dialog; import android.content.Context; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; import android.text.Html; import android.text.Spanned; import android.util.Log; @@ -83,25 +83,32 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { bitRate.setText(makeTextWithTitle(context, R.string.label_bit_rate, "-")); samplingRate.setText(makeTextWithTitle(context, R.string.label_sampling_rate, "-")); - try { - if (song != null) { - final File songFile = new File(song.data); - if (songFile.exists()) { + if (song != null) { + final File songFile = new File(song.data); + if (songFile.exists()) { + fileName.setText(makeTextWithTitle(context, R.string.label_file_name, songFile.getName())); + filePath.setText(makeTextWithTitle(context, R.string.label_file_path, songFile.getAbsolutePath())); + fileSize.setText(makeTextWithTitle(context, R.string.label_file_size, getFileSizeString(songFile.length()))); + try { AudioFile audioFile = AudioFileIO.read(songFile); AudioHeader audioHeader = audioFile.getAudioHeader(); - fileName.setText(makeTextWithTitle(context, R.string.label_file_name, songFile.getName())); - filePath.setText(makeTextWithTitle(context, R.string.label_file_path, songFile.getAbsolutePath())); - fileSize.setText(makeTextWithTitle(context, R.string.label_file_size, getFileSizeString(songFile.length()))); fileFormat.setText(makeTextWithTitle(context, R.string.label_file_format, audioHeader.getFormat())); trackLength.setText(makeTextWithTitle(context, R.string.label_track_length, MusicUtil.getReadableDurationString(audioHeader.getTrackLength() * 1000))); bitRate.setText(makeTextWithTitle(context, R.string.label_bit_rate, audioHeader.getBitRate() + " kb/s")); samplingRate.setText(makeTextWithTitle(context, R.string.label_sampling_rate, audioHeader.getSampleRate() + " Hz")); + } catch (@NonNull CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) { + Log.e(TAG, "error while reading the song file", e); + // fallback + trackLength.setText(makeTextWithTitle(context, R.string.label_track_length, MusicUtil.getReadableDurationString(song.duration))); } + } else { + // fallback + fileName.setText(makeTextWithTitle(context, R.string.label_file_name, song.title)); + trackLength.setText(makeTextWithTitle(context, R.string.label_track_length, MusicUtil.getReadableDurationString(song.duration))); } - } catch (@NonNull CannotReadException | IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) { - Log.e(TAG, "error while reading the song file", e); } + return dialog; } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/dialogs/SongShareDialog.java b/app/src/main/java/com/kabouzeid/gramophone/dialogs/SongShareDialog.java index daad6fc6c..3b904507e 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/dialogs/SongShareDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/dialogs/SongShareDialog.java @@ -3,9 +3,8 @@ import android.app.Dialog; import android.content.Intent; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; -import android.view.View; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; import com.afollestad.materialdialogs.MaterialDialog; import com.kabouzeid.gramophone.R; diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/ArtistGlideRequest.java b/app/src/main/java/com/kabouzeid/gramophone/glide/ArtistGlideRequest.java index 73e9efd55..098d481cb 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/glide/ArtistGlideRequest.java +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/ArtistGlideRequest.java @@ -2,7 +2,10 @@ import android.content.Context; import android.graphics.Bitmap; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; import com.bumptech.glide.BitmapRequestBuilder; import com.bumptech.glide.DrawableRequestBuilder; @@ -15,10 +18,13 @@ import com.bumptech.glide.request.target.Target; import com.kabouzeid.gramophone.App; import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.glide.artistimage.AlbumCover; import com.kabouzeid.gramophone.glide.artistimage.ArtistImage; import com.kabouzeid.gramophone.glide.palette.BitmapPaletteTranscoder; import com.kabouzeid.gramophone.glide.palette.BitmapPaletteWrapper; +import com.kabouzeid.gramophone.model.Album; import com.kabouzeid.gramophone.model.Artist; +import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.util.ArtistSignatureUtil; import com.kabouzeid.gramophone.util.CustomArtistImageUtil; @@ -27,7 +33,7 @@ */ public class ArtistGlideRequest { - private static final DiskCacheStrategy DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.SOURCE; + private static final DiskCacheStrategy DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.ALL; private static final int DEFAULT_ERROR_IMAGE = R.drawable.default_artist_image; public static final int DEFAULT_ANIMATION = android.R.anim.fade_in; @@ -35,7 +41,6 @@ public static class Builder { final RequestManager requestManager; final Artist artist; boolean noCustomImage; - boolean forceDownload; public static Builder from(@NonNull RequestManager requestManager, Artist artist) { return new Builder(requestManager, artist); @@ -59,14 +64,9 @@ public Builder noCustomImage(boolean noCustomImage) { return this; } - public Builder forceDownload(boolean forceDownload) { - this.forceDownload = forceDownload; - return this; - } - public DrawableRequestBuilder build() { //noinspection unchecked - return createBaseRequest(requestManager, artist, noCustomImage, forceDownload) + return createBaseRequest(requestManager, artist, noCustomImage) .diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY) .error(DEFAULT_ERROR_IMAGE) .animate(DEFAULT_ANIMATION) @@ -85,7 +85,7 @@ public BitmapBuilder(Builder builder) { public BitmapRequestBuilder build() { //noinspection unchecked - return createBaseRequest(builder.requestManager, builder.artist, builder.noCustomImage, builder.forceDownload) + return createBaseRequest(builder.requestManager, builder.artist, builder.noCustomImage) .asBitmap() .diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY) .error(DEFAULT_ERROR_IMAGE) @@ -107,7 +107,7 @@ public PaletteBuilder(Builder builder, Context context) { public BitmapRequestBuilder build() { //noinspection unchecked - return createBaseRequest(builder.requestManager, builder.artist, builder.noCustomImage, builder.forceDownload) + return createBaseRequest(builder.requestManager, builder.artist, builder.noCustomImage) .asBitmap() .transcode(new BitmapPaletteTranscoder(context), BitmapPaletteWrapper.class) .diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY) @@ -119,16 +119,21 @@ public BitmapRequestBuilder build() { } } - public static DrawableTypeRequest createBaseRequest(RequestManager requestManager, Artist artist, boolean noCustomImage, boolean forceDownload) { + public static DrawableTypeRequest createBaseRequest(RequestManager requestManager, Artist artist, boolean noCustomImage) { boolean hasCustomImage = CustomArtistImageUtil.getInstance(App.getInstance()).hasCustomArtistImage(artist); if (noCustomImage || !hasCustomImage) { - return requestManager.load(new ArtistImage(artist.getName(), forceDownload)); + final List songs = new ArrayList<>(); + for (final Album album : artist.albums) { + final Song song = album.safeGetFirstSong(); + songs.add(new AlbumCover(album.getYear(), song.data)); + } + return requestManager.load(new ArtistImage(artist.getName(), songs)); } else { return requestManager.load(CustomArtistImageUtil.getFile(artist)); } } - public static Key createSignature(Artist artist) { + private static Key createSignature(Artist artist) { return ArtistSignatureUtil.getInstance(App.getInstance()).getArtistSignature(artist.getName()); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/BlurTransformation.java b/app/src/main/java/com/kabouzeid/gramophone/glide/BlurTransformation.java index 403e26e2b..66f5fde9c 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/glide/BlurTransformation.java +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/BlurTransformation.java @@ -10,8 +10,8 @@ import android.renderscript.RSRuntimeException; import android.renderscript.RenderScript; import android.renderscript.ScriptIntrinsicBlur; -import android.support.annotation.FloatRange; -import android.support.annotation.NonNull; +import androidx.annotation.FloatRange; +import androidx.annotation.NonNull; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/PhonographGlideModule.java b/app/src/main/java/com/kabouzeid/gramophone/glide/PhonographGlideModule.java index 5feb54eb1..407c28e0e 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/glide/PhonographGlideModule.java +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/PhonographGlideModule.java @@ -2,6 +2,8 @@ import android.content.Context; +import java.io.InputStream; + import com.bumptech.glide.Glide; import com.bumptech.glide.GlideBuilder; import com.bumptech.glide.module.GlideModule; @@ -10,8 +12,6 @@ import com.kabouzeid.gramophone.glide.audiocover.AudioFileCover; import com.kabouzeid.gramophone.glide.audiocover.AudioFileCoverLoader; -import java.io.InputStream; - /** * @author Karim Abou Zeid (kabouzeid) */ @@ -24,6 +24,6 @@ public void applyOptions(Context context, GlideBuilder builder) { @Override public void registerComponents(Context context, Glide glide) { glide.register(AudioFileCover.class, InputStream.class, new AudioFileCoverLoader.Factory()); - glide.register(ArtistImage.class, InputStream.class, new ArtistImageLoader.Factory(context)); + glide.register(ArtistImage.class, InputStream.class, new ArtistImageLoader.Factory()); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/SongGlideRequest.java b/app/src/main/java/com/kabouzeid/gramophone/glide/SongGlideRequest.java index 27fe2b0e1..dad073b21 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/glide/SongGlideRequest.java +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/SongGlideRequest.java @@ -2,7 +2,7 @@ import android.content.Context; import android.graphics.Bitmap; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.bumptech.glide.BitmapRequestBuilder; import com.bumptech.glide.DrawableRequestBuilder; diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/AlbumCover.java b/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/AlbumCover.java new file mode 100644 index 000000000..9f026078e --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/AlbumCover.java @@ -0,0 +1,37 @@ +package com.kabouzeid.gramophone.glide.artistimage; + +/** + * Used to define the artist cover + */ +public class AlbumCover { + + private int year; + + private String filePath; + + public AlbumCover(int year, String filePath) { + + this.filePath = filePath; + this.year = year; + } + + public int getYear() { + + return year; + } + + public void setYear(int year) { + + this.year = year; + } + + public String getFilePath() { + + return filePath; + } + + public void setFilePath(String filePath) { + + this.filePath = filePath; + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImage.java b/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImage.java index 4c3803bb6..7dc3daf37 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImage.java +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImage.java @@ -1,14 +1,27 @@ package com.kabouzeid.gramophone.glide.artistimage; +import java.util.List; + /** * @author Karim Abou Zeid (kabouzeid) */ public class ArtistImage { public final String artistName; - public final boolean skipOkHttpCache; - public ArtistImage(String artistName, boolean skipOkHttpCache) { + // filePath to get the image of the artist + public final List albumCovers; + + public ArtistImage(String artistName, final List albumCovers) { + this.artistName = artistName; - this.skipOkHttpCache = skipOkHttpCache; + this.albumCovers = albumCovers; + } + + public String toIdString() { + StringBuilder id = new StringBuilder(artistName); + for (AlbumCover albumCover: albumCovers) { + id.append(albumCover.getYear()).append(albumCover.getFilePath()); + } + return id.toString(); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageFetcher.java b/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageFetcher.java index 1a49230fc..2cba8cee7 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageFetcher.java +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageFetcher.java @@ -1,84 +1,175 @@ package com.kabouzeid.gramophone.glide.artistimage; -import android.content.Context; - -import com.bumptech.glide.Priority; -import com.bumptech.glide.load.data.DataFetcher; -import com.bumptech.glide.load.model.GlideUrl; -import com.bumptech.glide.load.model.ModelLoader; -import com.kabouzeid.gramophone.lastfm.rest.LastFMRestClient; -import com.kabouzeid.gramophone.lastfm.rest.model.LastFmArtist; -import com.kabouzeid.gramophone.util.LastFMUtil; -import com.kabouzeid.gramophone.util.MusicUtil; -import com.kabouzeid.gramophone.util.Util; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.media.MediaMetadataRetriever; +import android.util.Log; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; -import retrofit2.Response; +import com.bumptech.glide.Priority; +import com.bumptech.glide.load.data.DataFetcher; +import com.kabouzeid.gramophone.glide.audiocover.AudioFileCoverUtils; +import com.kabouzeid.gramophone.util.ImageUtil; +import com.kabouzeid.gramophone.util.PreferenceUtil; /** * @author Karim Abou Zeid (kabouzeid) */ public class ArtistImageFetcher implements DataFetcher { - public static final String TAG = ArtistImageFetcher.class.getSimpleName(); - private Context context; - private final LastFMRestClient lastFMRestClient; + private final ArtistImage model; - private ModelLoader urlLoader; - private final int width; - private final int height; - private volatile boolean isCancelled; - private DataFetcher urlFetcher; - - public ArtistImageFetcher(Context context, LastFMRestClient lastFMRestClient, ArtistImage model, ModelLoader urlLoader, int width, int height) { - this.context = context; - this.lastFMRestClient = lastFMRestClient; + + private InputStream stream; + + private boolean ignoreMediaStore; + + public ArtistImageFetcher(final ArtistImage model, boolean ignoreMediaStore) { this.model = model; - this.urlLoader = urlLoader; - this.width = width; - this.height = height; + this.ignoreMediaStore = ignoreMediaStore; } @Override public String getId() { - // makes sure we never ever return null here - return String.valueOf(model.artistName); + Log.d("MOSAIC", "get id for" + model.artistName); + // never return NULL here! + // this id is used to determine whether the image is already cached + // we use the artist name as well as the album years + file paths + return model.toIdString() + "ignoremediastore:" + ignoreMediaStore; } @Override public InputStream loadData(Priority priority) throws Exception { - if (!MusicUtil.isArtistNameUnknown(model.artistName) && Util.isAllowedToDownloadMetadata(context)) { - Response response = lastFMRestClient.getApiService().getArtistInfo(model.artistName, null, model.skipOkHttpCache ? "no-cache" : null).execute(); + Log.d("MOSAIC", "load data for" + model.artistName); + return stream = getMosaic(model.albumCovers); + } + + private InputStream getMosaic(final List albumCovers) throws FileNotFoundException { + + MediaMetadataRetriever retriever = new MediaMetadataRetriever(); + + int artistBitMapSize = 512; + + final Map images = new HashMap<>(); + + InputStream result = null; + List streams = new ArrayList<>(); + + try { + for (final AlbumCover cover : albumCovers) { + byte[] picture = null; + if (!ignoreMediaStore) { + retriever.setDataSource(cover.getFilePath()); + picture = retriever.getEmbeddedPicture(); + } + final InputStream stream; + if (picture != null) { + stream = new ByteArrayInputStream(picture); + } else { + stream = AudioFileCoverUtils.fallback(cover.getFilePath()); + } - if (!response.isSuccessful()) { - throw new IOException("Request failed with code: " + response.code()); + if (stream != null) { + images.put(stream, cover.getYear()); + } } - LastFmArtist lastFmArtist = response.body(); + int nbImages = images.size(); - if (isCancelled) return null; + if (nbImages > 3) { + streams = new ArrayList<>(images.keySet()); - GlideUrl url = new GlideUrl(LastFMUtil.getLargestArtistImageUrl(lastFmArtist.getArtist().getImage())); - urlFetcher = urlLoader.getResourceFetcher(url, width, height); + int divisor = 1; + for (int i = 1; i < nbImages && Math.pow(i, 2) <= nbImages; ++i) { + divisor = i; + } + divisor += 1; + double nbTiles = Math.pow(divisor, 2); + + if (nbImages < nbTiles) { + divisor -= 1; + nbTiles = Math.pow(divisor, 2); + } + final int resize = (artistBitMapSize / divisor) + 1; + + final Bitmap bitmap = Bitmap.createBitmap(artistBitMapSize, artistBitMapSize, Bitmap.Config.RGB_565); + final Canvas canvas = new Canvas(bitmap); + + int x = 0; + int y = 0; + + for (int i = 0; i < streams.size() && i < nbTiles; ++i) { + final Bitmap bitmap1 = ImageUtil.resize(streams.get(i), resize, resize); + canvas.drawBitmap(bitmap1, x, y, null); + x += resize; + + if (x >= artistBitMapSize) { + x = 0; + y += resize; + } + } + + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos); + result = new ByteArrayInputStream(bos.toByteArray()); + + } else if (nbImages > 0) { + // we return the last cover album of the artist + Map.Entry maxEntryYear = null; + + for (final Map.Entry entry : images.entrySet()) { + if (maxEntryYear == null || entry.getValue() + .compareTo(maxEntryYear.getValue()) > 0) { + maxEntryYear = entry; + } + } + + if (maxEntryYear != null) { + result = maxEntryYear.getKey(); + } else { + result = images.entrySet() + .iterator() + .next() + .getKey(); + } + + } + } finally { + retriever.release(); + try { + for (final InputStream stream : streams) { + stream.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } - return urlFetcher.loadData(priority); } - return null; + return result; } @Override public void cleanup() { - if (urlFetcher != null) { - urlFetcher.cleanup(); + // already cleaned up in loadData and ByteArrayInputStream will be GC'd + if (stream != null) { + try { + stream.close(); + } catch (IOException ignore) { + // can't do much about it + } } } @Override public void cancel() { - isCancelled = true; - if (urlFetcher != null) { - urlFetcher.cancel(); - } + } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageLoader.java b/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageLoader.java index f0f4b972c..ea6008031 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageLoader.java +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageLoader.java @@ -2,68 +2,42 @@ import android.content.Context; -import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader; +import java.io.InputStream; + import com.bumptech.glide.load.data.DataFetcher; import com.bumptech.glide.load.model.GenericLoaderFactory; -import com.bumptech.glide.load.model.GlideUrl; import com.bumptech.glide.load.model.ModelLoader; import com.bumptech.glide.load.model.ModelLoaderFactory; import com.bumptech.glide.load.model.stream.StreamModelLoader; -import com.kabouzeid.gramophone.lastfm.rest.LastFMRestClient; - -import java.io.InputStream; -import java.util.concurrent.TimeUnit; - -import okhttp3.OkHttpClient; +import com.kabouzeid.gramophone.util.PreferenceUtil; /** * @author Karim Abou Zeid (kabouzeid) */ public class ArtistImageLoader implements StreamModelLoader { - // we need these very low values to make sure our artist image loading calls doesn't block the image loading queue - private static final int TIMEOUT = 500; - private Context context; - private LastFMRestClient lastFMClient; - private ModelLoader urlLoader; - public ArtistImageLoader(Context context, LastFMRestClient lastFMRestClient, ModelLoader urlLoader) { + public ArtistImageLoader(Context context) { this.context = context; - this.lastFMClient = lastFMRestClient; - this.urlLoader = urlLoader; } @Override - public DataFetcher getResourceFetcher(ArtistImage model, int width, int height) { - return new ArtistImageFetcher(context, lastFMClient, model, urlLoader, width, height); + public DataFetcher getResourceFetcher(final ArtistImage model, int width, int height) { + + return new ArtistImageFetcher(model, PreferenceUtil.getInstance(context).ignoreMediaStoreArtwork()); } public static class Factory implements ModelLoaderFactory { - private LastFMRestClient lastFMClient; - private OkHttpUrlLoader.Factory okHttpFactory; - - public Factory(Context context) { - okHttpFactory = new OkHttpUrlLoader.Factory(new OkHttpClient.Builder() - .connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS) - .readTimeout(TIMEOUT, TimeUnit.MILLISECONDS) - .writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS) - .build()); - lastFMClient = new LastFMRestClient(LastFMRestClient.createDefaultOkHttpClientBuilder(context) - .connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS) - .readTimeout(TIMEOUT, TimeUnit.MILLISECONDS) - .writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS) - .build()); - } @Override public ModelLoader build(Context context, GenericLoaderFactory factories) { - return new ArtistImageLoader(context, lastFMClient, okHttpFactory.build(context, factories)); + return new ArtistImageLoader(context); } @Override public void teardown() { - okHttpFactory.teardown(); + } } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/audiocover/AudioFileCoverFetcher.java b/app/src/main/java/com/kabouzeid/gramophone/glide/audiocover/AudioFileCoverFetcher.java index e97cbc29c..e1deb6aab 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/glide/audiocover/AudioFileCoverFetcher.java +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/audiocover/AudioFileCoverFetcher.java @@ -2,24 +2,23 @@ import android.media.MediaMetadataRetriever; -import com.bumptech.glide.Priority; -import com.bumptech.glide.load.data.DataFetcher; - import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import com.bumptech.glide.Priority; +import com.bumptech.glide.load.data.DataFetcher; + /** * @author Karim Abou Zeid (kabouzeid) */ public class AudioFileCoverFetcher implements DataFetcher { private final AudioFileCover model; - private FileInputStream stream; + + private InputStream stream; public AudioFileCoverFetcher(AudioFileCover model) { + this.model = model; } @@ -30,32 +29,22 @@ public String getId() { } @Override - public InputStream loadData(Priority priority) throws Exception { - MediaMetadataRetriever retriever = new MediaMetadataRetriever(); + public InputStream loadData(final Priority priority) throws Exception { + + final MediaMetadataRetriever retriever = new MediaMetadataRetriever(); try { retriever.setDataSource(model.filePath); byte[] picture = retriever.getEmbeddedPicture(); if (picture != null) { - return new ByteArrayInputStream(picture); + stream = new ByteArrayInputStream(picture); } else { - return fallback(model.filePath); + stream = AudioFileCoverUtils.fallback(model.filePath); } } finally { retriever.release(); } - } - private static final String[] FALLBACKS = {"cover.jpg", "album.jpg", "folder.jpg"}; - - private InputStream fallback(String path) throws FileNotFoundException { - File parent = new File(path).getParentFile(); - for (String fallback : FALLBACKS) { - File cover = new File(parent, fallback); - if (cover.exists()) { - return stream = new FileInputStream(cover); - } - } - return null; + return stream; } @Override diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/audiocover/AudioFileCoverUtils.java b/app/src/main/java/com/kabouzeid/gramophone/glide/audiocover/AudioFileCoverUtils.java new file mode 100644 index 000000000..51cbf994f --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/audiocover/AudioFileCoverUtils.java @@ -0,0 +1,49 @@ +package com.kabouzeid.gramophone.glide.audiocover; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException; +import org.jaudiotagger.audio.exceptions.ReadOnlyFileException; +import org.jaudiotagger.audio.mp3.MP3File; +import org.jaudiotagger.tag.TagException; +import org.jaudiotagger.tag.images.Artwork; + +public class AudioFileCoverUtils { + + public static final String[] FALLBACKS = {"cover.jpg", "album.jpg", "folder.jpg", "cover.png", "album.png", "folder.png"}; + + + public static InputStream fallback(String path) throws FileNotFoundException { + // Method 1: use embedded high resolution album art if there is any + try { + MP3File mp3File = new MP3File(path); + if (mp3File.hasID3v2Tag()) { + Artwork art = mp3File.getTag().getFirstArtwork(); + if (art != null) { + byte[] imageData = art.getBinaryData(); + return new ByteArrayInputStream(imageData); + } + } + // If there are any exceptions, we ignore them and continue to the other fallback method + } catch (ReadOnlyFileException ignored) { + } catch (InvalidAudioFrameException ignored) { + } catch (TagException ignored) { + } catch (IOException ignored) { + } + + // Method 2: look for album art in external files + final File parent = new File(path).getParentFile(); + for (String fallback : FALLBACKS) { + File cover = new File(parent, fallback); + if (cover.exists()) { + return new FileInputStream(cover); + } + } + return null; + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/palette/BitmapPaletteWrapper.java b/app/src/main/java/com/kabouzeid/gramophone/glide/palette/BitmapPaletteWrapper.java index 441b98e95..ff28ee08e 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/glide/palette/BitmapPaletteWrapper.java +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/palette/BitmapPaletteWrapper.java @@ -1,7 +1,7 @@ package com.kabouzeid.gramophone.glide.palette; import android.graphics.Bitmap; -import android.support.v7.graphics.Palette; +import androidx.palette.graphics.Palette; public class BitmapPaletteWrapper { private final Bitmap mBitmap; diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/M3UWriter.java b/app/src/main/java/com/kabouzeid/gramophone/helper/M3UWriter.java index d137143e9..7c36c24cd 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/M3UWriter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/M3UWriter.java @@ -12,16 +12,16 @@ import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; +import java.util.List; public class M3UWriter implements M3UConstants { - public static final String TAG = M3UWriter.class.getSimpleName(); public static File write(Context context, File dir, Playlist playlist) throws IOException { if (!dir.exists()) //noinspection ResultOfMethodCallIgnored dir.mkdirs(); File file = new File(dir, playlist.name.concat("." + EXTENSION)); - ArrayList songs; + List songs; if (playlist instanceof AbsCustomPlaylist) { songs = ((AbsCustomPlaylist) playlist).getSongs(context); } else { @@ -44,4 +44,4 @@ public static File write(Context context, File dir, Playlist playlist) throws IO return file; } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java b/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java index 6a4bdeeaa..250cef0b2 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java @@ -15,8 +15,8 @@ import android.os.IBinder; import android.provider.DocumentsContract; import android.provider.MediaStore; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import android.util.Log; import android.widget.Toast; @@ -24,9 +24,11 @@ import com.kabouzeid.gramophone.loader.SongLoader; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.service.MusicService; +import com.kabouzeid.gramophone.util.PreferenceUtil; import java.io.File; import java.util.ArrayList; +import java.util.List; import java.util.Random; import java.util.WeakHashMap; @@ -173,16 +175,19 @@ public static void resumePlaying() { /** * Async */ - public static void openQueue(final ArrayList queue, final int startPosition, final boolean startPlaying) { + public static void openQueue(final List queue, final int startPosition, final boolean startPlaying) { if (!tryToHandleOpenPlayingQueue(queue, startPosition, startPlaying) && musicService != null) { musicService.openQueue(queue, startPosition, startPlaying); + if (!PreferenceUtil.getInstance(musicService).rememberShuffle()){ + setShuffleMode(MusicService.SHUFFLE_MODE_NONE); + } } } /** * Async */ - public static void openAndShuffleQueue(final ArrayList queue, boolean startPlaying) { + public static void openAndShuffleQueue(final List queue, boolean startPlaying) { int startPosition = 0; if (!queue.isEmpty()) { startPosition = new Random().nextInt(queue.size()); @@ -194,7 +199,7 @@ public static void openAndShuffleQueue(final ArrayList queue, boolean star } } - private static boolean tryToHandleOpenPlayingQueue(final ArrayList queue, final int startPosition, final boolean startPlaying) { + private static boolean tryToHandleOpenPlayingQueue(final List queue, final int startPosition, final boolean startPlaying) { if (getPlayingQueue() == queue) { if (startPlaying) { playSongAt(startPosition); @@ -220,7 +225,7 @@ public static int getPosition() { return -1; } - public static ArrayList getPlayingQueue() { + public static List getPlayingQueue() { if (musicService != null) { return musicService.getPlayingQueue(); } @@ -298,7 +303,7 @@ public static boolean playNext(Song song) { if (getPlayingQueue().size() > 0) { musicService.addSong(getPosition() + 1, song); } else { - ArrayList queue = new ArrayList<>(); + List queue = new ArrayList<>(); queue.add(song); openQueue(queue, 0, false); } @@ -308,7 +313,7 @@ public static boolean playNext(Song song) { return false; } - public static boolean playNext(@NonNull ArrayList songs) { + public static boolean playNext(@NonNull List songs) { if (musicService != null) { if (getPlayingQueue().size() > 0) { musicService.addSongs(getPosition() + 1, songs); @@ -327,7 +332,7 @@ public static boolean enqueue(Song song) { if (getPlayingQueue().size() > 0) { musicService.addSong(song); } else { - ArrayList queue = new ArrayList<>(); + List queue = new ArrayList<>(); queue.add(song); openQueue(queue, 0, false); } @@ -337,7 +342,7 @@ public static boolean enqueue(Song song) { return false; } - public static boolean enqueue(@NonNull ArrayList songs) { + public static boolean enqueue(@NonNull List songs) { if (musicService != null) { if (getPlayingQueue().size() > 0) { musicService.addSongs(songs); @@ -392,7 +397,7 @@ public static int getAudioSessionId() { public static void playFromUri(Uri uri) { if (musicService != null) { - ArrayList songs = null; + List songs = null; if (uri.getScheme() != null && uri.getAuthority() != null) { if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) { String songId = null; diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/MusicProgressViewUpdateHelper.java b/app/src/main/java/com/kabouzeid/gramophone/helper/MusicProgressViewUpdateHelper.java index 4f912e890..c3c8fe787 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/MusicProgressViewUpdateHelper.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/MusicProgressViewUpdateHelper.java @@ -2,7 +2,7 @@ import android.os.Handler; import android.os.Message; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; /** * @author Karim Abou Zeid (kabouzeid) diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/SearchQueryHelper.java b/app/src/main/java/com/kabouzeid/gramophone/helper/SearchQueryHelper.java index 210846f5c..b11bd335b 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/SearchQueryHelper.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/SearchQueryHelper.java @@ -4,12 +4,13 @@ import android.content.Context; import android.os.Bundle; import android.provider.MediaStore; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.kabouzeid.gramophone.loader.SongLoader; import com.kabouzeid.gramophone.model.Song; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) @@ -21,68 +22,68 @@ public class SearchQueryHelper { private static final String AND = " AND "; @NonNull - public static ArrayList getSongs(@NonNull final Context context, @NonNull final Bundle extras) { + public static List getSongs(@NonNull final Context context, @NonNull final Bundle extras) { final String query = extras.getString(SearchManager.QUERY, null); final String artistName = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST, null); final String albumName = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM, null); final String titleName = extras.getString(MediaStore.EXTRA_MEDIA_TITLE, null); - ArrayList songs = new ArrayList<>(); + List songs = new ArrayList<>(); if (artistName != null && albumName != null && titleName != null) { - songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION, new String[]{artistName.toLowerCase(), albumName.toLowerCase(), titleName.toLowerCase()})); + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION, new String[]{artistName.toLowerCase().trim(), albumName.toLowerCase().trim(), titleName.toLowerCase().trim()})); } if (!songs.isEmpty()) { return songs; } if (artistName != null && titleName != null) { - songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION + AND + TITLE_SELECTION, new String[]{artistName.toLowerCase(), titleName.toLowerCase()})); + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION + AND + TITLE_SELECTION, new String[]{artistName.toLowerCase().trim(), titleName.toLowerCase().trim()})); } if (!songs.isEmpty()) { return songs; } if (albumName != null && titleName != null) { - songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ALBUM_SELECTION + AND + TITLE_SELECTION, new String[]{albumName.toLowerCase(), titleName.toLowerCase()})); + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ALBUM_SELECTION + AND + TITLE_SELECTION, new String[]{albumName.toLowerCase().trim(), titleName.toLowerCase().trim()})); } if (!songs.isEmpty()) { return songs; } if (artistName != null) { - songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION, new String[]{artistName.toLowerCase()})); + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION, new String[]{artistName.toLowerCase().trim()})); } if (!songs.isEmpty()) { return songs; } if (albumName != null) { - songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ALBUM_SELECTION, new String[]{albumName.toLowerCase()})); + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ALBUM_SELECTION, new String[]{albumName.toLowerCase().trim()})); } if (!songs.isEmpty()) { return songs; } if (titleName != null) { - songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, TITLE_SELECTION, new String[]{titleName.toLowerCase()})); + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, TITLE_SELECTION, new String[]{titleName.toLowerCase().trim()})); } if (!songs.isEmpty()) { return songs; } - songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION, new String[]{query.toLowerCase()})); + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION, new String[]{query.toLowerCase().trim()})); if (!songs.isEmpty()) { return songs; } - songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ALBUM_SELECTION, new String[]{query.toLowerCase()})); + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ALBUM_SELECTION, new String[]{query.toLowerCase().trim()})); if (!songs.isEmpty()) { return songs; } - songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, TITLE_SELECTION, new String[]{query.toLowerCase()})); + songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, TITLE_SELECTION, new String[]{query.toLowerCase().trim()})); if (!songs.isEmpty()) { return songs; } diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/ShuffleHelper.java b/app/src/main/java/com/kabouzeid/gramophone/helper/ShuffleHelper.java index c55da086f..fb41098f0 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/ShuffleHelper.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/ShuffleHelper.java @@ -1,6 +1,6 @@ package com.kabouzeid.gramophone.helper; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.kabouzeid.gramophone.model.Song; diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/SortOrder.java b/app/src/main/java/com/kabouzeid/gramophone/helper/SortOrder.java index ae805959f..89e750e26 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/SortOrder.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/SortOrder.java @@ -60,11 +60,11 @@ public interface AlbumSortOrder { + " DESC"; /* Album sort order artist */ - String ALBUM_ARTIST = MediaStore.Audio.Albums.ARTIST; + String ALBUM_ARTIST = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER + + ", " + MediaStore.Audio.Albums.DEFAULT_SORT_ORDER; /* Album sort order year */ - String ALBUM_YEAR = MediaStore.Audio.Albums.FIRST_YEAR + " DESC"; - + String ALBUM_YEAR = MediaStore.Audio.Media.YEAR + " DESC"; } /** @@ -78,10 +78,10 @@ public interface SongSortOrder { String SONG_Z_A = SONG_A_Z + " DESC"; /* Song sort order artist */ - String SONG_ARTIST = MediaStore.Audio.Media.ARTIST; + String SONG_ARTIST = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER; /* Song sort order album */ - String SONG_ALBUM = MediaStore.Audio.Media.ALBUM; + String SONG_ALBUM = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER; /* Song sort order year */ String SONG_YEAR = MediaStore.Audio.Media.YEAR + " DESC"; diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/StackBlur.java b/app/src/main/java/com/kabouzeid/gramophone/helper/StackBlur.java index 535d2a7ac..c810f7f06 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/StackBlur.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/StackBlur.java @@ -3,6 +3,7 @@ import android.graphics.Bitmap; import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -83,8 +84,8 @@ public static Bitmap blur(Bitmap original, float radius) { original.getPixels(currentPixels, 0, w, 0, 0, w, h); int cores = EXECUTOR_THREADS; - ArrayList horizontal = new ArrayList(cores); - ArrayList vertical = new ArrayList(cores); + List horizontal = new ArrayList<>(cores); + List vertical = new ArrayList<>(cores); for (int i = 0; i < cores; i++) { horizontal.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 1)); vertical.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 2)); diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/StopWatch.java b/app/src/main/java/com/kabouzeid/gramophone/helper/StopWatch.java index d65f90751..581d8fc20 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/StopWatch.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/StopWatch.java @@ -1,5 +1,7 @@ package com.kabouzeid.gramophone.helper; +import java.util.Locale; + /** * Simple thread safe stop watch. * @@ -77,6 +79,6 @@ public final long getElapsedTime() { @Override public String toString() { - return String.format("%d millis", getElapsedTime()); + return String.format(Locale.getDefault(), "%d millis", getElapsedTime()); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/menu/PlaylistMenuHelper.java b/app/src/main/java/com/kabouzeid/gramophone/helper/menu/PlaylistMenuHelper.java index 7b405085a..13067c1b7 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/menu/PlaylistMenuHelper.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/menu/PlaylistMenuHelper.java @@ -1,20 +1,20 @@ package com.kabouzeid.gramophone.helper.menu; -import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; -import android.os.AsyncTask; -import android.support.annotation.NonNull; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; import android.view.MenuItem; import android.widget.Toast; +import com.kabouzeid.gramophone.App; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog; import com.kabouzeid.gramophone.dialogs.DeletePlaylistDialog; import com.kabouzeid.gramophone.dialogs.RenamePlaylistDialog; import com.kabouzeid.gramophone.helper.MusicPlayerRemote; import com.kabouzeid.gramophone.loader.PlaylistSongLoader; +import com.kabouzeid.gramophone.misc.WeakContextAsyncTask; import com.kabouzeid.gramophone.model.AbsCustomPlaylist; import com.kabouzeid.gramophone.model.Playlist; import com.kabouzeid.gramophone.model.Song; @@ -22,6 +22,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) @@ -48,43 +49,42 @@ public static boolean handleMenuClick(@NonNull AppCompatActivity activity, @NonN DeletePlaylistDialog.create(playlist).show(activity.getSupportFragmentManager(), "DELETE_PLAYLIST"); return true; case R.id.action_save_playlist: - @SuppressLint("ShowToast") - final Toast toast = Toast.makeText(activity, R.string.saving_to_file, Toast.LENGTH_SHORT); - new AsyncTask() { - @Override - protected void onPreExecute() { - super.onPreExecute(); - toast.show(); - } - - @Override - protected String doInBackground(Context... params) { - try { - return String.format(params[0].getString(R.string.saved_playlist_to), PlaylistsUtil.savePlaylist(params[0], playlist)); - } catch (IOException e) { - e.printStackTrace(); - return String.format(params[0].getString(R.string.failed_to_save_playlist), e); - } - } - - @Override - protected void onPostExecute(String string) { - super.onPostExecute(string); - if (toast != null) { - toast.setText(string); - toast.show(); - } - } - }.execute(activity.getApplicationContext()); + new SavePlaylistAsyncTask(activity).execute(playlist); return true; } return false; } @NonNull - private static ArrayList getPlaylistSongs(@NonNull Activity activity, Playlist playlist) { + private static List getPlaylistSongs(@NonNull Activity activity, Playlist playlist) { return playlist instanceof AbsCustomPlaylist ? ((AbsCustomPlaylist) playlist).getSongs(activity) : PlaylistSongLoader.getPlaylistSongList(activity, playlist.id); } + + + private static class SavePlaylistAsyncTask extends WeakContextAsyncTask { + public SavePlaylistAsyncTask(Context context) { + super(context); + } + + @Override + protected String doInBackground(Playlist... params) { + try { + return String.format(App.getInstance().getApplicationContext().getString(R.string.saved_playlist_to), PlaylistsUtil.savePlaylist(App.getInstance().getApplicationContext(), params[0])); + } catch (IOException e) { + e.printStackTrace(); + return String.format(App.getInstance().getApplicationContext().getString(R.string.failed_to_save_playlist), e); + } + } + + @Override + protected void onPostExecute(String string) { + super.onPostExecute(string); + Context context = getContext(); + if (context != null) { + Toast.makeText(context, string, Toast.LENGTH_LONG).show(); + } + } + } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/menu/SongMenuHelper.java b/app/src/main/java/com/kabouzeid/gramophone/helper/menu/SongMenuHelper.java index a3cedbd22..b55c7f53e 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/menu/SongMenuHelper.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/menu/SongMenuHelper.java @@ -1,9 +1,9 @@ package com.kabouzeid.gramophone.helper.menu; import android.content.Intent; -import android.support.annotation.NonNull; -import android.support.v4.app.FragmentActivity; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentActivity; +import androidx.appcompat.app.AppCompatActivity; import android.view.MenuItem; import android.view.View; import android.widget.PopupMenu; @@ -19,6 +19,7 @@ import com.kabouzeid.gramophone.ui.activities.tageditor.SongTagEditorActivity; import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.NavigationUtil; +import com.kabouzeid.gramophone.util.RingtoneManager; /** * @author Karim Abou Zeid (kabouzeid) @@ -29,7 +30,12 @@ public class SongMenuHelper { public static boolean handleMenuClick(@NonNull FragmentActivity activity, @NonNull Song song, int menuItemId) { switch (menuItemId) { case R.id.action_set_as_ringtone: - MusicUtil.setRingtone(activity, song.id); + if (RingtoneManager.requiresDialog(activity)) { + RingtoneManager.showDialog(activity); + } else { + RingtoneManager ringtoneManager = new RingtoneManager(); + ringtoneManager.setRingtone(activity, song.id); + } return true; case R.id.action_share: activity.startActivity(Intent.createChooser(MusicUtil.createShareSongFileIntent(song, activity), null)); diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/menu/SongsMenuHelper.java b/app/src/main/java/com/kabouzeid/gramophone/helper/menu/SongsMenuHelper.java index 5adb08688..f3ce04fee 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/menu/SongsMenuHelper.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/menu/SongsMenuHelper.java @@ -1,7 +1,7 @@ package com.kabouzeid.gramophone.helper.menu; -import android.support.annotation.NonNull; -import android.support.v4.app.FragmentActivity; +import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentActivity; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog; @@ -10,12 +10,13 @@ import com.kabouzeid.gramophone.model.Song; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ public class SongsMenuHelper { - public static boolean handleMenuClick(@NonNull FragmentActivity activity, @NonNull ArrayList songs, int menuItemId) { + public static boolean handleMenuClick(@NonNull FragmentActivity activity, @NonNull List songs, int menuItemId) { switch (menuItemId) { case R.id.action_play_next: MusicPlayerRemote.playNext(songs); diff --git a/app/src/main/java/com/kabouzeid/gramophone/interfaces/CabHolder.java b/app/src/main/java/com/kabouzeid/gramophone/interfaces/CabHolder.java index 7818c2345..a7e93cd40 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/interfaces/CabHolder.java +++ b/app/src/main/java/com/kabouzeid/gramophone/interfaces/CabHolder.java @@ -1,6 +1,6 @@ package com.kabouzeid.gramophone.interfaces; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.afollestad.materialcab.MaterialCab; diff --git a/app/src/main/java/com/kabouzeid/gramophone/interfaces/PaletteColorHolder.java b/app/src/main/java/com/kabouzeid/gramophone/interfaces/PaletteColorHolder.java index b6df7c74d..6036c1e67 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/interfaces/PaletteColorHolder.java +++ b/app/src/main/java/com/kabouzeid/gramophone/interfaces/PaletteColorHolder.java @@ -1,6 +1,6 @@ package com.kabouzeid.gramophone.interfaces; -import android.support.annotation.ColorInt; +import androidx.annotation.ColorInt; /** * @author Aidan Follestad (afollestad) diff --git a/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/LastFMRestClient.java b/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/LastFMRestClient.java index 65fb8e5fb..c5ac4743a 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/LastFMRestClient.java +++ b/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/LastFMRestClient.java @@ -1,20 +1,19 @@ package com.kabouzeid.gramophone.lastfm.rest; import android.content.Context; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.kabouzeid.gramophone.lastfm.rest.service.LastFMService; import java.io.File; -import java.io.IOException; +import java.util.Locale; import okhttp3.Cache; import okhttp3.Call; import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; -import okhttp3.Response; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; @@ -22,7 +21,7 @@ * @author Karim Abou Zeid (kabouzeid) */ public class LastFMRestClient { - public static final String BASE_URL = "http://ws.audioscrobbler.com/2.0/"; + public static final String BASE_URL = "https://ws.audioscrobbler.com/2.0/"; private LastFMService apiService; @@ -56,7 +55,7 @@ public static Cache createDefaultCache(Context context) { public static Interceptor createCacheControlInterceptor() { return chain -> { Request modifiedRequest = chain.request().newBuilder() - .addHeader("Cache-Control", String.format("max-age=%d, max-stale=%d", 31536000, 31536000)) + .addHeader("Cache-Control", String.format(Locale.getDefault(), "max-age=%d, max-stale=%d", 31536000, 31536000)) .build(); return chain.proceed(modifiedRequest); }; diff --git a/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/service/LastFMService.java b/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/service/LastFMService.java index 4a9b33c79..01be3e72f 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/service/LastFMService.java +++ b/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/service/LastFMService.java @@ -1,6 +1,6 @@ package com.kabouzeid.gramophone.lastfm.rest.service; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import com.kabouzeid.gramophone.lastfm.rest.model.LastFmAlbum; import com.kabouzeid.gramophone.lastfm.rest.model.LastFmArtist; diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/AlbumLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/AlbumLoader.java index 89657a628..7956cc7d5 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/loader/AlbumLoader.java +++ b/app/src/main/java/com/kabouzeid/gramophone/loader/AlbumLoader.java @@ -2,14 +2,16 @@ import android.content.Context; import android.provider.MediaStore.Audio.AudioColumns; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.kabouzeid.gramophone.model.Album; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.util.PreferenceUtil; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) @@ -21,8 +23,8 @@ public static String getSongLoaderSortOrder(Context context) { } @NonNull - public static ArrayList getAllAlbums(@NonNull final Context context) { - ArrayList songs = SongLoader.getSongs(SongLoader.makeSongCursor( + public static List getAllAlbums(@NonNull final Context context) { + List songs = SongLoader.getSongs(SongLoader.makeSongCursor( context, null, null, @@ -32,8 +34,8 @@ public static ArrayList getAllAlbums(@NonNull final Context context) { } @NonNull - public static ArrayList getAlbums(@NonNull final Context context, String query) { - ArrayList songs = SongLoader.getSongs(SongLoader.makeSongCursor( + public static List getAlbums(@NonNull final Context context, String query) { + List songs = SongLoader.getSongs(SongLoader.makeSongCursor( context, AudioColumns.ALBUM + " LIKE ?", new String[]{"%" + query + "%"}, @@ -44,22 +46,27 @@ public static ArrayList getAlbums(@NonNull final Context context, String @NonNull public static Album getAlbum(@NonNull final Context context, int albumId) { - ArrayList songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, AudioColumns.ALBUM_ID + "=?", new String[]{String.valueOf(albumId)}, getSongLoaderSortOrder(context))); - return new Album(songs); + List songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, AudioColumns.ALBUM_ID + "=?", new String[]{String.valueOf(albumId)}, getSongLoaderSortOrder(context))); + Album album = new Album(songs); + sortSongsByTrackNumber(album); + return album; } @NonNull - public static ArrayList splitIntoAlbums(@Nullable final ArrayList songs) { - ArrayList albums = new ArrayList<>(); + public static List splitIntoAlbums(@Nullable final List songs) { + List albums = new ArrayList<>(); if (songs != null) { for (Song song : songs) { getOrCreateAlbum(albums, song.albumId).songs.add(song); } } + for (Album album : albums) { + sortSongsByTrackNumber(album); + } return albums; } - private static Album getOrCreateAlbum(ArrayList albums, int albumId) { + private static Album getOrCreateAlbum(List albums, int albumId) { for (Album album : albums) { if (!album.songs.isEmpty() && album.songs.get(0).albumId == albumId) { return album; @@ -69,4 +76,8 @@ private static Album getOrCreateAlbum(ArrayList albums, int albumId) { albums.add(album); return album; } + + private static void sortSongsByTrackNumber(Album album) { + Collections.sort(album.songs, (o1, o2) -> o1.trackNumber - o2.trackNumber); + } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistLoader.java index 566c64f71..ea8de5f80 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistLoader.java +++ b/app/src/main/java/com/kabouzeid/gramophone/loader/ArtistLoader.java @@ -2,8 +2,8 @@ import android.content.Context; import android.provider.MediaStore.Audio.AudioColumns; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.kabouzeid.gramophone.model.Album; import com.kabouzeid.gramophone.model.Artist; @@ -11,6 +11,7 @@ import com.kabouzeid.gramophone.util.PreferenceUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) @@ -21,8 +22,8 @@ public static String getSongLoaderSortOrder(Context context) { } @NonNull - public static ArrayList getAllArtists(@NonNull final Context context) { - ArrayList songs = SongLoader.getSongs(SongLoader.makeSongCursor( + public static List getAllArtists(@NonNull final Context context) { + List songs = SongLoader.getSongs(SongLoader.makeSongCursor( context, null, null, @@ -32,8 +33,8 @@ public static ArrayList getAllArtists(@NonNull final Context context) { } @NonNull - public static ArrayList getArtists(@NonNull final Context context, String query) { - ArrayList songs = SongLoader.getSongs(SongLoader.makeSongCursor( + public static List getArtists(@NonNull final Context context, String query) { + List songs = SongLoader.getSongs(SongLoader.makeSongCursor( context, AudioColumns.ARTIST + " LIKE ?", new String[]{"%" + query + "%"}, @@ -44,7 +45,7 @@ public static ArrayList getArtists(@NonNull final Context context, Strin @NonNull public static Artist getArtist(@NonNull final Context context, int artistId) { - ArrayList songs = SongLoader.getSongs(SongLoader.makeSongCursor( + List songs = SongLoader.getSongs(SongLoader.makeSongCursor( context, AudioColumns.ARTIST_ID + "=?", new String[]{String.valueOf(artistId)}, @@ -54,8 +55,8 @@ public static Artist getArtist(@NonNull final Context context, int artistId) { } @NonNull - public static ArrayList splitIntoArtists(@Nullable final ArrayList albums) { - ArrayList artists = new ArrayList<>(); + public static List splitIntoArtists(@Nullable final List albums) { + List artists = new ArrayList<>(); if (albums != null) { for (Album album : albums) { getOrCreateArtist(artists, album.getArtistId()).albums.add(album); @@ -64,14 +65,14 @@ public static ArrayList splitIntoArtists(@Nullable final ArrayList artists, int artistId) { + private static Artist getOrCreateArtist(List artists, int artistId) { for (Artist artist : artists) { if (!artist.albums.isEmpty() && !artist.albums.get(0).songs.isEmpty() && artist.albums.get(0).songs.get(0).artistId == artistId) { return artist; } } - Artist album = new Artist(); - artists.add(album); - return album; + Artist artist = new Artist(); + artists.add(artist); + return artist; } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/GenreLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/GenreLoader.java index 5fcc3c0f9..1a4b74f1a 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/loader/GenreLoader.java +++ b/app/src/main/java/com/kabouzeid/gramophone/loader/GenreLoader.java @@ -3,30 +3,31 @@ import android.content.Context; import android.database.Cursor; import android.provider.MediaStore.Audio.Genres; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.kabouzeid.gramophone.model.Genre; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.util.PreferenceUtil; import java.util.ArrayList; +import java.util.List; public class GenreLoader { @NonNull - public static ArrayList getAllGenres(@NonNull final Context context) { + public static List getAllGenres(@NonNull final Context context) { return getGenresFromCursor(context, makeGenreCursor(context)); } @NonNull - public static ArrayList getSongs(@NonNull final Context context, final int genreId) { + public static List getSongs(@NonNull final Context context, final int genreId) { return SongLoader.getSongs(makeGenreSongCursor(context, genreId)); } @NonNull - private static ArrayList getGenresFromCursor(@NonNull final Context context, @Nullable final Cursor cursor) { - final ArrayList genres = new ArrayList<>(); + private static List getGenresFromCursor(@NonNull final Context context, @Nullable final Cursor cursor) { + final List genres = new ArrayList<>(); if (cursor != null) { if (cursor.moveToFirst()) { do { diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/LastAddedLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/LastAddedLoader.java index 44364df65..2c3d4f9f0 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/loader/LastAddedLoader.java +++ b/app/src/main/java/com/kabouzeid/gramophone/loader/LastAddedLoader.java @@ -3,17 +3,18 @@ import android.content.Context; import android.database.Cursor; import android.provider.MediaStore; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.util.PreferenceUtil; import java.util.ArrayList; +import java.util.List; public class LastAddedLoader { @NonNull - public static ArrayList getLastAddedSongs(@NonNull Context context) { + public static List getLastAddedSongs(@NonNull Context context) { return SongLoader.getSongs(makeLastAddedCursor(context)); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistLoader.java index 60ad9f1b7..c8008d1c4 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistLoader.java +++ b/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistLoader.java @@ -5,17 +5,18 @@ import android.provider.BaseColumns; import android.provider.MediaStore; import android.provider.MediaStore.Audio.PlaylistsColumns; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.kabouzeid.gramophone.model.Playlist; import java.util.ArrayList; +import java.util.List; public class PlaylistLoader { @NonNull - public static ArrayList getAllPlaylists(@NonNull final Context context) { + public static List getAllPlaylists(@NonNull final Context context) { return getAllPlaylists(makePlaylistCursor(context, null, null)); } @@ -54,8 +55,8 @@ public static Playlist getPlaylist(@Nullable final Cursor cursor) { } @NonNull - public static ArrayList getAllPlaylists(@Nullable final Cursor cursor) { - ArrayList playlists = new ArrayList<>(); + public static List getAllPlaylists(@Nullable final Cursor cursor) { + List playlists = new ArrayList<>(); if (cursor != null && cursor.moveToFirst()) { do { diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistSongLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistSongLoader.java index 3f91684f8..b5434da96 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistSongLoader.java +++ b/app/src/main/java/com/kabouzeid/gramophone/loader/PlaylistSongLoader.java @@ -4,17 +4,18 @@ import android.database.Cursor; import android.provider.MediaStore; import android.provider.MediaStore.Audio.AudioColumns; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.kabouzeid.gramophone.model.PlaylistSong; import java.util.ArrayList; +import java.util.List; public class PlaylistSongLoader { @NonNull - public static ArrayList getPlaylistSongList(@NonNull final Context context, final int playlistId) { - ArrayList songs = new ArrayList<>(); + public static List getPlaylistSongList(@NonNull final Context context, final int playlistId) { + List songs = new ArrayList<>(); Cursor cursor = makePlaylistSongCursor(context, playlistId); if (cursor != null && cursor.moveToFirst()) { diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/SongLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/SongLoader.java index 7d84c9921..fca36aa9a 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/loader/SongLoader.java +++ b/app/src/main/java/com/kabouzeid/gramophone/loader/SongLoader.java @@ -5,14 +5,15 @@ import android.provider.BaseColumns; import android.provider.MediaStore; import android.provider.MediaStore.Audio.AudioColumns; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.provider.BlacklistStore; import com.kabouzeid.gramophone.util.PreferenceUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) @@ -34,13 +35,13 @@ public class SongLoader { }; @NonNull - public static ArrayList getAllSongs(@NonNull Context context) { + public static List getAllSongs(@NonNull Context context) { Cursor cursor = makeSongCursor(context, null, null); return getSongs(cursor); } @NonNull - public static ArrayList getSongs(@NonNull final Context context, final String query) { + public static List getSongs(@NonNull final Context context, final String query) { Cursor cursor = makeSongCursor(context, AudioColumns.TITLE + " LIKE ?", new String[]{"%" + query + "%"}); return getSongs(cursor); } @@ -52,8 +53,8 @@ public static Song getSong(@NonNull final Context context, final int queryId) { } @NonNull - public static ArrayList getSongs(@Nullable final Cursor cursor) { - ArrayList songs = new ArrayList<>(); + public static List getSongs(@Nullable final Cursor cursor) { + List songs = new ArrayList<>(); if (cursor != null && cursor.moveToFirst()) { do { songs.add(getSongFromCursorImpl(cursor)); @@ -110,7 +111,7 @@ public static Cursor makeSongCursor(@NonNull final Context context, @Nullable St } // Blacklist - ArrayList paths = BlacklistStore.getInstance(context).getPaths(); + List paths = BlacklistStore.getInstance(context).getPaths(); if (!paths.isEmpty()) { selection = generateBlacklistSelection(selection, paths.size()); selectionValues = addBlacklistSelectionValues(selectionValues, paths); @@ -133,7 +134,7 @@ private static String generateBlacklistSelection(String selection, int pathCount return newSelection; } - private static String[] addBlacklistSelectionValues(String[] selectionValues, ArrayList paths) { + private static String[] addBlacklistSelectionValues(String[] selectionValues, List paths) { if (selectionValues == null) selectionValues = new String[0]; String[] newSelectionValues = new String[selectionValues.length + paths.size()]; System.arraycopy(selectionValues, 0, newSelectionValues, 0, selectionValues.length); diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/SortedCursor.java b/app/src/main/java/com/kabouzeid/gramophone/loader/SortedCursor.java index 38348b442..ecd6a4bd3 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/loader/SortedCursor.java +++ b/app/src/main/java/com/kabouzeid/gramophone/loader/SortedCursor.java @@ -17,12 +17,13 @@ import android.database.AbstractCursor; import android.database.Cursor; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.List; /** * This cursor basically wraps a song cursor and is given a list of the order of the ids of the @@ -33,9 +34,9 @@ public class SortedCursor extends AbstractCursor { // cursor to wrap private final Cursor mCursor; // the map of external indices to internal indices - private ArrayList mOrderedPositions; + private List mOrderedPositions; // this contains the ids that weren't found in the underlying cursor - private ArrayList mMissingValues; + private List mMissingValues; // this contains the mapped cursor positions and afterwards the extra ids that weren't found private HashMap mMapCursorPositions; @@ -57,8 +58,8 @@ public SortedCursor(@NonNull final Cursor cursor, @Nullable final String[] order * @return returns the ids that aren't found in the underlying cursor */ @NonNull - private ArrayList buildCursorPositionMapping(@Nullable final String[] order, final String columnName) { - ArrayList missingValues = new ArrayList<>(); + private List buildCursorPositionMapping(@Nullable final String[] order, final String columnName) { + List missingValues = new ArrayList<>(); mOrderedPositions = new ArrayList<>(mCursor.getCount()); @@ -93,7 +94,7 @@ private ArrayList buildCursorPositionMapping(@Nullable final String[] or /** * @return the list of ids that weren't found in the underlying cursor */ - public ArrayList getMissingValues() { + public List getMissingValues() { return mMissingValues; } diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/SortedLongCursor.java b/app/src/main/java/com/kabouzeid/gramophone/loader/SortedLongCursor.java index 60336f223..929b19822 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/loader/SortedLongCursor.java +++ b/app/src/main/java/com/kabouzeid/gramophone/loader/SortedLongCursor.java @@ -17,12 +17,13 @@ import android.database.AbstractCursor; import android.database.Cursor; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.List; /** * This cursor basically wraps a song cursor and is given a list of the order of the ids of the @@ -33,9 +34,9 @@ public class SortedLongCursor extends AbstractCursor { // cursor to wrap private final Cursor mCursor; // the map of external indices to internal indices - private ArrayList mOrderedPositions; + private List mOrderedPositions; // this contains the ids that weren't found in the underlying cursor - private ArrayList mMissingIds; + private List mMissingIds; // this contains the mapped cursor positions and afterwards the extra ids that weren't found private HashMap mMapCursorPositions; @@ -58,8 +59,8 @@ public SortedLongCursor(final Cursor cursor, final long[] order, final String co * @return returns the ids that aren't found in the underlying cursor */ @NonNull - private ArrayList buildCursorPositionMapping(@Nullable final long[] order, final String columnName) { - ArrayList missingIds = new ArrayList<>(); + private List buildCursorPositionMapping(@Nullable final long[] order, final String columnName) { + List missingIds = new ArrayList<>(); mOrderedPositions = new ArrayList<>(mCursor.getCount()); @@ -93,7 +94,7 @@ private ArrayList buildCursorPositionMapping(@Nullable final long[] order, /** * @return the list of ids that weren't found in the underlying cursor */ - public ArrayList getMissingIds() { + public List getMissingIds() { return mMissingIds; } diff --git a/app/src/main/java/com/kabouzeid/gramophone/loader/TopAndRecentlyPlayedTracksLoader.java b/app/src/main/java/com/kabouzeid/gramophone/loader/TopAndRecentlyPlayedTracksLoader.java index 1c98eff73..e5cdf8aa0 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/loader/TopAndRecentlyPlayedTracksLoader.java +++ b/app/src/main/java/com/kabouzeid/gramophone/loader/TopAndRecentlyPlayedTracksLoader.java @@ -19,25 +19,27 @@ import android.content.Context; import android.database.Cursor; import android.provider.BaseColumns; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.provider.MediaStore; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.provider.HistoryStore; import com.kabouzeid.gramophone.provider.SongPlayCountStore; import java.util.ArrayList; +import java.util.List; public class TopAndRecentlyPlayedTracksLoader { - public static final int NUMBER_OF_TOP_TRACKS = 99; + public static final int NUMBER_OF_TOP_TRACKS = 100; @NonNull - public static ArrayList getRecentlyPlayedTracks(@NonNull Context context) { + public static List getRecentlyPlayedTracks(@NonNull Context context) { return SongLoader.getSongs(makeRecentTracksCursorAndClearUpDatabase(context)); } @NonNull - public static ArrayList getTopTracks(@NonNull Context context) { + public static List getTopTracks(@NonNull Context context) { return SongLoader.getSongs(makeTopTracksCursorAndClearUpDatabase(context)); } @@ -47,7 +49,7 @@ public static Cursor makeRecentTracksCursorAndClearUpDatabase(@NonNull final Con // clean up the databases with any ids not found if (retCursor != null) { - ArrayList missingIds = retCursor.getMissingIds(); + List missingIds = retCursor.getMissingIds(); if (missingIds != null && missingIds.size() > 0) { for (long id : missingIds) { HistoryStore.getInstance(context).removeSongId(id); @@ -63,7 +65,7 @@ public static Cursor makeTopTracksCursorAndClearUpDatabase(@NonNull final Contex // clean up the databases with any ids not found if (retCursor != null) { - ArrayList missingIds = retCursor.getMissingIds(); + List missingIds = retCursor.getMissingIds(); if (missingIds != null && missingIds.size() > 0) { for (long id : missingIds) { SongPlayCountStore.getInstance(context).removeItem(id); diff --git a/app/src/main/java/com/kabouzeid/gramophone/misc/CustomFragmentStatePagerAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/misc/CustomFragmentStatePagerAdapter.java index 378a67f79..d920d244c 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/misc/CustomFragmentStatePagerAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/misc/CustomFragmentStatePagerAdapter.java @@ -18,18 +18,21 @@ import android.os.Bundle; import android.os.Parcelable; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.app.FragmentTransaction; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.fragment.app.FragmentTransaction; +import androidx.viewpager.widget.PagerAdapter; + import android.util.Log; import android.view.View; import android.view.ViewGroup; import java.util.ArrayList; +import java.util.List; /** - * Implementation of {@link android.support.v4.view.PagerAdapter} that + * Implementation of {@link PagerAdapter} that * uses a {@link Fragment} to manage each page. This class also handles * saving and restoring of fragment's state. *

    @@ -64,15 +67,16 @@ * {@sample development/samples/Support13Demos/res/layout/fragment_pager_list.xml * complete} */ -public abstract class CustomFragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter { +public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter { + public static final String TAG = CustomFragmentStatePagerAdapter.class.getSimpleName(); private static final boolean DEBUG = false; private final FragmentManager mFragmentManager; private FragmentTransaction mCurTransaction = null; - private ArrayList mSavedState = new ArrayList(); - private ArrayList mFragments = new ArrayList(); + private List mSavedState = new ArrayList<>(); + private List mFragments = new ArrayList<>(); private Fragment mCurrentPrimaryItem = null; public CustomFragmentStatePagerAdapter(FragmentManager fm) { @@ -203,8 +207,8 @@ public void restoreState(Parcelable state, ClassLoader loader) { mSavedState.clear(); mFragments.clear(); if (fss != null) { - for (int i = 0; i < fss.length; i++) { - mSavedState.add((Fragment.SavedState) fss[i]); + for (Parcelable fs : fss) { + mSavedState.add((Fragment.SavedState) fs); } } Iterable keys = bundle.keySet(); diff --git a/app/src/main/java/com/kabouzeid/gramophone/misc/DialogAsyncTask.java b/app/src/main/java/com/kabouzeid/gramophone/misc/DialogAsyncTask.java index 8ddd3ad24..f45b10447 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/misc/DialogAsyncTask.java +++ b/app/src/main/java/com/kabouzeid/gramophone/misc/DialogAsyncTask.java @@ -2,19 +2,17 @@ import android.app.Dialog; import android.content.Context; -import android.os.AsyncTask; import android.os.Handler; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.lang.ref.WeakReference; /** * @author Karim Abou Zeid (kabouzeid) */ -public abstract class DialogAsyncTask extends AsyncTask { +public abstract class DialogAsyncTask extends WeakContextAsyncTask { private final int delay; - private WeakReference contextWeakReference; private WeakReference

    dialogWeakReference; private boolean supposedToBeDismissed; @@ -24,8 +22,8 @@ public DialogAsyncTask(Context context) { } public DialogAsyncTask(Context context, int showDelay) { + super(context); this.delay = showDelay; - contextWeakReference = new WeakReference<>(context); dialogWeakReference = new WeakReference<>(null); } @@ -62,11 +60,6 @@ protected void onProgressUpdate(Progress... values) { protected void onProgressUpdate(@NonNull Dialog dialog, Progress... values) { } - @Nullable - protected Context getContext() { - return contextWeakReference.get(); - } - @Nullable protected Dialog getDialog() { return dialogWeakReference.get(); diff --git a/app/src/main/java/com/kabouzeid/gramophone/misc/LagTracker.java b/app/src/main/java/com/kabouzeid/gramophone/misc/LagTracker.java deleted file mode 100644 index 12ab4f812..000000000 --- a/app/src/main/java/com/kabouzeid/gramophone/misc/LagTracker.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.kabouzeid.gramophone.misc; - -import android.util.Log; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -/** - * @author Aidan Follestad (afollestad) - */ -public class LagTracker { - - private static LagTracker mSingleton; - private static Map mMap; - private boolean mEnabled = true; - - private LagTracker() { - mMap = new HashMap<>(); - } - - public static LagTracker get() { - if (mSingleton == null) - mSingleton = new LagTracker(); - return mSingleton; - } - - public LagTracker enable() { - mEnabled = true; - return this; - } - - public LagTracker disable() { - mEnabled = false; - return this; - } - - public void start(String key) { - final long start = System.nanoTime(); - if (!mEnabled) { - if (!mMap.isEmpty()) - mMap.clear(); - return; - } - mMap.put(key, start); - } - - public void end(String key) { - final long end = System.nanoTime(); - if (!mEnabled) { - if (!mMap.isEmpty()) - mMap.clear(); - return; - } - if (!mMap.containsKey(key)) - throw new IllegalStateException("No start time found for " + key); - long start = mMap.get(key); - long diff = end - start; - print(key, diff); - mMap.remove(key); - } - - private void print(String key, long diff) { - long ms = TimeUnit.NANOSECONDS.toMillis(diff); - long s = TimeUnit.NANOSECONDS.toSeconds(diff); - Log.d("LagTracker", "[" + key + " completed in]: " + diff + " ns (" + ms + "ms, " + s + "s)"); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/kabouzeid/gramophone/misc/SimpleTransitionListener.java b/app/src/main/java/com/kabouzeid/gramophone/misc/SimpleTransitionListener.java deleted file mode 100644 index fda8216c1..000000000 --- a/app/src/main/java/com/kabouzeid/gramophone/misc/SimpleTransitionListener.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.kabouzeid.gramophone.misc; - -import android.annotation.TargetApi; -import android.os.Build; -import android.transition.Transition; - -/** - * @author Karim Abou Zeid (kabouzeid) - */ -@TargetApi(Build.VERSION_CODES.LOLLIPOP) -public abstract class SimpleTransitionListener implements Transition.TransitionListener { - @Override - public void onTransitionStart(Transition transition) { - } - - @Override - public void onTransitionEnd(Transition transition) { - } - - @Override - public void onTransitionCancel(Transition transition) { - } - - @Override - public void onTransitionPause(Transition transition) { - } - - @Override - public void onTransitionResume(Transition transition) { - } -} diff --git a/app/src/main/java/com/kabouzeid/gramophone/misc/UpdateToastMediaScannerCompletionListener.java b/app/src/main/java/com/kabouzeid/gramophone/misc/UpdateToastMediaScannerCompletionListener.java index 48c06eeaa..62204d6d4 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/misc/UpdateToastMediaScannerCompletionListener.java +++ b/app/src/main/java/com/kabouzeid/gramophone/misc/UpdateToastMediaScannerCompletionListener.java @@ -14,15 +14,15 @@ * @author Karim Abou Zeid (kabouzeid) */ public class UpdateToastMediaScannerCompletionListener implements MediaScannerConnection.OnScanCompletedListener { - int scanned = 0; - int failed = 0; + private int scanned = 0; + private int failed = 0; private final String[] toBeScanned; private final String scannedFiles; private final String couldNotScanFiles; - private final WeakReference toastWeakReference; + private Toast toast; private final WeakReference activityWeakReference; @SuppressLint("ShowToast") @@ -30,7 +30,7 @@ public UpdateToastMediaScannerCompletionListener(Activity activity, String[] toB this.toBeScanned = toBeScanned; scannedFiles = activity.getString(R.string.scanned_files); couldNotScanFiles = activity.getString(R.string.could_not_scan_files); - toastWeakReference = new WeakReference<>(Toast.makeText(activity, "", Toast.LENGTH_SHORT)); + toast = Toast.makeText(activity.getApplicationContext(), "", Toast.LENGTH_SHORT); activityWeakReference = new WeakReference<>(activity); } @@ -39,17 +39,14 @@ public void onScanCompleted(final String path, final Uri uri) { Activity activity = activityWeakReference.get(); if (activity != null) { activity.runOnUiThread(() -> { - Toast toast = toastWeakReference.get(); - if (toast != null) { - if (uri == null) { - failed++; - } else { - scanned++; - } - String text = " " + String.format(scannedFiles, scanned, toBeScanned.length) + (failed > 0 ? " " + String.format(couldNotScanFiles, failed) : ""); - toast.setText(text); - toast.show(); + if (uri == null) { + failed++; + } else { + scanned++; } + String text = " " + String.format(scannedFiles, scanned, toBeScanned.length) + (failed > 0 ? " " + String.format(couldNotScanFiles, failed) : ""); + toast.setText(text); + toast.show(); }); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/misc/WeakContextAsyncTask.java b/app/src/main/java/com/kabouzeid/gramophone/misc/WeakContextAsyncTask.java new file mode 100644 index 000000000..1d02d37c6 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/misc/WeakContextAsyncTask.java @@ -0,0 +1,23 @@ +package com.kabouzeid.gramophone.misc; + +import android.content.Context; +import android.os.AsyncTask; +import androidx.annotation.Nullable; + +import java.lang.ref.WeakReference; + +/** + * @author Karim Abou Zeid (kabouzeid) + */ +public abstract class WeakContextAsyncTask extends AsyncTask { + private WeakReference contextWeakReference; + + public WeakContextAsyncTask(Context context) { + contextWeakReference = new WeakReference<>(context); + } + + @Nullable + protected Context getContext() { + return contextWeakReference.get(); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/misc/WrappedAsyncTaskLoader.java b/app/src/main/java/com/kabouzeid/gramophone/misc/WrappedAsyncTaskLoader.java index 9dd48a2b4..85746b6ef 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/misc/WrappedAsyncTaskLoader.java +++ b/app/src/main/java/com/kabouzeid/gramophone/misc/WrappedAsyncTaskLoader.java @@ -2,7 +2,7 @@ package com.kabouzeid.gramophone.misc; import android.content.Context; -import android.support.v4.content.AsyncTaskLoader; +import androidx.loader.content.AsyncTaskLoader; /** * Issue diff --git a/app/src/main/java/com/kabouzeid/gramophone/model/AbsCustomPlaylist.java b/app/src/main/java/com/kabouzeid/gramophone/model/AbsCustomPlaylist.java index ccc3af4a2..0cf2af7f0 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/model/AbsCustomPlaylist.java +++ b/app/src/main/java/com/kabouzeid/gramophone/model/AbsCustomPlaylist.java @@ -2,9 +2,10 @@ import android.content.Context; import android.os.Parcel; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) @@ -23,5 +24,5 @@ public AbsCustomPlaylist(Parcel in) { } @NonNull - public abstract ArrayList getSongs(Context context); + public abstract List getSongs(Context context); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/model/Album.java b/app/src/main/java/com/kabouzeid/gramophone/model/Album.java index 4983ab6f5..2d4455625 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/model/Album.java +++ b/app/src/main/java/com/kabouzeid/gramophone/model/Album.java @@ -2,17 +2,18 @@ import android.os.Parcel; import android.os.Parcelable; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ public class Album implements Parcelable { - public final ArrayList songs; + public final List songs; - public Album(ArrayList songs) { + public Album(List songs) { this.songs = songs; } diff --git a/app/src/main/java/com/kabouzeid/gramophone/model/Artist.java b/app/src/main/java/com/kabouzeid/gramophone/model/Artist.java index 3844dc56a..bb5ed9e64 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/model/Artist.java +++ b/app/src/main/java/com/kabouzeid/gramophone/model/Artist.java @@ -2,17 +2,22 @@ import android.os.Parcel; import android.os.Parcelable; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; + +import com.kabouzeid.gramophone.util.MusicUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ public class Artist implements Parcelable { - public final ArrayList albums; + public static final String UNKNOWN_ARTIST_DISPLAY_NAME = "Unknown Artist"; + + public final List albums; - public Artist(ArrayList albums) { + public Artist(List albums) { this.albums = albums; } @@ -25,7 +30,11 @@ public int getId() { } public String getName() { - return safeGetFirstAlbum().getArtistName(); + String name = safeGetFirstAlbum().getArtistName(); + if (MusicUtil.isArtistNameUnknown(name)) { + return UNKNOWN_ARTIST_DISPLAY_NAME; + } + return name; } public int getSongCount() { @@ -40,8 +49,8 @@ public int getAlbumCount() { return albums.size(); } - public ArrayList getSongs() { - ArrayList songs = new ArrayList<>(); + public List getSongs() { + List songs = new ArrayList<>(); for (Album album : albums) { songs.addAll(album.songs); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/model/Playlist.java b/app/src/main/java/com/kabouzeid/gramophone/model/Playlist.java index bb42f4b28..98159d77b 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/model/Playlist.java +++ b/app/src/main/java/com/kabouzeid/gramophone/model/Playlist.java @@ -2,6 +2,7 @@ import android.os.Parcel; import android.os.Parcelable; +import androidx.annotation.NonNull; /** * @author Karim Abou Zeid (kabouzeid) diff --git a/app/src/main/java/com/kabouzeid/gramophone/model/lyrics/Lyrics.java b/app/src/main/java/com/kabouzeid/gramophone/model/lyrics/Lyrics.java index fb38ca250..eb6d54ebe 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/model/lyrics/Lyrics.java +++ b/app/src/main/java/com/kabouzeid/gramophone/model/lyrics/Lyrics.java @@ -3,9 +3,10 @@ import com.kabouzeid.gramophone.model.Song; import java.util.ArrayList; +import java.util.List; public class Lyrics { - private static final ArrayList> FORMATS = new ArrayList<>(); + private static final List> FORMATS = new ArrayList<>(); public Song song; public String data; diff --git a/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/AbsSmartPlaylist.java b/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/AbsSmartPlaylist.java index d21e39b3e..09ccc2c5c 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/AbsSmartPlaylist.java +++ b/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/AbsSmartPlaylist.java @@ -2,8 +2,8 @@ import android.content.Context; import android.os.Parcel; -import android.support.annotation.DrawableRes; -import android.support.annotation.Nullable; +import androidx.annotation.DrawableRes; +import androidx.annotation.Nullable; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.model.AbsCustomPlaylist; diff --git a/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/HistoryPlaylist.java b/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/HistoryPlaylist.java index 02cc93c3e..07753636c 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/HistoryPlaylist.java +++ b/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/HistoryPlaylist.java @@ -2,14 +2,16 @@ import android.content.Context; import android.os.Parcel; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; -import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.loader.TopAndRecentlyPlayedTracksLoader; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.provider.HistoryStore; +import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.util.PreferenceUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) @@ -22,7 +24,7 @@ public HistoryPlaylist(@NonNull Context context) { @NonNull @Override - public ArrayList getSongs(@NonNull Context context) { + public List getSongs(@NonNull Context context) { return TopAndRecentlyPlayedTracksLoader.getRecentlyPlayedTracks(context); } @@ -37,11 +39,6 @@ public int describeContents() { return 0; } - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - } - protected HistoryPlaylist(Parcel in) { super(in); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/LastAddedPlaylist.java b/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/LastAddedPlaylist.java index 9009632a3..b3d3550e1 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/LastAddedPlaylist.java +++ b/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/LastAddedPlaylist.java @@ -2,13 +2,15 @@ import android.content.Context; import android.os.Parcel; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; -import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.loader.LastAddedLoader; import com.kabouzeid.gramophone.model.Song; +import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.util.PreferenceUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) @@ -21,7 +23,7 @@ public LastAddedPlaylist(@NonNull Context context) { @NonNull @Override - public ArrayList getSongs(@NonNull Context context) { + public List getSongs(@NonNull Context context) { return LastAddedLoader.getLastAddedSongs(context); } @@ -35,11 +37,6 @@ public int describeContents() { return 0; } - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - } - protected LastAddedPlaylist(Parcel in) { super(in); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/MyTopTracksPlaylist.java b/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/MyTopTracksPlaylist.java index 444debf17..dbfe2f0e8 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/MyTopTracksPlaylist.java +++ b/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/MyTopTracksPlaylist.java @@ -2,7 +2,7 @@ import android.content.Context; import android.os.Parcel; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.loader.TopAndRecentlyPlayedTracksLoader; @@ -10,6 +10,7 @@ import com.kabouzeid.gramophone.provider.SongPlayCountStore; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) @@ -22,7 +23,7 @@ public MyTopTracksPlaylist(@NonNull Context context) { @NonNull @Override - public ArrayList getSongs(@NonNull Context context) { + public List getSongs(@NonNull Context context) { return TopAndRecentlyPlayedTracksLoader.getTopTracks(context); } @@ -37,11 +38,6 @@ public int describeContents() { return 0; } - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - } - protected MyTopTracksPlaylist(Parcel in) { super(in); } @@ -55,4 +51,4 @@ public MyTopTracksPlaylist[] newArray(int size) { return new MyTopTracksPlaylist[size]; } }; -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/ShuffleAllPlaylist.java b/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/ShuffleAllPlaylist.java index bf8c818de..f1c736eda 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/ShuffleAllPlaylist.java +++ b/app/src/main/java/com/kabouzeid/gramophone/model/smartplaylist/ShuffleAllPlaylist.java @@ -2,13 +2,14 @@ import android.content.Context; import android.os.Parcel; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.loader.SongLoader; import com.kabouzeid.gramophone.model.Song; import java.util.ArrayList; +import java.util.List; public class ShuffleAllPlaylist extends AbsSmartPlaylist { @@ -18,7 +19,7 @@ public ShuffleAllPlaylist(@NonNull Context context) { @NonNull @Override - public ArrayList getSongs(@NonNull Context context) { + public List getSongs(@NonNull Context context) { return SongLoader.getAllSongs(context); } @@ -32,11 +33,6 @@ public int describeContents() { return 0; } - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - } - protected ShuffleAllPlaylist(Parcel in) { super(in); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/preferences/BlacklistPreferenceDialog.java b/app/src/main/java/com/kabouzeid/gramophone/preferences/BlacklistPreferenceDialog.java index 130731407..143d6ce62 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/preferences/BlacklistPreferenceDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/preferences/BlacklistPreferenceDialog.java @@ -2,12 +2,10 @@ import android.app.Dialog; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; import android.text.Html; -import android.view.View; -import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.dialogs.BlacklistFolderChooserDialog; @@ -15,14 +13,14 @@ import java.io.File; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ public class BlacklistPreferenceDialog extends DialogFragment implements BlacklistFolderChooserDialog.FolderCallback { - public static final String TAG = BlacklistPreferenceDialog.class.getSimpleName(); - private ArrayList paths; + private List paths; public static BlacklistPreferenceDialog newInstance() { return new BlacklistPreferenceDialog(); @@ -89,4 +87,4 @@ public void onFolderSelection(@NonNull BlacklistFolderChooserDialog folderChoose BlacklistStore.getInstance(getContext()).addPath(file); refreshBlacklistData(); } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/preferences/LibraryPreferenceDialog.java b/app/src/main/java/com/kabouzeid/gramophone/preferences/LibraryPreferenceDialog.java index 53a808fe6..9c3ad92b8 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/preferences/LibraryPreferenceDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/preferences/LibraryPreferenceDialog.java @@ -2,10 +2,10 @@ import android.app.Dialog; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.view.View; import com.afollestad.materialdialogs.MaterialDialog; @@ -15,6 +15,7 @@ import com.kabouzeid.gramophone.util.PreferenceUtil; import java.util.ArrayList; +import java.util.List; public class LibraryPreferenceDialog extends DialogFragment { @@ -29,7 +30,7 @@ public static LibraryPreferenceDialog newInstance() { public Dialog onCreateDialog(Bundle savedInstanceState) { View view = getActivity().getLayoutInflater().inflate(R.layout.preference_dialog_library_categories, null); - ArrayList categoryInfos; + List categoryInfos; if (savedInstanceState != null) { categoryInfos = savedInstanceState.getParcelableArrayList(PreferenceUtil.LIBRARY_CATEGORIES); } else { @@ -62,16 +63,16 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putParcelableArrayList(PreferenceUtil.LIBRARY_CATEGORIES, adapter.getCategoryInfos()); + outState.putParcelableArrayList(PreferenceUtil.LIBRARY_CATEGORIES, new ArrayList<>(adapter.getCategoryInfos())); } - private void updateCategories(ArrayList categories) { + private void updateCategories(List categories) { if (getSelected(categories) == 0) return; PreferenceUtil.getInstance(getContext()).setLibraryCategoryInfos(categories); } - private int getSelected(ArrayList categories) { + private int getSelected(List categories) { int selected = 0; for (CategoryInfo categoryInfo : categories) { if (categoryInfo.visible) diff --git a/app/src/main/java/com/kabouzeid/gramophone/preferences/NowPlayingScreenPreferenceDialog.java b/app/src/main/java/com/kabouzeid/gramophone/preferences/NowPlayingScreenPreferenceDialog.java index b2ef1e3cf..047ddaf44 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/preferences/NowPlayingScreenPreferenceDialog.java +++ b/app/src/main/java/com/kabouzeid/gramophone/preferences/NowPlayingScreenPreferenceDialog.java @@ -5,10 +5,10 @@ import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; -import android.support.v4.view.PagerAdapter; -import android.support.v4.view.ViewPager; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -23,13 +23,10 @@ import com.kabouzeid.gramophone.util.PreferenceUtil; import com.kabouzeid.gramophone.util.ViewUtil; -import butterknife.ButterKnife; - /** * @author Karim Abou Zeid (kabouzeid) */ public class NowPlayingScreenPreferenceDialog extends DialogFragment implements MaterialDialog.SingleButtonCallback, ViewPager.OnPageChangeListener { - public static final String TAG = NowPlayingScreenPreferenceDialog.class.getSimpleName(); private DialogAction whichButtonClicked; private int viewPagerPosition; @@ -42,13 +39,13 @@ public static NowPlayingScreenPreferenceDialog newInstance() { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { @SuppressLint("InflateParams") View view = LayoutInflater.from(getContext()).inflate(R.layout.preference_dialog_now_playing_screen, null); - ViewPager viewPager = ButterKnife.findById(view, R.id.now_playing_screen_view_pager); + ViewPager viewPager = view.findViewById(R.id.now_playing_screen_view_pager); viewPager.setAdapter(new NowPlayingScreenAdapter(getContext())); viewPager.addOnPageChangeListener(this); viewPager.setPageMargin((int) ViewUtil.convertDpToPixel(32, getResources())); viewPager.setCurrentItem(PreferenceUtil.getInstance(getContext()).getNowPlayingScreen().ordinal()); - InkPageIndicator pageIndicator = ButterKnife.findById(view, R.id.page_indicator); + InkPageIndicator pageIndicator = view.findViewById(R.id.page_indicator); pageIndicator.setViewPager(viewPager); pageIndicator.onPageSelected(viewPager.getCurrentItem()); @@ -98,15 +95,16 @@ public NowPlayingScreenAdapter(Context context) { } @Override - public Object instantiateItem(ViewGroup collection, int position) { + @NonNull + public Object instantiateItem(@NonNull ViewGroup collection, int position) { NowPlayingScreen nowPlayingScreen = NowPlayingScreen.values()[position]; LayoutInflater inflater = LayoutInflater.from(context); ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.preference_now_playing_screen_item, collection, false); collection.addView(layout); - ImageView image = ButterKnife.findById(layout, R.id.image); - TextView title = ButterKnife.findById(layout, R.id.title); + ImageView image = layout.findViewById(R.id.image); + TextView title = layout.findViewById(R.id.title); image.setImageResource(nowPlayingScreen.drawableResId); title.setText(nowPlayingScreen.titleRes); @@ -114,7 +112,7 @@ public Object instantiateItem(ViewGroup collection, int position) { } @Override - public void destroyItem(ViewGroup collection, int position, Object view) { + public void destroyItem(@NonNull ViewGroup collection, int position, @NonNull Object view) { collection.removeView((View) view); } @@ -124,7 +122,7 @@ public int getCount() { } @Override - public boolean isViewFromObject(View view, Object object) { + public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { return view == object; } @@ -133,4 +131,4 @@ public CharSequence getPageTitle(int position) { return context.getString(NowPlayingScreen.values()[position].titleRes); } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/provider/BlacklistStore.java b/app/src/main/java/com/kabouzeid/gramophone/provider/BlacklistStore.java index 2f2dafa53..47b54a0e3 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/provider/BlacklistStore.java +++ b/app/src/main/java/com/kabouzeid/gramophone/provider/BlacklistStore.java @@ -7,7 +7,7 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.os.Environment; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.kabouzeid.gramophone.service.MusicService; import com.kabouzeid.gramophone.util.FileUtil; @@ -15,6 +15,7 @@ import java.io.File; import java.util.ArrayList; +import java.util.List; public class BlacklistStore extends SQLiteOpenHelper { private static BlacklistStore sInstance = null; @@ -130,12 +131,12 @@ private void notifyMediaStoreChanged() { } @NonNull - public ArrayList getPaths() { + public List getPaths() { Cursor cursor = getReadableDatabase().query(BlacklistStoreColumns.NAME, new String[]{BlacklistStoreColumns.PATH}, null, null, null, null, null); - ArrayList paths = new ArrayList<>(); + List paths = new ArrayList<>(); if (cursor != null && cursor.moveToFirst()) { do { paths.add(cursor.getString(0)); diff --git a/app/src/main/java/com/kabouzeid/gramophone/provider/HistoryStore.java b/app/src/main/java/com/kabouzeid/gramophone/provider/HistoryStore.java index 357be4102..af5b02dad 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/provider/HistoryStore.java +++ b/app/src/main/java/com/kabouzeid/gramophone/provider/HistoryStore.java @@ -21,8 +21,8 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; public class HistoryStore extends SQLiteOpenHelper { private static final int MAX_ITEMS_IN_DB = 100; diff --git a/app/src/main/java/com/kabouzeid/gramophone/provider/MusicPlaybackQueueStore.java b/app/src/main/java/com/kabouzeid/gramophone/provider/MusicPlaybackQueueStore.java index 921b13e19..fb9d1d652 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/provider/MusicPlaybackQueueStore.java +++ b/app/src/main/java/com/kabouzeid/gramophone/provider/MusicPlaybackQueueStore.java @@ -22,13 +22,14 @@ import android.database.sqlite.SQLiteOpenHelper; import android.provider.BaseColumns; import android.provider.MediaStore.Audio.AudioColumns; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.kabouzeid.gramophone.loader.SongLoader; import com.kabouzeid.gramophone.model.Song; import java.util.ArrayList; +import java.util.List; /** * @author Andrew Neal, modified for Phonograph by Karim Abou Zeid @@ -129,7 +130,7 @@ public static synchronized MusicPlaybackQueueStore getInstance(@NonNull final Co return sInstance; } - public synchronized void saveQueues(@NonNull final ArrayList playingQueue, @NonNull final ArrayList originalPlayingQueue) { + public synchronized void saveQueues(@NonNull final List playingQueue, @NonNull final List originalPlayingQueue) { saveQueue(PLAYING_QUEUE_TABLE_NAME, playingQueue); saveQueue(ORIGINAL_PLAYING_QUEUE_TABLE_NAME, originalPlayingQueue); } @@ -140,7 +141,7 @@ public synchronized void saveQueues(@NonNull final ArrayList playingQueue, * * @param queue the queue to save */ - private synchronized void saveQueue(final String tableName, @NonNull final ArrayList queue) { + private synchronized void saveQueue(final String tableName, @NonNull final List queue) { final SQLiteDatabase database = getWritableDatabase(); database.beginTransaction(); @@ -183,17 +184,17 @@ private synchronized void saveQueue(final String tableName, @NonNull final Array } @NonNull - public ArrayList getSavedPlayingQueue() { + public List getSavedPlayingQueue() { return getQueue(PLAYING_QUEUE_TABLE_NAME); } @NonNull - public ArrayList getSavedOriginalPlayingQueue() { + public List getSavedOriginalPlayingQueue() { return getQueue(ORIGINAL_PLAYING_QUEUE_TABLE_NAME); } @NonNull - private ArrayList getQueue(@NonNull final String tableName) { + private List getQueue(@NonNull final String tableName) { Cursor cursor = getReadableDatabase().query(tableName, null, null, null, null, null, null); return SongLoader.getSongs(cursor); diff --git a/app/src/main/java/com/kabouzeid/gramophone/provider/SongPlayCountStore.java b/app/src/main/java/com/kabouzeid/gramophone/provider/SongPlayCountStore.java index 203bc02b7..0ddc49f74 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/provider/SongPlayCountStore.java +++ b/app/src/main/java/com/kabouzeid/gramophone/provider/SongPlayCountStore.java @@ -21,8 +21,8 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import android.view.animation.AccelerateInterpolator; import android.view.animation.Interpolator; diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/MediaButtonIntentReceiver.java b/app/src/main/java/com/kabouzeid/gramophone/service/MediaButtonIntentReceiver.java index 51565d087..0bab58682 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/service/MediaButtonIntentReceiver.java +++ b/app/src/main/java/com/kabouzeid/gramophone/service/MediaButtonIntentReceiver.java @@ -25,6 +25,8 @@ import android.util.Log; import android.view.KeyEvent; +import androidx.core.content.ContextCompat; + import com.kabouzeid.gramophone.BuildConfig; /** @@ -99,7 +101,9 @@ public static boolean handleIntent(final Context context, final Intent intent) { final int keycode = event.getKeyCode(); final int action = event.getAction(); - final long eventTime = event.getEventTime(); + final long eventTime = event.getEventTime() != 0 ? + event.getEventTime() : System.currentTimeMillis(); + // Fallback to system time if event time was not available. String command = null; switch (keycode) { @@ -165,10 +169,16 @@ public static boolean handleIntent(final Context context, final Intent intent) { private static void startService(Context context, String command) { final Intent intent = new Intent(context, MusicService.class); intent.setAction(command); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - context.startForegroundService(intent); - } else { + try { + // IMPORTANT NOTE: (kind of a hack) + // on Android O and above the following crashes when the app is not running + // there is no good way to check whether the app is running so we catch the exception + // we do not always want to use startForegroundService() because then one gets an ANR + // if no notification is displayed via startForeground() + // according to Play analytics this happens a lot, I suppose for example if command = PAUSE context.startService(intent); + } catch (IllegalStateException ignored) { + ContextCompat.startForegroundService(context, intent); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/MultiPlayer.java b/app/src/main/java/com/kabouzeid/gramophone/service/MultiPlayer.java index 8a89b71f2..1cb3e15ed 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/service/MultiPlayer.java +++ b/app/src/main/java/com/kabouzeid/gramophone/service/MultiPlayer.java @@ -7,8 +7,8 @@ import android.media.audiofx.AudioEffect; import android.net.Uri; import android.os.PowerManager; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import android.util.Log; import android.widget.Toast; @@ -144,7 +144,7 @@ public void setNextDataSource(@Nullable final String path) { * @param callbacks The callbacks to use */ @Override - public void setCallbacks(final Playback.PlaybackCallbacks callbacks) { + public void setCallbacks(@Nullable Playback.PlaybackCallbacks callbacks) { this.callbacks = callbacks; } @@ -329,4 +329,4 @@ public void onCompletion(final MediaPlayer mp) { callbacks.onTrackEnded(); } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java index 84dc7e116..03feda61c 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java +++ b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java @@ -27,13 +27,14 @@ import android.os.Process; import android.preference.PreferenceManager; import android.provider.MediaStore; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.support.v4.media.MediaMetadataCompat; import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.PlaybackStateCompat; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.bumptech.glide.BitmapRequestBuilder; import com.bumptech.glide.Glide; import com.bumptech.glide.request.animation.GlideAnimation; @@ -71,7 +72,6 @@ * @author Karim Abou Zeid (kabouzeid), Andrew Neal */ public class MusicService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks { - public static final String TAG = MusicService.class.getSimpleName(); public static final String PHONOGRAPH_PACKAGE_NAME = "com.kabouzeid.gramophone"; public static final String MUSIC_PACKAGE_NAME = "com.android.music"; @@ -84,6 +84,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP public static final String ACTION_SKIP = PHONOGRAPH_PACKAGE_NAME + ".skip"; public static final String ACTION_REWIND = PHONOGRAPH_PACKAGE_NAME + ".rewind"; public static final String ACTION_QUIT = PHONOGRAPH_PACKAGE_NAME + ".quitservice"; + public static final String ACTION_PENDING_QUIT = PHONOGRAPH_PACKAGE_NAME + ".pendingquitservice"; public static final String INTENT_EXTRA_PLAYLIST = PHONOGRAPH_PACKAGE_NAME + "intentextra.playlist"; public static final String INTENT_EXTRA_SHUFFLE_MODE = PHONOGRAPH_PACKAGE_NAME + ".intentextra.shufflemode"; @@ -126,14 +127,16 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP private final IBinder musicBind = new MusicBinder(); + public boolean pendingQuit = false; + private AppWidgetBig appWidgetBig = AppWidgetBig.getInstance(); private AppWidgetClassic appWidgetClassic = AppWidgetClassic.getInstance(); private AppWidgetSmall appWidgetSmall = AppWidgetSmall.getInstance(); private AppWidgetCard appWidgetCard = AppWidgetCard.getInstance(); private Playback playback; - private ArrayList playingQueue = new ArrayList<>(); - private ArrayList originalPlayingQueue = new ArrayList<>(); + private List playingQueue = new ArrayList<>(); + private List originalPlayingQueue = new ArrayList<>(); private int position = -1; private int nextPosition = -1; private int shuffleMode; @@ -303,12 +306,12 @@ public int onStartCommand(@Nullable Intent intent, int flags, int startId) { Playlist playlist = intent.getParcelableExtra(INTENT_EXTRA_PLAYLIST); int shuffleMode = intent.getIntExtra(INTENT_EXTRA_SHUFFLE_MODE, getShuffleMode()); if (playlist != null) { - ArrayList playlistSongs; + List playlistSongs; if (playlist instanceof AbsCustomPlaylist) { playlistSongs = ((AbsCustomPlaylist) playlist).getSongs(getApplicationContext()); } else { //noinspection unchecked - playlistSongs = (ArrayList) (List) PlaylistSongLoader.getPlaylistSongList(getApplicationContext(), playlist.id); + playlistSongs = (List) PlaylistSongLoader.getPlaylistSongList(getApplicationContext(), playlist.id); } if (!playlistSongs.isEmpty()) { if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { @@ -336,8 +339,12 @@ public int onStartCommand(@Nullable Intent intent, int flags, int startId) { break; case ACTION_STOP: case ACTION_QUIT: + pendingQuit = false; quit(); break; + case ACTION_PENDING_QUIT: + pendingQuit = true; + break; } } } @@ -422,8 +429,8 @@ private void restoreState() { private synchronized void restoreQueuesAndPositionIfNecessary() { if (!queuesRestored && playingQueue.isEmpty()) { - ArrayList restoredQueue = MusicPlaybackQueueStore.getInstance(this).getSavedPlayingQueue(); - ArrayList restoredOriginalQueue = MusicPlaybackQueueStore.getInstance(this).getSavedOriginalPlayingQueue(); + List restoredQueue = MusicPlaybackQueueStore.getInstance(this).getSavedPlayingQueue(); + List restoredOriginalQueue = MusicPlaybackQueueStore.getInstance(this).getSavedOriginalPlayingQueue(); int restoredPosition = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_POSITION, -1); int restoredPositionInTrack = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_POSITION_IN_TRACK, -1); @@ -671,7 +678,7 @@ private boolean isLastTrack() { return getPosition() == getPlayingQueue().size() - 1; } - public ArrayList getPlayingQueue() { + public List getPlayingQueue() { return playingQueue; } @@ -694,7 +701,7 @@ public void setRepeatMode(final int repeatMode) { } } - public void openQueue(@Nullable final ArrayList playingQueue, final int startPosition, final boolean startPlaying) { + public void openQueue(@Nullable final List playingQueue, final int startPosition, final boolean startPlaying) { if (playingQueue != null && !playingQueue.isEmpty() && startPosition >= 0 && startPosition < playingQueue.size()) { // it is important to copy the playing queue here first as we might add/remove songs later originalPlayingQueue = new ArrayList<>(playingQueue); @@ -863,7 +870,7 @@ public void play() { } } - public void playSongs(ArrayList songs, int shuffleMode) { + public void playSongs(List songs, int shuffleMode) { if (songs != null && !songs.isEmpty()) { if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { int startPosition = 0; @@ -886,7 +893,7 @@ public void playPreviousSong(boolean force) { } public void back(boolean force) { - if (getSongProgressMillis() > 2000) { + if (getSongProgressMillis() > 5000) { seek(0); } else { playPreviousSong(force); @@ -1194,9 +1201,16 @@ public void handleMessage(@NonNull final Message msg) { break; case TRACK_ENDED: - if (service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) { + // if there is a timer finished, don't continue + if (service.pendingQuit || + service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) { service.notifyChange(PLAY_STATE_CHANGED); service.seek(0); + if (service.pendingQuit) { + service.pendingQuit = false; + service.quit(); + break; + } } else { service.playNextSong(false); } @@ -1273,18 +1287,24 @@ public MusicService getService() { public void onReceive(final Context context, final Intent intent) { final String command = intent.getStringExtra(EXTRA_APP_WIDGET_NAME); - if (AppWidgetClassic.NAME.equals(command)) { - final int[] ids = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); - appWidgetClassic.performUpdate(MusicService.this, ids); - } else if (AppWidgetSmall.NAME.equals(command)) { - final int[] ids = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); - appWidgetSmall.performUpdate(MusicService.this, ids); - } else if (AppWidgetBig.NAME.equals(command)) { - final int[] ids = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); - appWidgetBig.performUpdate(MusicService.this, ids); - } else if (AppWidgetCard.NAME.equals(command)) { - final int[] ids = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); - appWidgetCard.performUpdate(MusicService.this, ids); + final int[] ids = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); + switch (command) { + case AppWidgetClassic.NAME: { + appWidgetClassic.performUpdate(MusicService.this, ids); + break; + } + case AppWidgetSmall.NAME: { + appWidgetSmall.performUpdate(MusicService.this, ids); + break; + } + case AppWidgetBig.NAME: { + appWidgetBig.performUpdate(MusicService.this, ids); + break; + } + case AppWidgetCard.NAME: { + appWidgetCard.performUpdate(MusicService.this, ids); + break; + } } } }; diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotification.java b/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotification.java index 2969a4e2d..06d7418c3 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotification.java +++ b/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotification.java @@ -4,7 +4,7 @@ import android.app.NotificationChannel; import android.app.NotificationManager; import android.os.Build; -import android.support.annotation.RequiresApi; +import androidx.annotation.RequiresApi; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.service.MusicService; @@ -70,7 +70,6 @@ private void createNotificationChannel() { notificationChannel.setDescription(service.getString(R.string.playing_notification_description)); notificationChannel.enableLights(false); notificationChannel.enableVibration(false); - notificationChannel.setShowBadge(false); notificationManager.createNotificationChannel(notificationChannel); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl.java b/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl.java index 357a91061..b12de5611 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl.java +++ b/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl.java @@ -6,11 +6,10 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.Color; import android.graphics.drawable.Drawable; -import android.support.annotation.Nullable; -import android.support.v4.app.NotificationCompat; +import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; import android.text.TextUtils; import android.view.View; import android.widget.RemoteViews; @@ -27,9 +26,9 @@ import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.service.MusicService; import com.kabouzeid.gramophone.ui.activities.MainActivity; +import com.kabouzeid.gramophone.util.ImageUtil; import com.kabouzeid.gramophone.util.PhonographColorUtil; import com.kabouzeid.gramophone.util.PreferenceUtil; -import com.kabouzeid.gramophone.util.Util; public class PlayingNotificationImpl extends PlayingNotification { @@ -133,9 +132,9 @@ private void setNotificationContent(boolean dark) { int primary = MaterialValueHelper.getPrimaryTextColor(service, dark); int secondary = MaterialValueHelper.getSecondaryTextColor(service, dark); - Bitmap prev = createBitmap(Util.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, primary), 1.5f); - Bitmap next = createBitmap(Util.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, primary), 1.5f); - Bitmap playPause = createBitmap(Util.getTintedVectorDrawable(service, isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp, primary), 1.5f); + Bitmap prev = ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, primary), 1.5f); + Bitmap next = ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, primary), 1.5f); + Bitmap playPause = ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp, primary), 1.5f); notificationLayout.setTextColor(R.id.title, primary); notificationLayout.setTextColor(R.id.text, secondary); @@ -182,11 +181,4 @@ private PendingIntent buildPendingIntent(Context context, final String action, f return PendingIntent.getService(context, 0, intent, 0); } - private static Bitmap createBitmap(Drawable drawable, float sizeMultiplier) { - Bitmap bitmap = Bitmap.createBitmap((int) (drawable.getIntrinsicWidth() * sizeMultiplier), (int) (drawable.getIntrinsicHeight() * sizeMultiplier), Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(bitmap); - drawable.setBounds(0, 0, c.getWidth(), c.getHeight()); - drawable.draw(c); - return bitmap; - } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl24.java b/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl24.java index 4d197f273..dfeba7f69 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl24.java +++ b/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl24.java @@ -8,10 +8,9 @@ import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.Build; -import android.support.v4.app.NotificationCompat; -import android.support.v4.media.app.NotificationCompat.MediaStyle; -import android.support.v7.graphics.Palette; -import android.text.TextUtils; +import androidx.core.app.NotificationCompat; +import androidx.media.app.NotificationCompat.MediaStyle; +import androidx.palette.graphics.Palette; import com.bumptech.glide.Glide; import com.bumptech.glide.request.animation.GlideAnimation; @@ -22,6 +21,7 @@ import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.service.MusicService; import com.kabouzeid.gramophone.ui.activities.MainActivity; +import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.PreferenceUtil; import static com.kabouzeid.gramophone.service.MusicService.ACTION_REWIND; @@ -36,11 +36,7 @@ public synchronized void update() { final Song song = service.getCurrentSong(); - final String albumName = song.albumName; - final String artistName = song.artistName; final boolean isPlaying = service.isPlaying(); - final String text = TextUtils.isEmpty(albumName) - ? artistName : artistName + " - " + albumName; final int playButtonResId = isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp; @@ -84,11 +80,12 @@ void update(Bitmap bitmap, int color) { retrievePlaybackAction(ACTION_SKIP)); NotificationCompat.Builder builder = new NotificationCompat.Builder(service, NOTIFICATION_CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) + .setSubText(song.albumName) .setLargeIcon(bitmap) .setContentIntent(clickIntent) .setDeleteIntent(deleteIntent) .setContentTitle(song.title) - .setContentText(text) + .setContentText(song.artistName) .setOngoing(isPlaying) .setShowWhen(false) .addAction(previousAction) diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/playback/Playback.java b/app/src/main/java/com/kabouzeid/gramophone/service/playback/Playback.java index ded13670f..5c4b9abdf 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/service/playback/Playback.java +++ b/app/src/main/java/com/kabouzeid/gramophone/service/playback/Playback.java @@ -1,6 +1,6 @@ package com.kabouzeid.gramophone.service.playback; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; /** * @author Karim Abou Zeid (kabouzeid) diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AboutActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AboutActivity.java index 7cee4e287..19d4caa17 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AboutActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AboutActivity.java @@ -5,9 +5,9 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v7.widget.AppCompatButton; -import android.support.v7.widget.Toolbar; +import androidx.annotation.NonNull; +import androidx.appcompat.widget.AppCompatButton; +import androidx.appcompat.widget.Toolbar; import android.view.MenuItem; import android.view.View; import android.widget.LinearLayout; @@ -35,11 +35,9 @@ public class AboutActivity extends AbsBaseActivity implements View.OnClickListen private static String GITHUB = "https://github.com/kabouzeid/Phonograph"; - private static String GOOGLE_PLUS = "https://google.com/+KarimAbouZeid23697"; - private static String TWITTER = "https://twitter.com/karim23697"; + private static String TWITTER = "https://twitter.com/karimjabouzeid"; private static String WEBSITE = "https://kabouzeid.com/"; - private static String GOOGLE_PLUS_COMMUNITY = "https://plus.google.com/u/0/communities/106227738496107108513"; private static String TRANSLATE = "https://phonograph.oneskyapp.com/collaboration/project?id=26521"; private static String RATE_ON_GOOGLE_PLAY = "https://play.google.com/store/apps/details?id=com.kabouzeid.gramophone"; @@ -56,6 +54,9 @@ public class AboutActivity extends AbsBaseActivity implements View.OnClickListen private static String EUGENE_CHEUNG_GITHUB = "https://github.com/arkon"; private static String EUGENE_CHEUNG_WEBSITE = "https://echeung.me/"; + private static String ADRIAN_TWITTER = "https://twitter.com/froschgames"; + private static String ADRIAN_WEBSITE = "https://froschgames.com/"; + @BindView(R.id.toolbar) Toolbar toolbar; @BindView(R.id.app_version) @@ -68,8 +69,6 @@ public class AboutActivity extends AbsBaseActivity implements View.OnClickListen LinearLayout licenses; @BindView(R.id.write_an_email) LinearLayout writeAnEmail; - @BindView(R.id.add_to_google_plus_circles) - LinearLayout addToGooglePlusCircles; @BindView(R.id.follow_on_twitter) LinearLayout followOnTwitter; @BindView(R.id.fork_on_github) @@ -78,8 +77,6 @@ public class AboutActivity extends AbsBaseActivity implements View.OnClickListen LinearLayout visitWebsite; @BindView(R.id.report_bugs) LinearLayout reportBugs; - @BindView(R.id.join_google_plus_community) - LinearLayout joinGooglePlusCommunity; @BindView(R.id.translate) LinearLayout translate; @BindView(R.id.donate) @@ -102,12 +99,16 @@ public class AboutActivity extends AbsBaseActivity implements View.OnClickListen AppCompatButton eugeneCheungGitHub; @BindView(R.id.eugene_cheung_website) AppCompatButton eugeneCheungWebsite; + @BindView(R.id.adrian_twitter) + AppCompatButton adrianTwitter; + @BindView(R.id.adrian_website) + AppCompatButton adrianWebsite; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_about); - setDrawUnderStatusbar(true); + setDrawUnderStatusbar(); ButterKnife.bind(this); setStatusbarColorAuto(); @@ -138,13 +139,11 @@ private void setUpOnClickListeners() { changelog.setOnClickListener(this); intro.setOnClickListener(this); licenses.setOnClickListener(this); - addToGooglePlusCircles.setOnClickListener(this); followOnTwitter.setOnClickListener(this); forkOnGitHub.setOnClickListener(this); visitWebsite.setOnClickListener(this); reportBugs.setOnClickListener(this); writeAnEmail.setOnClickListener(this); - joinGooglePlusCommunity.setOnClickListener(this); translate.setOnClickListener(this); rateOnGooglePlay.setOnClickListener(this); donate.setOnClickListener(this); @@ -156,6 +155,8 @@ private void setUpOnClickListeners() { aleksandarTesicGooglePlus.setOnClickListener(this); eugeneCheungGitHub.setOnClickListener(this); eugeneCheungWebsite.setOnClickListener(this); + adrianTwitter.setOnClickListener(this); + adrianWebsite.setOnClickListener(this); } @Override @@ -184,8 +185,6 @@ public void onClick(View v) { showLicenseDialog(); } else if (v == intro) { startActivity(new Intent(this, AppIntroActivity.class)); - } else if (v == addToGooglePlusCircles) { - openUrl(GOOGLE_PLUS); } else if (v == followOnTwitter) { openUrl(TWITTER); } else if (v == forkOnGitHub) { @@ -200,8 +199,6 @@ public void onClick(View v) { intent.putExtra(Intent.EXTRA_EMAIL, "contact@kabouzeid.com"); intent.putExtra(Intent.EXTRA_SUBJECT, "Phonograph"); startActivity(Intent.createChooser(intent, "E-Mail")); - } else if (v == joinGooglePlusCommunity) { - openUrl(GOOGLE_PLUS_COMMUNITY); } else if (v == translate) { openUrl(TRANSLATE); } else if (v == rateOnGooglePlay) { @@ -228,6 +225,10 @@ public void onClick(View v) { openUrl(EUGENE_CHEUNG_GITHUB); } else if (v == eugeneCheungWebsite) { openUrl(EUGENE_CHEUNG_WEBSITE); + } else if (v == adrianTwitter) { + openUrl(ADRIAN_TWITTER); + } else if (v == adrianWebsite) { + openUrl(ADRIAN_WEBSITE); } } @@ -249,6 +250,6 @@ private void showLicenseDialog() { ) .setIncludeOwnLicense(true) .build() - .showAppCompat(); + .show(); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AlbumDetailActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AlbumDetailActivity.java index a133aa676..6a860ffcd 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AlbumDetailActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AlbumDetailActivity.java @@ -2,15 +2,15 @@ import android.content.Context; import android.content.Intent; -import android.os.Build; +import android.graphics.PorterDuff; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.Loader; -import android.support.v7.widget.GridLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.Toolbar; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.appcompat.widget.Toolbar; import android.text.Html; import android.text.Spanned; import android.view.Menu; @@ -24,8 +24,6 @@ import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.util.DialogUtils; import com.bumptech.glide.Glide; -import com.bumptech.glide.request.RequestListener; -import com.bumptech.glide.request.target.Target; import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; import com.kabouzeid.appthemehelper.util.ColorUtil; import com.kabouzeid.appthemehelper.util.MaterialValueHelper; @@ -36,7 +34,6 @@ import com.kabouzeid.gramophone.dialogs.SleepTimerDialog; import com.kabouzeid.gramophone.glide.PhonographColoredTarget; import com.kabouzeid.gramophone.glide.SongGlideRequest; -import com.kabouzeid.gramophone.glide.palette.BitmapPaletteWrapper; import com.kabouzeid.gramophone.helper.MusicPlayerRemote; import com.kabouzeid.gramophone.interfaces.CabHolder; import com.kabouzeid.gramophone.interfaces.LoaderIds; @@ -51,13 +48,14 @@ import com.kabouzeid.gramophone.ui.activities.base.AbsSlidingMusicPanelActivity; import com.kabouzeid.gramophone.ui.activities.tageditor.AbsTagEditorActivity; import com.kabouzeid.gramophone.ui.activities.tageditor.AlbumTagEditorActivity; +import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.NavigationUtil; import com.kabouzeid.gramophone.util.PhonographColorUtil; -import com.kabouzeid.gramophone.util.Util; - -import java.util.Locale; +import com.kabouzeid.gramophone.util.PreferenceUtil; import java.util.ArrayList; +import java.util.List; +import java.util.Locale; import butterknife.BindView; import butterknife.ButterKnife; @@ -70,7 +68,6 @@ */ public class AlbumDetailActivity extends AbsSlidingMusicPanelActivity implements PaletteColorHolder, CabHolder, LoaderManager.LoaderCallbacks { - public static final String TAG = AlbumDetailActivity.class.getSimpleName(); private static final int TAG_EDITOR_REQUEST = 2001; private static final int LOADER_ID = LoaderIds.ALBUM_DETAIL_ACTIVITY; @@ -84,19 +81,33 @@ public class AlbumDetailActivity extends AbsSlidingMusicPanelActivity implements ImageView albumArtImageView; @BindView(R.id.toolbar) Toolbar toolbar; - @BindView(R.id.title) - TextView albumTitleView; - @BindView(R.id.list_background) - View songsBackgroundView; + @BindView(R.id.header) + View headerView; + @BindView(R.id.header_overlay) + View headerOverlay; + + @BindView(R.id.artist_icon) + ImageView artistIconImageView; + @BindView(R.id.duration_icon) + ImageView durationIconImageView; + @BindView(R.id.song_count_icon) + ImageView songCountIconImageView; + @BindView(R.id.album_year_icon) + ImageView albumYearIconImageView; + @BindView(R.id.artist_text) + TextView artistTextView; + @BindView(R.id.duration_text) + TextView durationTextView; + @BindView(R.id.song_count_text) + TextView songCountTextView; + @BindView(R.id.album_year_text) + TextView albumYearTextView; private AlbumSongAdapter adapter; private MaterialCab cab; - private int headerOffset; - private int titleViewHeight; - private int albumArtViewHeight; + private int headerViewHeight; private int toolbarColor; - private float toolbarAlpha; @Nullable private Spanned wiki; @@ -106,11 +117,9 @@ public class AlbumDetailActivity extends AbsSlidingMusicPanelActivity implements @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setDrawUnderStatusbar(true); + setDrawUnderStatusbar(); ButterKnife.bind(this); - supportPostponeEnterTransition(); - lastFMRestClient = new LastFMRestClient(this); setUpObservableListViewParams(); @@ -128,43 +137,32 @@ protected View createContentView() { private final SimpleObservableScrollViewCallbacks observableScrollViewCallbacks = new SimpleObservableScrollViewCallbacks() { @Override public void onScrollChanged(int scrollY, boolean b, boolean b2) { - scrollY += albumArtViewHeight + titleViewHeight; - float flexibleRange = albumArtViewHeight - headerOffset; - - // Translate album cover - albumArtImageView.setTranslationY(Math.max(-albumArtViewHeight, -scrollY / 2)); - - // Translate list background - songsBackgroundView.setTranslationY(Math.max(0, -scrollY + albumArtViewHeight)); + scrollY += headerViewHeight; // Change alpha of overlay - toolbarAlpha = Math.max(0, Math.min(1, (float) scrollY / flexibleRange)); - toolbar.setBackgroundColor(ColorUtil.withAlpha(toolbarColor, toolbarAlpha)); - setStatusbarColor(ColorUtil.withAlpha(toolbarColor, cab != null && cab.isActive() ? 1 : toolbarAlpha)); + float headerAlpha = Math.max(0, Math.min(1, (float) 2 * scrollY / headerViewHeight)); + headerOverlay.setBackgroundColor(ColorUtil.withAlpha(toolbarColor, headerAlpha)); // Translate name text - int maxTitleTranslationY = albumArtViewHeight; - int titleTranslationY = maxTitleTranslationY - scrollY; - titleTranslationY = Math.max(headerOffset, titleTranslationY); - - albumTitleView.setTranslationY(titleTranslationY); + headerView.setTranslationY(Math.max(-scrollY, -headerViewHeight)); + headerOverlay.setTranslationY(Math.max(-scrollY, -headerViewHeight)); + albumArtImageView.setTranslationY(Math.max(-scrollY, -headerViewHeight)); } }; private void setUpObservableListViewParams() { - albumArtViewHeight = getResources().getDimensionPixelSize(R.dimen.header_image_height); - toolbarColor = DialogUtils.resolveColor(this, R.attr.defaultFooterColor); - int toolbarHeight = Util.getActionBarSize(this); - titleViewHeight = getResources().getDimensionPixelSize(R.dimen.title_view_height); - headerOffset = toolbarHeight; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - headerOffset += getResources().getDimensionPixelSize(R.dimen.status_bar_padding); - } + headerViewHeight = getResources().getDimensionPixelSize(R.dimen.detail_header_height); } private void setUpViews() { setUpRecyclerView(); setUpSongsAdapter(); + artistTextView.setOnClickListener(v -> { + if (album != null) { + NavigationUtil.goToArtist(AlbumDetailActivity.this, album.getArtistId()); + } + }); + setColors(DialogUtils.resolveColor(this, R.attr.defaultFooterColor)); } private void loadAlbumCover() { @@ -172,19 +170,6 @@ private void loadAlbumCover() { .checkIgnoreMediaStore(this) .generatePalette(this).build() .dontAnimate() - .listener(new RequestListener() { - @Override - public boolean onException(Exception e, Object model, Target target, boolean isFirstResource) { - supportStartPostponedEnterTransition(); - return false; - } - - @Override - public boolean onResourceReady(BitmapPaletteWrapper resource, Object model, Target target, boolean isFromMemoryCache, boolean isFirstResource) { - supportStartPostponedEnterTransition(); - return false; - } - }) .into(new PhonographColoredTarget(albumArtImageView) { @Override public void onColorReady(int color) { @@ -195,11 +180,24 @@ public void onColorReady(int color) { private void setColors(int color) { toolbarColor = color; - albumTitleView.setBackgroundColor(color); - albumTitleView.setTextColor(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(color))); + headerView.setBackgroundColor(color); setNavigationbarColor(color); setTaskDescriptionColor(color); + + toolbar.setBackgroundColor(color); + setSupportActionBar(toolbar); // needed to auto readjust the toolbar content color + setStatusbarColor(color); + + int secondaryTextColor = MaterialValueHelper.getSecondaryTextColor(this, ColorUtil.isColorLight(color)); + artistIconImageView.setColorFilter(secondaryTextColor, PorterDuff.Mode.SRC_IN); + durationIconImageView.setColorFilter(secondaryTextColor, PorterDuff.Mode.SRC_IN); + songCountIconImageView.setColorFilter(secondaryTextColor, PorterDuff.Mode.SRC_IN); + albumYearIconImageView.setColorFilter(secondaryTextColor, PorterDuff.Mode.SRC_IN); + artistTextView.setTextColor(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(color))); + durationTextView.setTextColor(secondaryTextColor); + songCountTextView.setTextColor(secondaryTextColor); + albumYearTextView.setTextColor(secondaryTextColor); } @Override @@ -211,17 +209,11 @@ private void setUpRecyclerView() { setUpRecyclerViewPadding(); recyclerView.setScrollViewCallbacks(observableScrollViewCallbacks); final View contentView = getWindow().getDecorView().findViewById(android.R.id.content); - contentView.post(() -> { - songsBackgroundView.getLayoutParams().height = contentView.getHeight(); - observableScrollViewCallbacks.onScrollChanged(-(albumArtViewHeight + titleViewHeight), false, false); - // necessary to fix a bug - recyclerView.scrollBy(0, 1); - recyclerView.scrollBy(0, -1); - }); + contentView.post(() -> observableScrollViewCallbacks.onScrollChanged(-headerViewHeight, false, false)); } private void setUpRecyclerViewPadding() { - recyclerView.setPadding(0, albumArtViewHeight + titleViewHeight, 0, 0); + recyclerView.setPadding(0, headerViewHeight, 0, 0); } private void setUpToolBar() { @@ -251,7 +243,7 @@ private void reload() { @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_album_detail, menu); - return true; + return super.onCreateOptionsMenu(menu); } private void loadWiki() { @@ -280,7 +272,7 @@ public void onResponse(@NonNull Call call, @NonNull Response call, @NonNull Throwable t) { @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { int id = item.getItemId(); - final ArrayList songs = adapter.getDataSet(); + final List songs = adapter.getDataSet(); switch (id) { case R.id.action_sleep_timer: new SleepTimerDialog().show(getSupportFragmentManager(), "SET_SLEEP_TIMER"); @@ -341,7 +333,7 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) { .positiveText(android.R.string.ok) .build(); } - if (Util.isAllowedToDownloadMetadata(this)) { + if (PreferenceUtil.isAllowedToDownloadMetadata(this)) { if (wiki != null) { wikiDialog.setContent(wiki); wikiDialog.show(); @@ -377,7 +369,6 @@ public MaterialCab openCab(int menuRes, @NonNull final MaterialCab.Callback call .start(new MaterialCab.Callback() { @Override public boolean onCabCreated(MaterialCab materialCab, Menu menu) { - setStatusbarColor(ColorUtil.stripAlpha(toolbarColor)); return callback.onCabCreated(materialCab, menu); } @@ -388,7 +379,6 @@ public boolean onCabItemClicked(MenuItem menuItem) { @Override public boolean onCabFinished(MaterialCab materialCab) { - setStatusbarColor(ColorUtil.withAlpha(toolbarColor, toolbarAlpha)); return callback.onCabFinished(materialCab); } }); @@ -420,11 +410,16 @@ private void setAlbum(Album album) { this.album = album; loadAlbumCover(); - if (Util.isAllowedToDownloadMetadata(this)) { + if (PreferenceUtil.isAllowedToDownloadMetadata(this)) { loadWiki(); } - albumTitleView.setText(album.getTitle()); + getSupportActionBar().setTitle(album.getTitle()); + artistTextView.setText(album.getArtistName()); + songCountTextView.setText(MusicUtil.getSongCountString(this, album.getSongCount())); + durationTextView.setText(MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, album.songs))); + albumYearTextView.setText(MusicUtil.getYearString(album.getYear())); + adapter.swapDataSet(album.songs); } @@ -440,7 +435,6 @@ public Loader onCreateLoader(int id, Bundle args) { @Override public void onLoadFinished(Loader loader, Album data) { - supportStartPostponedEnterTransition(); setAlbum(data); } @@ -463,4 +457,4 @@ public Album loadInBackground() { return AlbumLoader.getAlbum(getContext(), albumId); } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java index 3791917fa..a46ab160c 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java @@ -2,15 +2,8 @@ import android.content.Context; import android.content.Intent; -import android.os.Build; +import android.graphics.PorterDuff; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.Loader; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.Toolbar; import android.text.Html; import android.text.Spanned; import android.view.LayoutInflater; @@ -20,6 +13,21 @@ import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.Toolbar; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import butterknife.BindView; +import butterknife.ButterKnife; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +import java.util.List; +import java.util.Locale; import com.afollestad.materialcab.MaterialCab; import com.afollestad.materialdialogs.MaterialDialog; @@ -48,52 +56,51 @@ import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.ui.activities.base.AbsSlidingMusicPanelActivity; import com.kabouzeid.gramophone.util.CustomArtistImageUtil; +import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.NavigationUtil; import com.kabouzeid.gramophone.util.PhonographColorUtil; import com.kabouzeid.gramophone.util.PreferenceUtil; -import com.kabouzeid.gramophone.util.Util; - -import java.util.ArrayList; -import java.util.Locale; - -import butterknife.BindView; -import butterknife.ButterKnife; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; /** * Be careful when changing things in this Activity! */ public class ArtistDetailActivity extends AbsSlidingMusicPanelActivity implements PaletteColorHolder, CabHolder, LoaderManager.LoaderCallbacks { - public static final String TAG = ArtistDetailActivity.class.getSimpleName(); private static final int LOADER_ID = LoaderIds.ARTIST_DETAIL_ACTIVITY; private static final int REQUEST_CODE_SELECT_IMAGE = 1000; public static final String EXTRA_ARTIST_ID = "extra_artist_id"; - @BindView(R.id.image) - ImageView artistImage; - @BindView(R.id.list_background) - View songListBackground; @BindView(R.id.list) ObservableListView songListView; - @BindView(R.id.title) - TextView artistName; + @BindView(R.id.image) + ImageView artistImage; @BindView(R.id.toolbar) Toolbar toolbar; + @BindView(R.id.header) + View headerView; + @BindView(R.id.header_overlay) + View headerOverlay; + + @BindView(R.id.duration_icon) + ImageView durationIconImageView; + @BindView(R.id.song_count_icon) + ImageView songCountIconImageView; + @BindView(R.id.album_count_icon) + ImageView albumCountIconImageView; + @BindView(R.id.duration_text) + TextView durationTextView; + @BindView(R.id.song_count_text) + TextView songCountTextView; + @BindView(R.id.album_count_text) + TextView albumCountTextView; View songListHeader; RecyclerView albumRecyclerView; private MaterialCab cab; - private int headerOffset; - private int titleViewHeight; - private int artistImageViewHeight; + private int headerViewHeight; private int toolbarColor; - private float toolbarAlpha; - private boolean usePalette; private Artist artist; @Nullable @@ -104,23 +111,35 @@ public class ArtistDetailActivity extends AbsSlidingMusicPanelActivity implement private LastFMRestClient lastFMRestClient; - private boolean forceDownload; + private final SimpleObservableScrollViewCallbacks observableScrollViewCallbacks = new SimpleObservableScrollViewCallbacks() { + @Override + public void onScrollChanged(int scrollY, boolean b, boolean b2) { + scrollY += headerViewHeight; + + // Change alpha of overlay + float headerAlpha = Math.max(0, Math.min(1, (float) 2 * scrollY / headerViewHeight)); + headerOverlay.setBackgroundColor(ColorUtil.withAlpha(toolbarColor, headerAlpha)); + + // Translate name text + headerView.setTranslationY(Math.max(-scrollY, -headerViewHeight)); + headerOverlay.setTranslationY(Math.max(-scrollY, -headerViewHeight)); + artistImage.setTranslationY(Math.max(-scrollY, -headerViewHeight)); + } + }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setDrawUnderStatusbar(true); + setDrawUnderStatusbar(); ButterKnife.bind(this); - supportPostponeEnterTransition(); - lastFMRestClient = new LastFMRestClient(this); usePalette = PreferenceUtil.getInstance(this).albumArtistColoredFooters(); initViews(); setUpObservableListViewParams(); - setUpViews(); setUpToolbar(); + setUpViews(); getSupportLoaderManager().initLoader(LOADER_ID, getIntent().getExtras(), this); } @@ -130,51 +149,21 @@ protected View createContentView() { return wrapSlidingMusicPanel(R.layout.activity_artist_detail); } - private final SimpleObservableScrollViewCallbacks observableScrollViewCallbacks = new SimpleObservableScrollViewCallbacks() { - @Override - public void onScrollChanged(int scrollY, boolean b, boolean b2) { - scrollY += artistImageViewHeight + titleViewHeight; - float flexibleRange = artistImageViewHeight - headerOffset; - - // Translate album cover - artistImage.setTranslationY(Math.max(-artistImageViewHeight, -scrollY / 2)); - - // Translate list background - songListBackground.setTranslationY(Math.max(0, -scrollY + artistImageViewHeight)); - - // Change alpha of overlay - toolbarAlpha = Math.max(0, Math.min(1, (float) scrollY / flexibleRange)); - toolbar.setBackgroundColor(ColorUtil.withAlpha(toolbarColor, toolbarAlpha)); - setStatusbarColor(ColorUtil.withAlpha(toolbarColor, cab != null && cab.isActive() ? 1 : toolbarAlpha)); - - // Translate name text - int maxTitleTranslationY = artistImageViewHeight; - int titleTranslationY = maxTitleTranslationY - scrollY; - titleTranslationY = Math.max(headerOffset, titleTranslationY); - - artistName.setTranslationY(titleTranslationY); - } - }; + private boolean usePalette; private void setUpObservableListViewParams() { - artistImageViewHeight = getResources().getDimensionPixelSize(R.dimen.header_image_height); - toolbarColor = DialogUtils.resolveColor(this, R.attr.defaultFooterColor); - int toolbarHeight = Util.getActionBarSize(this); - titleViewHeight = getResources().getDimensionPixelSize(R.dimen.title_view_height); - headerOffset = toolbarHeight; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - headerOffset += getResources().getDimensionPixelSize(R.dimen.status_bar_padding); - } + headerViewHeight = getResources().getDimensionPixelSize(R.dimen.detail_header_height); } private void initViews() { songListHeader = LayoutInflater.from(this).inflate(R.layout.artist_detail_header, songListView, false); - albumRecyclerView = ButterKnife.findById(songListHeader, R.id.recycler_view); + albumRecyclerView = songListHeader.findViewById(R.id.recycler_view); } private void setUpViews() { setUpSongListView(); setUpAlbumRecyclerView(); + setColors(DialogUtils.resolveColor(this, R.attr.defaultFooterColor)); } private void setUpSongListView() { @@ -186,14 +175,11 @@ private void setUpSongListView() { songListView.setAdapter(songAdapter); final View contentView = getWindow().getDecorView().findViewById(android.R.id.content); - contentView.post(() -> { - songListBackground.getLayoutParams().height = contentView.getHeight(); - observableScrollViewCallbacks.onScrollChanged(-(artistImageViewHeight + titleViewHeight), false, false); - }); + contentView.post(() -> observableScrollViewCallbacks.onScrollChanged(-headerViewHeight, false, false)); } private void setUpSongListPadding() { - songListView.setPadding(0, artistImageViewHeight + titleViewHeight, 0, 0); + songListView.setPadding(0, headerViewHeight, 0, 0); } private void setUpAlbumRecyclerView() { @@ -245,7 +231,7 @@ public void onResponse(@NonNull Call call, @NonNull Response call, @NonNull Throwable t) { private void loadArtistImage() { ArtistGlideRequest.Builder.from(Glide.with(this), artist) - .forceDownload(forceDownload) .generatePalette(this).build() .dontAnimate() .into(new PhonographColoredTarget(artistImage) { @@ -274,7 +259,6 @@ public void onColorReady(int color) { setColors(color); } }); - forceDownload = false; } @Override @@ -301,10 +285,22 @@ public int getPaletteColor() { private void setColors(int color) { toolbarColor = color; - artistName.setBackgroundColor(color); - artistName.setTextColor(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(color))); + headerView.setBackgroundColor(color); + setNavigationbarColor(color); setTaskDescriptionColor(color); + + toolbar.setBackgroundColor(color); + setSupportActionBar(toolbar); // needed to auto readjust the toolbar content color + setStatusbarColor(color); + + int secondaryTextColor = MaterialValueHelper.getSecondaryTextColor(this, ColorUtil.isColorLight(color)); + durationIconImageView.setColorFilter(secondaryTextColor, PorterDuff.Mode.SRC_IN); + songCountIconImageView.setColorFilter(secondaryTextColor, PorterDuff.Mode.SRC_IN); + albumCountIconImageView.setColorFilter(secondaryTextColor, PorterDuff.Mode.SRC_IN); + durationTextView.setTextColor(secondaryTextColor); + songCountTextView.setTextColor(secondaryTextColor); + albumCountTextView.setTextColor(secondaryTextColor); } private void setUpToolbar() { @@ -318,13 +314,13 @@ private void setUpToolbar() { public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_artist_detail, menu); menu.findItem(R.id.action_colored_footers).setChecked(usePalette); - return true; + return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { int id = item.getItemId(); - final ArrayList songs = songAdapter.getDataSet(); + final List songs = songAdapter.getDataSet(); switch (id) { case R.id.action_sleep_timer: new SleepTimerDialog().show(getSupportFragmentManager(), "SET_SLEEP_TIMER"); @@ -354,7 +350,7 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) { .positiveText(android.R.string.ok) .build(); } - if (Util.isAllowedToDownloadMetadata(ArtistDetailActivity.this)) { // wiki should've been already downloaded + if (PreferenceUtil.isAllowedToDownloadMetadata(ArtistDetailActivity.this)) { // wiki should've been already downloaded if (biography != null) { biographyDialog.setContent(biography); biographyDialog.show(); @@ -374,7 +370,6 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) { case R.id.action_reset_artist_image: Toast.makeText(ArtistDetailActivity.this, getResources().getString(R.string.updating), Toast.LENGTH_SHORT).show(); CustomArtistImageUtil.getInstance(ArtistDetailActivity.this).resetCustomArtistImage(artist); - forceDownload = true; return true; case R.id.action_colored_footers: item.setChecked(!item.isChecked()); @@ -395,7 +390,6 @@ public MaterialCab openCab(int menuRes, @NonNull final MaterialCab.Callback call .start(new MaterialCab.Callback() { @Override public boolean onCabCreated(MaterialCab materialCab, Menu menu) { - setStatusbarColor(ColorUtil.stripAlpha(toolbarColor)); return callback.onCabCreated(materialCab, menu); } @@ -406,7 +400,6 @@ public boolean onCabItemClicked(MenuItem menuItem) { @Override public boolean onCabFinished(MaterialCab materialCab) { - setStatusbarColor(ColorUtil.withAlpha(toolbarColor, toolbarAlpha)); return callback.onCabFinished(materialCab); } }); @@ -438,11 +431,15 @@ private void setArtist(Artist artist) { this.artist = artist; loadArtistImage(); - if (Util.isAllowedToDownloadMetadata(this)) { + if (PreferenceUtil.isAllowedToDownloadMetadata(this)) { loadBiography(); } - artistName.setText(artist.getName()); + getSupportActionBar().setTitle(artist.getName()); + songCountTextView.setText(MusicUtil.getSongCountString(this, artist.getSongCount())); + albumCountTextView.setText(MusicUtil.getAlbumCountString(this, artist.getAlbumCount())); + durationTextView.setText(MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, artist.getSongs()))); + songAdapter.swapDataSet(artist.getSongs()); albumAdapter.swapDataSet(artist.albums); } @@ -459,7 +456,6 @@ public Loader onCreateLoader(int id, Bundle args) { @Override public void onLoadFinished(Loader loader, Artist data) { - supportStartPostponedEnterTransition(); setArtist(data); } @@ -483,4 +479,4 @@ public Artist loadInBackground() { return ArtistLoader.getArtist(getContext(), artistId); } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/GenreDetailActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/GenreDetailActivity.java index 9aba61c84..e54cfa522 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/GenreDetailActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/GenreDetailActivity.java @@ -2,12 +2,12 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.Loader; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.Toolbar; +import androidx.annotation.NonNull; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.appcompat.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -31,13 +31,13 @@ import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; import java.util.ArrayList; +import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; -public class GenreDetailActivity extends AbsSlidingMusicPanelActivity implements CabHolder, LoaderManager.LoaderCallbacks> { +public class GenreDetailActivity extends AbsSlidingMusicPanelActivity implements CabHolder, LoaderManager.LoaderCallbacks> { - public static final String TAG = GenreDetailActivity.class.getSimpleName(); private static final int LOADER_ID = LoaderIds.GENRE_DETAIL_ACTIVITY; public static final String EXTRA_GENRE = "extra_genre"; @@ -59,7 +59,7 @@ public class GenreDetailActivity extends AbsSlidingMusicPanelActivity implements @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setDrawUnderStatusbar(true); + setDrawUnderStatusbar(); ButterKnife.bind(this); setStatusbarColorAuto(); @@ -84,7 +84,7 @@ private void setUpRecyclerView() { ViewUtil.setUpFastScrollRecyclerViewColor(this, ((FastScrollRecyclerView) recyclerView), ThemeStore.accentColor(this)); recyclerView.setLayoutManager(new LinearLayoutManager(this)); - adapter = new SongAdapter(this, new ArrayList(), R.layout.item_list, false, this); + adapter = new SongAdapter(this, new ArrayList<>(), R.layout.item_list, false, this); recyclerView.setAdapter(adapter); adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { @@ -174,23 +174,24 @@ protected void onDestroy() { } @Override - public Loader> onCreateLoader(int id, Bundle args) { + @NonNull + public Loader> onCreateLoader(int id, Bundle args) { return new GenreDetailActivity.AsyncGenreSongLoader(this, genre); } @Override - public void onLoadFinished(Loader> loader, ArrayList data) { + public void onLoadFinished(@NonNull Loader> loader, List data) { if (adapter != null) adapter.swapDataSet(data); } @Override - public void onLoaderReset(Loader> loader) { + public void onLoaderReset(@NonNull Loader> loader) { if (adapter != null) - adapter.swapDataSet(new ArrayList()); + adapter.swapDataSet(new ArrayList<>()); } - private static class AsyncGenreSongLoader extends WrappedAsyncTaskLoader> { + private static class AsyncGenreSongLoader extends WrappedAsyncTaskLoader> { private final Genre genre; public AsyncGenreSongLoader(Context context, Genre genre) { @@ -199,8 +200,8 @@ public AsyncGenreSongLoader(Context context, Genre genre) { } @Override - public ArrayList loadInBackground() { + public List loadInBackground() { return GenreLoader.getSongs(getContext(), genre.id); } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/MainActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/MainActivity.java index 8152c959d..f81e5ebb0 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/MainActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/MainActivity.java @@ -1,7 +1,6 @@ package com.kabouzeid.gramophone.ui.activities; import android.annotation.SuppressLint; -import android.annotation.TargetApi; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -10,27 +9,28 @@ import android.os.Bundle; import android.os.Handler; import android.provider.MediaStore; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.NavigationView; -import android.support.v4.app.Fragment; -import android.support.v4.widget.DrawerLayout; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.material.navigation.NavigationView; +import androidx.fragment.app.Fragment; +import androidx.drawerlayout.widget.DrawerLayout; import android.util.Log; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.view.WindowInsets; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.bumptech.glide.Glide; + import com.kabouzeid.appthemehelper.ThemeStore; import com.kabouzeid.appthemehelper.util.ATHUtil; import com.kabouzeid.appthemehelper.util.NavigationViewUtil; import com.kabouzeid.gramophone.App; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.dialogs.ChangelogDialog; +import com.kabouzeid.gramophone.dialogs.ScanMediaFolderChooserDialog; import com.kabouzeid.gramophone.glide.SongGlideRequest; import com.kabouzeid.gramophone.helper.MusicPlayerRemote; import com.kabouzeid.gramophone.helper.SearchQueryHelper; @@ -43,11 +43,13 @@ import com.kabouzeid.gramophone.ui.activities.intro.AppIntroActivity; import com.kabouzeid.gramophone.ui.fragments.mainactivity.folders.FoldersFragment; import com.kabouzeid.gramophone.ui.fragments.mainactivity.library.LibraryFragment; +import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.PreferenceUtil; -import com.kabouzeid.gramophone.util.Util; + import com.sothree.slidinguppanel.SlidingUpPanelLayout; import java.util.ArrayList; +import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; @@ -77,19 +79,11 @@ public class MainActivity extends AbsSlidingMusicPanelActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setDrawUnderStatusbar(); ButterKnife.bind(this); if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { - Util.setStatusBarTranslucent(getWindow()); - drawerLayout.setFitsSystemWindows(false); - navigationView.setFitsSystemWindows(false); - //noinspection ConstantConditions - findViewById(R.id.drawer_content_container).setFitsSystemWindows(false); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - drawerLayout.setOnApplyWindowInsetsListener((view, windowInsets) -> { - navigationView.dispatchApplyWindowInsets(windowInsets); - return windowInsets.replaceSystemWindowInsets(0, 0, 0, 0); - }); + navigationView.setFitsSystemWindows(false); // for header to go below statusbar } setUpDrawerLayout(); @@ -101,7 +95,7 @@ protected void onCreate(Bundle savedInstanceState) { } if (!checkShowIntro()) { - checkShowChangelog(); + showChangelog(); } } @@ -159,7 +153,7 @@ protected void requestPermissions() { protected View createContentView() { @SuppressLint("InflateParams") View contentView = getLayoutInflater().inflate(R.layout.activity_main_drawer_layout, null); - ViewGroup drawerContent = ButterKnife.findById(contentView, R.id.drawer_content_container); + ViewGroup drawerContent = contentView.findViewById(R.id.drawer_content_container); drawerContent.addView(wrapSlidingMusicPanel(R.layout.activity_main_content)); return contentView; } @@ -182,6 +176,12 @@ private void setUpNavigationView() { case R.id.buy_pro: new Handler().postDelayed(() -> startActivityForResult(new Intent(MainActivity.this, PurchaseActivity.class), PURCHASE_REQUEST), 200); break; + case R.id.action_scan: + new Handler().postDelayed(() -> { + ScanMediaFolderChooserDialog dialog = ScanMediaFolderChooserDialog.create(); + dialog.show(getSupportFragmentManager(), "SCAN_MEDIA_FOLDER_CHOOSER"); + }, 200); + break; case R.id.nav_settings: new Handler().postDelayed(() -> startActivity(new Intent(MainActivity.this, SettingsActivity.class)), 200); break; @@ -221,7 +221,7 @@ private void updateNavigationDrawerHeader() { }); } ((TextView) navigationDrawerHeader.findViewById(R.id.title)).setText(song.title); - ((TextView) navigationDrawerHeader.findViewById(R.id.text)).setText(song.artistName); + ((TextView) navigationDrawerHeader.findViewById(R.id.text)).setText(MusicUtil.getSongInfoString(song)); SongGlideRequest.Builder.from(Glide.with(this), song) .checkIgnoreMediaStore(this).build() .into(((ImageView) navigationDrawerHeader.findViewById(R.id.image))); @@ -278,7 +278,7 @@ private void handlePlaybackIntent(@Nullable Intent intent) { boolean handled = false; if (intent.getAction() != null && intent.getAction().equals(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH)) { - final ArrayList songs = SearchQueryHelper.getSongs(this, intent.getExtras()); + final List songs = SearchQueryHelper.getSongs(this, intent.getExtras()); if (MusicPlayerRemote.getShuffleMode() == MusicService.SHUFFLE_MODE_SHUFFLE) { MusicPlayerRemote.openAndShuffleQueue(songs, true); } else { @@ -294,8 +294,7 @@ private void handlePlaybackIntent(@Nullable Intent intent) { final int id = (int) parseIdFromIntent(intent, "playlistId", "playlist"); if (id >= 0) { int position = intent.getIntExtra("position", 0); - ArrayList songs = new ArrayList<>(); - songs.addAll(PlaylistSongLoader.getPlaylistSongList(this, id)); + List songs = new ArrayList<>(PlaylistSongLoader.getPlaylistSongList(this, id)); MusicPlayerRemote.openQueue(songs, position, true); handled = true; } @@ -358,21 +357,19 @@ private boolean checkShowIntro() { return false; } - private boolean checkShowChangelog() { + private void showChangelog() { try { PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); int currentVersion = pInfo.versionCode; if (currentVersion != PreferenceUtil.getInstance(this).getLastChangelogVersion()) { ChangelogDialog.create().show(getSupportFragmentManager(), "CHANGE_LOG_DIALOG"); - return true; } } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } - return false; } public interface MainActivityFragmentCallbacks { boolean handleBackPress(); } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/PlaylistDetailActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/PlaylistDetailActivity.java index fce568626..0b9666c64 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/PlaylistDetailActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/PlaylistDetailActivity.java @@ -2,17 +2,18 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.Loader; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.appcompat.widget.Toolbar; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import com.afollestad.materialcab.MaterialCab; import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator; @@ -32,7 +33,6 @@ import com.kabouzeid.gramophone.misc.WrappedAsyncTaskLoader; import com.kabouzeid.gramophone.model.AbsCustomPlaylist; import com.kabouzeid.gramophone.model.Playlist; -import com.kabouzeid.gramophone.model.PlaylistSong; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.ui.activities.base.AbsSlidingMusicPanelActivity; import com.kabouzeid.gramophone.util.PhonographColorUtil; @@ -46,9 +46,7 @@ import butterknife.BindView; import butterknife.ButterKnife; -public class PlaylistDetailActivity extends AbsSlidingMusicPanelActivity implements CabHolder, LoaderManager.LoaderCallbacks> { - - public static final String TAG = PlaylistDetailActivity.class.getSimpleName(); +public class PlaylistDetailActivity extends AbsSlidingMusicPanelActivity implements CabHolder, LoaderManager.LoaderCallbacks> { private static final int LOADER_ID = LoaderIds.PLAYLIST_DETAIL_ACTIVITY; @@ -73,7 +71,7 @@ public class PlaylistDetailActivity extends AbsSlidingMusicPanelActivity impleme @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setDrawUnderStatusbar(true); + setDrawUnderStatusbar(); ButterKnife.bind(this); setStatusbarColorAuto(); @@ -98,12 +96,12 @@ private void setUpRecyclerView() { ViewUtil.setUpFastScrollRecyclerViewColor(this, ((FastScrollRecyclerView) recyclerView), ThemeStore.accentColor(this)); recyclerView.setLayoutManager(new LinearLayoutManager(this)); if (playlist instanceof AbsCustomPlaylist) { - adapter = new PlaylistSongAdapter(this, new ArrayList(), R.layout.item_list, false, this); + adapter = new PlaylistSongAdapter(this, new ArrayList<>(), R.layout.item_list, false, this); recyclerView.setAdapter(adapter); } else { recyclerViewDragDropManager = new RecyclerViewDragDropManager(); final GeneralItemAnimator animator = new RefactoredDefaultItemAnimator(); - adapter = new OrderablePlaylistSongAdapter(this, new ArrayList(), R.layout.item_list, false, this, (fromPosition, toPosition) -> { + adapter = new OrderablePlaylistSongAdapter(this, new ArrayList<>(), R.layout.item_list, false, this, (fromPosition, toPosition) -> { if (PlaylistsUtil.moveItem(PlaylistDetailActivity.this, playlist.id, fromPosition, toPosition)) { Song song = adapter.getDataSet().remove(fromPosition); adapter.getDataSet().add(toPosition, song); @@ -240,23 +238,23 @@ protected void onDestroy() { } @Override - public Loader> onCreateLoader(int id, Bundle args) { + public Loader> onCreateLoader(int id, Bundle args) { return new AsyncPlaylistSongLoader(this, playlist); } @Override - public void onLoadFinished(Loader> loader, ArrayList data) { + public void onLoadFinished(Loader> loader, List data) { if (adapter != null) adapter.swapDataSet(data); } @Override - public void onLoaderReset(Loader> loader) { + public void onLoaderReset(Loader> loader) { if (adapter != null) - adapter.swapDataSet(new ArrayList()); + adapter.swapDataSet(new ArrayList<>()); } - private static class AsyncPlaylistSongLoader extends WrappedAsyncTaskLoader> { + private static class AsyncPlaylistSongLoader extends WrappedAsyncTaskLoader> { private final Playlist playlist; public AsyncPlaylistSongLoader(Context context, Playlist playlist) { @@ -265,12 +263,12 @@ public AsyncPlaylistSongLoader(Context context, Playlist playlist) { } @Override - public ArrayList loadInBackground() { + public List loadInBackground() { if (playlist instanceof AbsCustomPlaylist) { return ((AbsCustomPlaylist) playlist).getSongs(getContext()); } else { //noinspection unchecked - return (ArrayList) (List) PlaylistSongLoader.getPlaylistSongList(getContext(), playlist.id); + return (List) PlaylistSongLoader.getPlaylistSongList(getContext(), playlist.id); } } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/PurchaseActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/PurchaseActivity.java index 826b05e9b..8c831e1b7 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/PurchaseActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/PurchaseActivity.java @@ -3,9 +3,9 @@ import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.widget.Toolbar; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.Toolbar; import android.util.Log; import android.view.MenuItem; import android.widget.Button; @@ -13,19 +13,12 @@ import com.anjlab.android.iab.v3.BillingProcessor; import com.anjlab.android.iab.v3.TransactionDetails; -import com.crashlytics.android.answers.AddToCartEvent; -import com.crashlytics.android.answers.Answers; -import com.crashlytics.android.answers.ContentViewEvent; -import com.crashlytics.android.answers.PurchaseEvent; import com.kabouzeid.appthemehelper.color.MaterialColor; import com.kabouzeid.gramophone.App; -import com.kabouzeid.gramophone.BuildConfig; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.ui.activities.base.AbsBaseActivity; import java.lang.ref.WeakReference; -import java.math.BigDecimal; -import java.util.Currency; import butterknife.BindView; import butterknife.ButterKnife; @@ -50,7 +43,7 @@ public class PurchaseActivity extends AbsBaseActivity implements BillingProcesso protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_purchase); - setDrawUnderStatusbar(true); + setDrawUnderStatusbar(); ButterKnife.bind(this); setStatusbarColor(ACTIVITY_COLOR); @@ -73,23 +66,9 @@ protected void onCreate(Bundle savedInstanceState) { purchaseButton.setOnClickListener(v -> { billingProcessor.purchase(PurchaseActivity.this, App.PRO_VERSION_PRODUCT_ID); - - if (!BuildConfig.DEBUG) { - Answers.getInstance().logAddToCart(new AddToCartEvent() - .putCurrency(Currency.getInstance("EUR")) - .putItemId("pro_version") - .putItemName("Phonograph Pro") - .putItemPrice(BigDecimal.valueOf(3))); - } }); billingProcessor = new BillingProcessor(this, App.GOOGLE_PLAY_LICENSE_KEY, this); - - if (!BuildConfig.DEBUG) { - Answers.getInstance().logContentView(new ContentViewEvent() - .putContentName("Purchase Activity") - .putContentId("1")); - } } private void restorePurchase() { @@ -103,15 +82,6 @@ private void restorePurchase() { public void onProductPurchased(@NonNull String productId, @Nullable TransactionDetails details) { Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show(); setResult(RESULT_OK); - - if (!BuildConfig.DEBUG) { - Answers.getInstance().logPurchase(new PurchaseEvent() - .putCurrency(Currency.getInstance("EUR")) - .putItemPrice(BigDecimal.valueOf(3)) - .putItemId("pro_version") - .putSuccess(true) - .putItemName("Phonograph Pro")); - } } @Override diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SearchActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SearchActivity.java index 8cf0fff08..a7bb042ee 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SearchActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SearchActivity.java @@ -2,18 +2,16 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.Loader; -import android.support.v4.view.MenuItemCompat; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.SearchView; -import android.support.v7.widget.Toolbar; +import androidx.annotation.NonNull; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.appcompat.widget.SearchView; +import androidx.appcompat.widget.Toolbar; import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; -import android.view.MotionEvent; import android.view.View; import android.widget.TextView; @@ -36,7 +34,7 @@ import butterknife.ButterKnife; public class SearchActivity extends AbsMusicServiceActivity implements SearchView.OnQueryTextListener, LoaderManager.LoaderCallbacks> { - public static final String TAG = SearchActivity.class.getSimpleName(); + public static final String QUERY = "query"; private static final int LOADER_ID = LoaderIds.SEARCH_ACTIVITY; @@ -56,7 +54,7 @@ public class SearchActivity extends AbsMusicServiceActivity implements SearchVie protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_search); - setDrawUnderStatusbar(true); + setDrawUnderStatusbar(); ButterKnife.bind(this); setStatusbarColorAuto(); @@ -94,11 +92,6 @@ protected void onSaveInstanceState(Bundle outState) { outState.putString(QUERY, query); } - @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); - } - private void setUpToolBar() { toolbar.setBackgroundColor(ThemeStore.primaryColor(this)); setSupportActionBar(toolbar); @@ -111,12 +104,12 @@ public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_search, menu); final MenuItem searchItem = menu.findItem(R.id.search); - searchView = (SearchView) MenuItemCompat.getActionView(searchItem); + searchView = (SearchView) searchItem.getActionView(); searchView.setQueryHint(getString(R.string.search_hint)); searchView.setMaxWidth(Integer.MAX_VALUE); - MenuItemCompat.expandActionView(searchItem); - MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() { + searchItem.expandActionView(); + searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() { @Override public boolean onMenuItemActionExpand(MenuItem item) { return true; @@ -200,19 +193,19 @@ public AsyncSearchResultLoader(Context context, String query) { public List loadInBackground() { List results = new ArrayList<>(); if (!TextUtils.isEmpty(query)) { - List songs = SongLoader.getSongs(getContext(), query); + List songs = SongLoader.getSongs(getContext(), query.trim()); if (!songs.isEmpty()) { results.add(getContext().getResources().getString(R.string.songs)); results.addAll(songs); } - List artists = ArtistLoader.getArtists(getContext(), query); + List artists = ArtistLoader.getArtists(getContext(), query.trim()); if (!artists.isEmpty()) { results.add(getContext().getResources().getString(R.string.artists)); results.addAll(artists); } - List albums = AlbumLoader.getAlbums(getContext(), query); + List albums = AlbumLoader.getAlbums(getContext(), query.trim()); if (!albums.isEmpty()) { results.add(getContext().getResources().getString(R.string.albums)); results.addAll(albums); diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SettingsActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SettingsActivity.java index 9e43e57bc..bba947bb8 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SettingsActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/SettingsActivity.java @@ -7,15 +7,15 @@ import android.media.audiofx.AudioEffect; import android.os.Build; import android.os.Bundle; -import android.support.annotation.ColorInt; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.DialogFragment; -import android.support.v7.preference.ListPreference; -import android.support.v7.preference.Preference; -import android.support.v7.preference.PreferenceManager; -import android.support.v7.preference.TwoStatePreference; -import android.support.v7.widget.Toolbar; +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceManager; +import androidx.preference.TwoStatePreference; +import androidx.appcompat.widget.Toolbar; import android.view.MenuItem; import android.view.View; import android.widget.Toast; @@ -45,7 +45,6 @@ import butterknife.ButterKnife; public class SettingsActivity extends AbsBaseActivity implements ColorChooserDialog.ColorCallback { - public static final String TAG = SettingsActivity.class.getSimpleName(); @BindView(R.id.toolbar) Toolbar toolbar; @@ -54,7 +53,7 @@ public class SettingsActivity extends AbsBaseActivity implements ColorChooserDia protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_preferences); - setDrawUnderStatusbar(true); + setDrawUnderStatusbar(); ButterKnife.bind(this); setStatusbarColorAuto(); @@ -200,15 +199,13 @@ private void invalidateSettings() { return false; } - int theme = PreferenceUtil.getThemeResFromPrefValue(themeName); setSummary(generalTheme, o); - ThemeStore.editTheme(getActivity()) - .activityTheme(theme) - .commit(); + + ThemeStore.markChanged(getActivity()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { // Set the new theme so that updateAppShortcuts can pull it - getActivity().setTheme(PreferenceUtil.getThemeResFromPrefValue((String) o)); + getActivity().setTheme(PreferenceUtil.getThemeResFromPrefValue(themeName)); new DynamicShortcutManager(getActivity()).updateDynamicShortcuts(); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsBaseActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsBaseActivity.java index 0f2682def..17c28f931 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsBaseActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsBaseActivity.java @@ -8,10 +8,10 @@ import android.os.Build; import android.os.Bundle; import android.provider.Settings; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.Snackbar; -import android.support.v4.app.ActivityCompat; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.material.snackbar.Snackbar; +import androidx.core.app.ActivityCompat; import android.view.KeyEvent; import android.view.View; diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsMusicServiceActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsMusicServiceActivity.java index 8a0a36730..32a87da24 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsMusicServiceActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsMusicServiceActivity.java @@ -9,8 +9,8 @@ import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.helper.MusicPlayerRemote; @@ -19,20 +19,19 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ public abstract class AbsMusicServiceActivity extends AbsBaseActivity implements MusicServiceEventListener { - public static final String TAG = AbsMusicServiceActivity.class.getSimpleName(); - private final ArrayList mMusicServiceEventListeners = new ArrayList<>(); + private final List mMusicServiceEventListeners = new ArrayList<>(); private MusicPlayerRemote.ServiceToken serviceToken; private MusicStateReceiver musicStateReceiver; private boolean receiverRegistered; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java index ac3f0921c..730204e87 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java @@ -3,13 +3,12 @@ import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; -import android.annotation.TargetApi; import android.os.Build; import android.os.Bundle; -import android.support.annotation.ColorInt; -import android.support.annotation.FloatRange; -import android.support.annotation.LayoutRes; -import android.support.v4.app.Fragment; +import androidx.annotation.ColorInt; +import androidx.annotation.FloatRange; +import androidx.annotation.LayoutRes; +import androidx.fragment.app.Fragment; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; @@ -36,7 +35,6 @@ * {@link #wrapSlidingMusicPanel(int)} first and then return it in {@link #createContentView()} */ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivity implements SlidingUpPanelLayout.PanelSlideListener, CardPlayerFragment.Callbacks { - public static final String TAG = AbsSlidingMusicPanelActivity.class.getSimpleName(); @BindView(R.id.sliding_layout) SlidingUpPanelLayout slidingUpPanelLayout; @@ -83,13 +81,17 @@ protected void onCreate(Bundle savedInstanceState) { public void onGlobalLayout() { slidingUpPanelLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); - if (getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) { - onPanelSlide(slidingUpPanelLayout, 1); - onPanelExpanded(slidingUpPanelLayout); - } else if (getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { - onPanelCollapsed(slidingUpPanelLayout); - } else { - playerFragment.onHide(); + switch (getPanelState()) { + case EXPANDED: + onPanelSlide(slidingUpPanelLayout, 1); + onPanelExpanded(slidingUpPanelLayout); + break; + case COLLAPSED: + onPanelCollapsed(slidingUpPanelLayout); + break; + default: + playerFragment.onHide(); + break; } } }); @@ -208,7 +210,7 @@ public void hideBottomBar(final boolean hide) { protected View wrapSlidingMusicPanel(@LayoutRes int resId) { @SuppressLint("InflateParams") View slidingMusicPanelLayout = getLayoutInflater().inflate(R.layout.sliding_music_panel_layout, null); - ViewGroup contentContainer = ButterKnife.findById(slidingMusicPanelLayout, R.id.content_container); + ViewGroup contentContainer = slidingMusicPanelLayout.findViewById(R.id.content_container); getLayoutInflater().inflate(resId, contentContainer); return slidingMusicPanelLayout; } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsThemeActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsThemeActivity.java index c9e58134d..338f88882 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsThemeActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsThemeActivity.java @@ -3,7 +3,7 @@ import android.graphics.Color; import android.os.Build; import android.os.Bundle; -import android.support.annotation.ColorInt; +import androidx.annotation.ColorInt; import android.view.View; import com.kabouzeid.appthemehelper.ATH; @@ -23,11 +23,12 @@ public abstract class AbsThemeActivity extends ATHToolbarActivity { @Override protected void onCreate(Bundle savedInstanceState) { + setTheme(PreferenceUtil.getInstance(this).getGeneralTheme()); super.onCreate(savedInstanceState); MaterialDialogsUtil.updateMaterialDialogsThemeSingleton(this); } - protected void setDrawUnderStatusbar(boolean drawUnderStatusbar) { + protected void setDrawUnderStatusbar() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) Util.setAllowDrawUnderStatusBar(getWindow()); else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) @@ -89,4 +90,4 @@ public void setLightStatusbar(boolean enabled) { public void setLightStatusbarAuto(int bgColor) { setLightStatusbar(ColorUtil.isColorLight(bgColor)); } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/BugReportActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/BugReportActivity.java index 69df1c4a9..70eda7edf 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/BugReportActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/BugReportActivity.java @@ -5,19 +5,17 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.StringDef; -import android.support.annotation.StringRes; -import android.support.design.widget.FloatingActionButton; -import android.support.design.widget.TextInputEditText; -import android.support.design.widget.TextInputLayout; -import android.support.v7.widget.Toolbar; +import androidx.annotation.NonNull; +import androidx.annotation.StringDef; +import androidx.annotation.StringRes; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.textfield.TextInputEditText; +import com.google.android.material.textfield.TextInputLayout; +import androidx.appcompat.widget.Toolbar; import android.text.TextUtils; -import android.view.KeyEvent; import android.view.MenuItem; import android.view.View; import android.view.inputmethod.EditorInfo; @@ -25,7 +23,6 @@ import android.widget.TextView; import android.widget.Toast; -import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import com.kabouzeid.appthemehelper.ThemeStore; import com.kabouzeid.appthemehelper.util.TintHelper; @@ -263,18 +260,12 @@ private void sendBugReport(GithubLogin login) { String bugTitle = inputTitle.getText().toString(); String bugDescription = inputDescription.getText().toString(); - ExtraInfo extraInfo = new ExtraInfo(); - onSaveExtraInfo(extraInfo); - - Report report = new Report(bugTitle, bugDescription, deviceInfo, extraInfo); + Report report = new Report(bugTitle, bugDescription, deviceInfo, new ExtraInfo()); GithubTarget target = new GithubTarget("kabouzeid", "Phonograph"); ReportIssueAsyncTask.report(this, report, target, login); } - protected void onSaveExtraInfo(ExtraInfo extraInfo) { - } - @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/DeviceInfo.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/DeviceInfo.java index 2f6108359..ad67c147f 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/DeviceInfo.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/DeviceInfo.java @@ -5,7 +5,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; -import android.support.annotation.IntRange; +import androidx.annotation.IntRange; import java.util.Arrays; diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/github/GithubLogin.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/github/GithubLogin.java index 97430f36e..e3713172c 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/github/GithubLogin.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/bugreport/model/github/GithubLogin.java @@ -3,10 +3,9 @@ import android.text.TextUtils; public class GithubLogin { - private final String username; + private final String username; private final String password; - private final String apiToken; public GithubLogin(String username, String password) { diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AbsTagEditorActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AbsTagEditorActivity.java index ed10d385b..53cf9c2ba 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AbsTagEditorActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AbsTagEditorActivity.java @@ -10,10 +10,10 @@ import android.media.MediaScannerConnection; import android.net.Uri; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.FloatingActionButton; -import android.support.v7.widget.Toolbar; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import androidx.appcompat.widget.Toolbar; import android.util.Log; import android.view.MenuItem; import android.view.View; diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AlbumTagEditorActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AlbumTagEditorActivity.java index f1f46893b..02d880ebc 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AlbumTagEditorActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AlbumTagEditorActivity.java @@ -6,7 +6,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; @@ -45,8 +45,6 @@ public class AlbumTagEditorActivity extends AbsTagEditorActivity implements TextWatcher { - public static final String TAG = AlbumTagEditorActivity.class.getSimpleName(); - @BindView(R.id.title) EditText albumTitle; @BindView(R.id.album_artist) @@ -182,8 +180,8 @@ protected int getContentViewLayout() { @NonNull @Override protected List getSongPaths() { - ArrayList songs = AlbumLoader.getAlbum(this, getId()).songs; - ArrayList paths = new ArrayList<>(songs.size()); + List songs = AlbumLoader.getAlbum(this, getId()).songs; + List paths = new ArrayList<>(songs.size()); for (Song song : songs) { paths.add(song.data); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/SongTagEditorActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/SongTagEditorActivity.java index a48224b22..303e67fe3 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/SongTagEditorActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/SongTagEditorActivity.java @@ -2,7 +2,7 @@ import android.net.Uri; import android.os.Bundle; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.text.Editable; import android.text.TextWatcher; import android.widget.EditText; @@ -23,8 +23,6 @@ public class SongTagEditorActivity extends AbsTagEditorActivity implements TextWatcher { - public static final String TAG = SongTagEditorActivity.class.getSimpleName(); - @BindView(R.id.title1) EditText songTitle; @BindView(R.id.title2) @@ -114,7 +112,7 @@ protected int getContentViewLayout() { @NonNull @Override protected List getSongPaths() { - ArrayList paths = new ArrayList<>(1); + List paths = new ArrayList<>(1); paths.add(SongLoader.getSong(this, getId()).data); return paths; } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/AbsMusicServiceFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/AbsMusicServiceFragment.java index 05193ab35..78847652a 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/AbsMusicServiceFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/AbsMusicServiceFragment.java @@ -2,7 +2,8 @@ import android.content.Context; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import android.view.View; import com.kabouzeid.gramophone.interfaces.MusicServiceEventListener; @@ -31,7 +32,7 @@ public void onDetach() { } @Override - public void onViewCreated(View view, Bundle savedInstanceState) { + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); activity.addMusicServiceEventListener(this); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/AbsMainActivityFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/AbsMainActivityFragment.java index 9c7a3e0e5..eb6657bc9 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/AbsMainActivityFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/AbsMainActivityFragment.java @@ -1,13 +1,8 @@ package com.kabouzeid.gramophone.ui.fragments.mainactivity; -import android.os.Build; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.View; +import androidx.fragment.app.Fragment; -import com.kabouzeid.appthemehelper.ThemeStore; -import com.kabouzeid.appthemehelper.util.ColorUtil; -import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.ui.activities.MainActivity; /** @@ -24,24 +19,4 @@ public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setHasOptionsMenu(true); } - - // WORKAROUND - public void setStatusbarColor(View view, int color) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - final View statusBar = view.findViewById(R.id.status_bar); - if (statusBar != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - statusBar.setBackgroundColor(ColorUtil.darkenColor(color)); - getMainActivity().setLightStatusbarAuto(color); - } else { - statusBar.setBackgroundColor(color); - } - } - } - } - - public void setStatusbarColorAuto(View view) { - // we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat - setStatusbarColor(view, ThemeStore.primaryColor(getContext())); - } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/folders/FoldersFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/folders/FoldersFragment.java index b8b6ea470..5041a4208 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/folders/FoldersFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/folders/FoldersFragment.java @@ -3,20 +3,9 @@ import android.app.Dialog; import android.content.Context; -import android.content.DialogInterface; import android.media.MediaScannerConnection; import android.os.Bundle; import android.os.Environment; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.AppBarLayout; -import android.support.design.widget.CoordinatorLayout; -import android.support.design.widget.Snackbar; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.Loader; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.Toolbar; import android.text.Html; import android.view.LayoutInflater; import android.view.Menu; @@ -28,9 +17,19 @@ import android.widget.PopupMenu; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.Toolbar; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import com.afollestad.materialcab.MaterialCab; -import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; +import com.google.android.material.appbar.AppBarLayout; +import com.google.android.material.snackbar.Snackbar; import com.kabouzeid.appthemehelper.ThemeStore; import com.kabouzeid.appthemehelper.common.ATHToolbarActivity; import com.kabouzeid.appthemehelper.util.ToolbarContentTintHelper; @@ -56,7 +55,6 @@ import java.io.File; import java.io.FileFilter; -import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; @@ -69,7 +67,6 @@ import butterknife.Unbinder; public class FoldersFragment extends AbsMainActivityFragment implements MainActivity.MainActivityFragmentCallbacks, CabHolder, BreadCrumbLayout.SelectionCallback, SongFileAdapter.Callbacks, AppBarLayout.OnOffsetChangedListener, LoaderManager.LoaderCallbacks> { - public static final String TAG = FoldersFragment.class.getSimpleName(); private static final int LOADER_ID = LoaderIds.FOLDERS_FRAGMENT; @@ -134,7 +131,7 @@ private BreadCrumbLayout.Crumb getActiveCrumb() { } @Override - public void onSaveInstanceState(Bundle outState) { + public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putParcelable(CRUMBS, breadCrumbs.getStateWrapper()); } @@ -143,7 +140,7 @@ public void onSaveInstanceState(Bundle outState) { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (savedInstanceState == null) { - setCrumb(new BreadCrumbLayout.Crumb(tryGetCanonicalFile((File) getArguments().getSerializable(PATH))), true); + setCrumb(new BreadCrumbLayout.Crumb(FileUtil.safeGetCanonicalFile((File) getArguments().getSerializable(PATH))), true); } else { breadCrumbs.restoreFromStateWrapper(savedInstanceState.getParcelable(CRUMBS)); getLoaderManager().initLoader(LOADER_ID, null, this); @@ -151,15 +148,15 @@ public void onActivityCreated(Bundle savedInstanceState) { } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_folder, container, false); unbinder = ButterKnife.bind(this, view); return view; } @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - setStatusbarColorAuto(view); + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + getMainActivity().setStatusbarColorAuto(); getMainActivity().setNavigationbarColorAuto(); getMainActivity().setTaskDescriptionColorAuto(); @@ -198,7 +195,7 @@ private void setUpRecyclerView() { } private void setUpAdapter() { - adapter = new SongFileAdapter(getMainActivity(), new LinkedList(), R.layout.item_list, this, this); + adapter = new SongFileAdapter(getMainActivity(), new LinkedList<>(), R.layout.item_list, this, this); adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { @Override public void onChanged() { @@ -261,15 +258,9 @@ public void onPrepareOptionsMenu(Menu menu) { ToolbarContentTintHelper.handleOnPrepareOptionsMenu(getActivity(), toolbar); } - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - switch (item.getItemId()) { - case R.id.action_go_to_start_directory: - setCrumb(new BreadCrumbLayout.Crumb(tryGetCanonicalFile(PreferenceUtil.getInstance(getActivity()).getStartDirectory())), true); - return true; - } - return super.onOptionsItemSelected(item); - } + public static final FileFilter AUDIO_FILE_FILTER = file -> !file.isHidden() && (file.isDirectory() || + FileUtil.fileIsMimeType(file, "audio/*", MimeTypeMap.getSingleton()) || + FileUtil.fileIsMimeType(file, "application/ogg", MimeTypeMap.getSingleton())); @Override public void onCrumbSelection(BreadCrumbLayout.Crumb crumb, int index) { @@ -292,18 +283,33 @@ public static File getDefaultStartDirectory() { return startFolder; } + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + switch (item.getItemId()) { + case R.id.action_go_to_start_directory: + setCrumb(new BreadCrumbLayout.Crumb(FileUtil.safeGetCanonicalFile(PreferenceUtil.getInstance(getActivity()).getStartDirectory())), true); + return true; + case R.id.action_scan: + BreadCrumbLayout.Crumb crumb = getActiveCrumb(); + if (crumb != null) { + new ArrayListPathsAsyncTask(getActivity(), this::scanPaths).execute(new ArrayListPathsAsyncTask.LoadingInfo(crumb.getFile(), AUDIO_FILE_FILTER)); + } + return true; + } + return super.onOptionsItemSelected(item); + } + @Override public void onFileSelected(File file) { - file = tryGetCanonicalFile(file); // important as we compare the path value later - if (file.isDirectory()) { - setCrumb(new BreadCrumbLayout.Crumb(file), true); + final File canonicalFile = FileUtil.safeGetCanonicalFile(file); // important as we compare the path value later + if (canonicalFile.isDirectory()) { + setCrumb(new BreadCrumbLayout.Crumb(canonicalFile), true); } else { - FileFilter fileFilter = pathname -> !pathname.isDirectory() && getFileFilter().accept(pathname); - new ListSongsAsyncTask(getActivity(), file, (songs, extra) -> { - File file1 = (File) extra; + FileFilter fileFilter = pathname -> !pathname.isDirectory() && AUDIO_FILE_FILTER.accept(pathname); + new ListSongsAsyncTask(getActivity(), null, (songs, extra) -> { int startIndex = -1; for (int i = 0; i < songs.size(); i++) { - if (file1.getPath().equals(songs.get(i).data)) { // path is already canonical here + if (canonicalFile.getPath().equals(songs.get(i).data)) { startIndex = i; break; } @@ -311,20 +317,56 @@ public void onFileSelected(File file) { if (startIndex > -1) { MusicPlayerRemote.openQueue(songs, startIndex, true); } else { - final File finalFile = file1; - Snackbar.make(coordinatorLayout, Html.fromHtml(String.format(getString(R.string.not_listed_in_media_store), file1.getName())), Snackbar.LENGTH_LONG) - .setAction(R.string.action_scan, v -> new ListPathsAsyncTask(getActivity(), paths -> scanPaths(paths)).execute(new ListPathsAsyncTask.LoadingInfo(finalFile, getFileFilter()))) + Snackbar.make(coordinatorLayout, Html.fromHtml(String.format(getString(R.string.not_listed_in_media_store), canonicalFile.getName())), Snackbar.LENGTH_LONG) + .setAction(R.string.action_scan, v -> scanPaths(new String[]{canonicalFile.getPath()})) .setActionTextColor(ThemeStore.accentColor(getActivity())) .show(); } - }).execute(new ListSongsAsyncTask.LoadingInfo(toList(file.getParentFile()), fileFilter, getFileComparator())); + }).execute(new ListSongsAsyncTask.LoadingInfo(toList(canonicalFile.getParentFile()), fileFilter, getFileComparator())); } } @Override - public void onMultipleItemAction(MenuItem item, ArrayList files) { + public void onMultipleItemAction(MenuItem item, List files) { final int itemId = item.getItemId(); - new ListSongsAsyncTask(getActivity(), null, (songs, extra) -> SongsMenuHelper.handleMenuClick(getActivity(), songs, itemId)).execute(new ListSongsAsyncTask.LoadingInfo(files, getFileFilter(), getFileComparator())); + new ListSongsAsyncTask(getActivity(), null, (songs, extra) -> { + if (!songs.isEmpty()) { + SongsMenuHelper.handleMenuClick(getActivity(), songs, itemId); + } + if (songs.size() != files.size()) { + Snackbar.make(coordinatorLayout, R.string.some_files_are_not_listed_in_the_media_store, Snackbar.LENGTH_LONG) + .setAction(R.string.action_scan, v -> { + String[] paths = new String[files.size()]; + for (int i = 0; i < files.size(); i++) { + paths[i] = FileUtil.safeGetCanonicalPath(files.get(i)); + } + scanPaths(paths); + }) + .setActionTextColor(ThemeStore.accentColor(getActivity())) + .show(); + } + }).execute(new ListSongsAsyncTask.LoadingInfo(files, AUDIO_FILE_FILTER, getFileComparator())); + } + + private List toList(File file) { + List files = new ArrayList<>(1); + files.add(file); + return files; + } + + Comparator fileComparator = (lhs, rhs) -> { + if (lhs.isDirectory() && !rhs.isDirectory()) { + return -1; + } else if (!lhs.isDirectory() && rhs.isDirectory()) { + return 1; + } else { + return lhs.getName().compareToIgnoreCase + (rhs.getName()); + } + }; + + private Comparator getFileComparator() { + return fileComparator; } @Override @@ -339,14 +381,18 @@ public void onFileMenuClicked(final File file, View view) { case R.id.action_add_to_current_playing: case R.id.action_add_to_playlist: case R.id.action_delete_from_device: - new ListSongsAsyncTask(getActivity(), null, (songs, extra) -> SongsMenuHelper.handleMenuClick(getActivity(), songs, itemId)).execute(new ListSongsAsyncTask.LoadingInfo(toList(file), getFileFilter(), getFileComparator())); + new ListSongsAsyncTask(getActivity(), null, (songs, extra) -> { + if (!songs.isEmpty()) { + SongsMenuHelper.handleMenuClick(getActivity(), songs, itemId); + } + }).execute(new ListSongsAsyncTask.LoadingInfo(toList(file), AUDIO_FILE_FILTER, getFileComparator())); return true; case R.id.action_set_as_start_directory: PreferenceUtil.getInstance(getActivity()).setStartDirectory(file); Toast.makeText(getActivity(), String.format(getString(R.string.new_start_directory), file.getPath()), Toast.LENGTH_SHORT).show(); return true; case R.id.action_scan: - new ListPathsAsyncTask(getActivity(), paths -> scanPaths(paths)).execute(new ListPathsAsyncTask.LoadingInfo(file, getFileFilter())); + new ArrayListPathsAsyncTask(getActivity(), this::scanPaths).execute(new ArrayListPathsAsyncTask.LoadingInfo(file, AUDIO_FILE_FILTER)); return true; } return false; @@ -366,10 +412,19 @@ public void onFileMenuClicked(final File file, View view) { case R.id.action_details: case R.id.action_set_as_ringtone: case R.id.action_delete_from_device: - new ListSongsAsyncTask(getActivity(), null, (songs, extra) -> SongMenuHelper.handleMenuClick(getActivity(), songs.get(0), itemId)).execute(new ListSongsAsyncTask.LoadingInfo(toList(file), getFileFilter(), getFileComparator())); + new ListSongsAsyncTask(getActivity(), null, (songs, extra) -> { + if (!songs.isEmpty()) { + SongMenuHelper.handleMenuClick(getActivity(), songs.get(0), itemId); + } else { + Snackbar.make(coordinatorLayout, Html.fromHtml(String.format(getString(R.string.not_listed_in_media_store), file.getName())), Snackbar.LENGTH_LONG) + .setAction(R.string.action_scan, v -> scanPaths(new String[]{FileUtil.safeGetCanonicalPath(file)})) + .setActionTextColor(ThemeStore.accentColor(getActivity())) + .show(); + } + }).execute(new ListSongsAsyncTask.LoadingInfo(toList(file), AUDIO_FILE_FILTER, getFileComparator())); return true; case R.id.action_scan: - new ListPathsAsyncTask(getActivity(), paths -> scanPaths(paths)).execute(new ListPathsAsyncTask.LoadingInfo(file, getFileFilter())); + scanPaths(new String[]{FileUtil.safeGetCanonicalPath(file)}); return true; } return false; @@ -378,35 +433,6 @@ public void onFileMenuClicked(final File file, View view) { popupMenu.show(); } - private ArrayList toList(File file) { - ArrayList files = new ArrayList<>(1); - files.add(file); - return files; - } - - Comparator fileComparator = (lhs, rhs) -> { - if (lhs.isDirectory() && !rhs.isDirectory()) { - return -1; - } else if (!lhs.isDirectory() && rhs.isDirectory()) { - return 1; - } else { - return lhs.getName().compareToIgnoreCase - (rhs.getName()); - } - }; - - private Comparator getFileComparator() { - return fileComparator; - } - - FileFilter audioFileFilter = file -> !file.isHidden() && (file.isDirectory() || - FileUtil.fileIsMimeType(file, "audio/*", MimeTypeMap.getSingleton()) || - FileUtil.fileIsMimeType(file, "application/ogg", MimeTypeMap.getSingleton())); - - private FileFilter getFileFilter() { - return audioFileFilter; - } - @Override public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) { container.setPadding(container.getPaddingLeft(), container.getPaddingTop(), container.getPaddingRight(), appbar.getTotalScrollRange() + verticalOffset); @@ -418,15 +444,6 @@ private void checkIsEmpty() { } } - private static File tryGetCanonicalFile(File file) { - try { - return file.getCanonicalFile(); - } catch (IOException e) { - e.printStackTrace(); - return file; - } - } - private void scanPaths(@Nullable String[] toBeScanned) { if (getActivity() == null) return; if (toBeScanned == null || toBeScanned.length < 1) { @@ -450,13 +467,13 @@ public Loader> onCreateLoader(int id, Bundle args) { } @Override - public void onLoadFinished(Loader> loader, List data) { + public void onLoadFinished(@NonNull Loader> loader, List data) { updateAdapter(data); } @Override - public void onLoaderReset(Loader> loader) { - updateAdapter(new LinkedList()); + public void onLoaderReset(@NonNull Loader> loader) { + updateAdapter(new LinkedList<>()); } private static class AsyncFileLoader extends WrappedAsyncTaskLoader> { @@ -478,7 +495,7 @@ public List loadInBackground() { } } if (directory != null) { - List files = FileUtil.listFiles(directory, foldersFragment.getFileFilter()); + List files = FileUtil.listFiles(directory, AUDIO_FILE_FILTER); Collections.sort(files, foldersFragment.getFileComparator()); return files; } else { @@ -487,13 +504,13 @@ public List loadInBackground() { } } - private static class ListSongsAsyncTask extends ListingFilesDialogAsyncTask> { + private static class ListSongsAsyncTask extends ListingFilesDialogAsyncTask> { private WeakReference contextWeakReference; private WeakReference callbackWeakReference; private final Object extra; public ListSongsAsyncTask(Context context, Object extra, OnSongsListedCallback callback) { - super(context); + super(context, 500); this.extra = extra; contextWeakReference = new WeakReference<>(context); callbackWeakReference = new WeakReference<>(callback); @@ -507,7 +524,7 @@ protected void onPreExecute() { } @Override - protected ArrayList doInBackground(LoadingInfo... params) { + protected List doInBackground(LoadingInfo... params) { try { LoadingInfo info = params[0]; List files = FileUtil.listFilesDeep(info.files, info.fileFilter); @@ -530,10 +547,10 @@ protected ArrayList doInBackground(LoadingInfo... params) { } @Override - protected void onPostExecute(ArrayList songs) { + protected void onPostExecute(List songs) { super.onPostExecute(songs); OnSongsListedCallback callback = checkCallbackReference(); - if (songs != null && callback != null && !songs.isEmpty()) + if (songs != null && callback != null) callback.onSongsListed(songs, extra); } @@ -566,15 +583,15 @@ public LoadingInfo(@NonNull List files, @NonNull FileFilter fileFilter, @N } public interface OnSongsListedCallback { - void onSongsListed(@NonNull ArrayList songs, Object extra); + void onSongsListed(@NonNull List songs, Object extra); } } - private static class ListPathsAsyncTask extends ListingFilesDialogAsyncTask { + public static class ArrayListPathsAsyncTask extends ListingFilesDialogAsyncTask { private WeakReference onPathsListedCallbackWeakReference; - public ListPathsAsyncTask(Context context, OnPathsListedCallback callback) { - super(context); + public ArrayListPathsAsyncTask(Context context, OnPathsListedCallback callback) { + super(context, 500); onPathsListedCallbackWeakReference = new WeakReference<>(callback); } @@ -603,11 +620,11 @@ protected String[] doInBackground(LoadingInfo... params) { File f = files.get(i); paths[i] = FileUtil.safeGetCanonicalPath(f); - if (isCancelled() || checkCallbackReference() == null) return paths; + if (isCancelled() || checkCallbackReference() == null) return null; } } else { paths = new String[1]; - paths[0] = info.file.getPath(); + paths[0] = FileUtil.safeGetCanonicalPath(info.file); } return paths; @@ -622,7 +639,7 @@ protected String[] doInBackground(LoadingInfo... params) { protected void onPostExecute(String[] paths) { super.onPostExecute(paths); OnPathsListedCallback callback = checkCallbackReference(); - if (callback != null) { + if (callback != null && paths != null) { callback.onPathsListed(paths); } } @@ -646,7 +663,7 @@ public LoadingInfo(File file, FileFilter fileFilter) { } public interface OnPathsListedCallback { - void onPathsListed(@Nullable String[] paths); + void onPathsListed(@NonNull String[] paths); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/LibraryFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/LibraryFragment.java index 4ca977161..fa1e9e0f0 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/LibraryFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/LibraryFragment.java @@ -5,13 +5,13 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.AppBarLayout; -import android.support.design.widget.TabLayout; -import android.support.v4.app.Fragment; -import android.support.v4.view.ViewPager; -import android.support.v7.widget.Toolbar; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.material.appbar.AppBarLayout; +import com.google.android.material.tabs.TabLayout; +import androidx.fragment.app.Fragment; +import androidx.viewpager.widget.ViewPager; +import androidx.appcompat.widget.Toolbar; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -29,13 +29,17 @@ import com.kabouzeid.gramophone.adapter.MusicLibraryPagerAdapter; import com.kabouzeid.gramophone.dialogs.CreatePlaylistDialog; import com.kabouzeid.gramophone.helper.MusicPlayerRemote; +import com.kabouzeid.gramophone.helper.SortOrder; import com.kabouzeid.gramophone.interfaces.CabHolder; import com.kabouzeid.gramophone.loader.SongLoader; import com.kabouzeid.gramophone.ui.activities.MainActivity; import com.kabouzeid.gramophone.ui.activities.SearchActivity; import com.kabouzeid.gramophone.ui.fragments.mainactivity.AbsMainActivityFragment; import com.kabouzeid.gramophone.ui.fragments.mainactivity.library.pager.AbsLibraryPagerRecyclerViewCustomGridSizeFragment; +import com.kabouzeid.gramophone.ui.fragments.mainactivity.library.pager.AlbumsFragment; +import com.kabouzeid.gramophone.ui.fragments.mainactivity.library.pager.ArtistsFragment; import com.kabouzeid.gramophone.ui.fragments.mainactivity.library.pager.PlaylistsFragment; +import com.kabouzeid.gramophone.ui.fragments.mainactivity.library.pager.SongsFragment; import com.kabouzeid.gramophone.util.PhonographColorUtil; import com.kabouzeid.gramophone.util.PreferenceUtil; import com.kabouzeid.gramophone.util.Util; @@ -45,7 +49,6 @@ import butterknife.Unbinder; public class LibraryFragment extends AbsMainActivityFragment implements CabHolder, MainActivity.MainActivityFragmentCallbacks, ViewPager.OnPageChangeListener, SharedPreferences.OnSharedPreferenceChangeListener { - public static final String TAG = LibraryFragment.class.getSimpleName(); private Unbinder unbinder; @@ -86,7 +89,7 @@ public void onDestroyView() { @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { PreferenceUtil.getInstance(getActivity()).registerOnSharedPreferenceChangedListener(this); - setStatusbarColorAuto(view); + getMainActivity().setStatusbarColorAuto(); getMainActivity().setNavigationbarColorAuto(); getMainActivity().setTaskDescriptionColorAuto(); @@ -133,13 +136,13 @@ private void setUpViewPager() { tabs.setSelectedTabIndicatorColor(ThemeStore.accentColor(getActivity())); updateTabVisibility(); - + if (PreferenceUtil.getInstance(getContext()).rememberLastTab()) { pager.setCurrentItem(PreferenceUtil.getInstance(getContext()).getLastPage()); } pager.addOnPageChangeListener(this); } - + private void updateTabVisibility() { // hide the tab bar when only a single tab is visible tabs.setVisibility(pagerAdapter.getCount() == 1 ? View.GONE : View.VISIBLE); @@ -197,9 +200,12 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.findItem(R.id.action_colored_footers).setChecked(absLibraryRecyclerViewCustomGridSizeFragment.usePalette()); menu.findItem(R.id.action_colored_footers).setEnabled(absLibraryRecyclerViewCustomGridSizeFragment.canUsePalette()); + + setUpSortOrderMenu(absLibraryRecyclerViewCustomGridSizeFragment, menu.findItem(R.id.action_sort_order).getSubMenu()); } else { menu.removeItem(R.id.action_grid_size); menu.removeItem(R.id.action_colored_footers); + menu.removeItem(R.id.action_sort_order); } Activity activity = getActivity(); if (activity == null) return; @@ -228,6 +234,9 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) { if (handleGridSizeMenuItem(absLibraryRecyclerViewCustomGridSizeFragment, item)) { return true; } + if (handleSortOrderMenuItem(absLibraryRecyclerViewCustomGridSizeFragment, item)) { + return true; + } } int id = item.getItemId(); @@ -330,6 +339,95 @@ private boolean handleGridSizeMenuItem(@NonNull AbsLibraryPagerRecyclerViewCusto return false; } + private void setUpSortOrderMenu(@NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment, @NonNull SubMenu sortOrderMenu) { + String currentSortOrder = fragment.getSortOrder(); + sortOrderMenu.clear(); + + if (fragment instanceof AlbumsFragment) { + sortOrderMenu.add(0, R.id.action_album_sort_order_asc, 0, R.string.sort_order_a_z) + .setChecked(currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_A_Z)); + sortOrderMenu.add(0, R.id.action_album_sort_order_desc, 1, R.string.sort_order_z_a) + .setChecked(currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_Z_A)); + sortOrderMenu.add(0, R.id.action_album_sort_order_artist, 2, R.string.sort_order_artist) + .setChecked(currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_ARTIST)); + sortOrderMenu.add(0, R.id.action_album_sort_order_year, 3, R.string.sort_order_year) + .setChecked(currentSortOrder.equals(SortOrder.AlbumSortOrder.ALBUM_YEAR)); + } else if (fragment instanceof ArtistsFragment) { + sortOrderMenu.add(0, R.id.action_artist_sort_order_asc, 0, R.string.sort_order_a_z) + .setChecked(currentSortOrder.equals(SortOrder.ArtistSortOrder.ARTIST_A_Z)); + sortOrderMenu.add(0, R.id.action_artist_sort_order_desc, 1, R.string.sort_order_z_a) + .setChecked(currentSortOrder.equals(SortOrder.ArtistSortOrder.ARTIST_Z_A)); + } else if (fragment instanceof SongsFragment) { + sortOrderMenu.add(0, R.id.action_song_sort_order_asc, 0, R.string.sort_order_a_z) + .setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_A_Z)); + sortOrderMenu.add(0, R.id.action_song_sort_order_desc, 1, R.string.sort_order_z_a) + .setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_Z_A)); + sortOrderMenu.add(0, R.id.action_song_sort_order_artist, 2, R.string.sort_order_artist) + .setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_ARTIST)); + sortOrderMenu.add(0, R.id.action_song_sort_order_album, 3, R.string.sort_order_album) + .setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_ALBUM)); + sortOrderMenu.add(0, R.id.action_song_sort_order_year, 4, R.string.sort_order_year) + .setChecked(currentSortOrder.equals(SortOrder.SongSortOrder.SONG_YEAR)); + } + + sortOrderMenu.setGroupCheckable(0, true, true); + } + + private boolean handleSortOrderMenuItem(@NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment, @NonNull MenuItem item) { + String sortOrder = null; + if (fragment instanceof AlbumsFragment) { + switch (item.getItemId()) { + case R.id.action_album_sort_order_asc: + sortOrder = SortOrder.AlbumSortOrder.ALBUM_A_Z; + break; + case R.id.action_album_sort_order_desc: + sortOrder = SortOrder.AlbumSortOrder.ALBUM_Z_A; + break; + case R.id.action_album_sort_order_artist: + sortOrder = SortOrder.AlbumSortOrder.ALBUM_ARTIST; + break; + case R.id.action_album_sort_order_year: + sortOrder = SortOrder.AlbumSortOrder.ALBUM_YEAR; + break; + } + } else if (fragment instanceof ArtistsFragment) { + switch (item.getItemId()) { + case R.id.action_artist_sort_order_asc: + sortOrder = SortOrder.ArtistSortOrder.ARTIST_A_Z; + break; + case R.id.action_artist_sort_order_desc: + sortOrder = SortOrder.ArtistSortOrder.ARTIST_Z_A; + break; + } + } else if (fragment instanceof SongsFragment) { + switch (item.getItemId()) { + case R.id.action_song_sort_order_asc: + sortOrder = SortOrder.SongSortOrder.SONG_A_Z; + break; + case R.id.action_song_sort_order_desc: + sortOrder = SortOrder.SongSortOrder.SONG_Z_A; + break; + case R.id.action_song_sort_order_artist: + sortOrder = SortOrder.SongSortOrder.SONG_ARTIST; + break; + case R.id.action_song_sort_order_album: + sortOrder = SortOrder.SongSortOrder.SONG_ALBUM; + break; + case R.id.action_song_sort_order_year: + sortOrder = SortOrder.SongSortOrder.SONG_YEAR; + break; + } + } + + if (sortOrder != null) { + item.setChecked(true); + fragment.setAndSaveSortOrder(sortOrder); + return true; + } + + return false; + } + @Override public boolean handleBackPress() { if (cab != null && cab.isActive()) { diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AbsLibraryPagerFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AbsLibraryPagerFragment.java index c52c0247c..fc6b51d99 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AbsLibraryPagerFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AbsLibraryPagerFragment.java @@ -1,7 +1,7 @@ package com.kabouzeid.gramophone.ui.fragments.mainactivity.library.pager; import android.os.Bundle; -import android.support.v4.app.LoaderManager; +import androidx.loader.app.LoaderManager; import com.kabouzeid.gramophone.ui.fragments.AbsMusicServiceFragment; import com.kabouzeid.gramophone.ui.fragments.mainactivity.library.LibraryFragment; diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AbsLibraryPagerRecyclerViewCustomGridSizeFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AbsLibraryPagerRecyclerViewCustomGridSizeFragment.java index 172617558..25927e4b3 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AbsLibraryPagerRecyclerViewCustomGridSizeFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AbsLibraryPagerRecyclerViewCustomGridSizeFragment.java @@ -1,9 +1,9 @@ package com.kabouzeid.gramophone.ui.fragments.mainactivity.library.pager; import android.os.Bundle; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; import android.view.View; import com.kabouzeid.gramophone.R; @@ -14,6 +14,7 @@ */ public abstract class AbsLibraryPagerRecyclerViewCustomGridSizeFragment extends AbsLibraryPagerRecyclerViewFragment { private int gridSize; + private String sortOrder; private boolean usePaletteInitialized; private boolean usePalette; @@ -49,6 +50,13 @@ public final boolean usePalette() { return usePalette; } + public final String getSortOrder() { + if (sortOrder == null) { + sortOrder = loadSortOrder(); + } + return sortOrder; + } + public void setAndSaveGridSize(final int gridSize) { int oldLayoutRes = getItemLayoutRes(); this.gridSize = gridSize; @@ -72,6 +80,12 @@ public void setAndSaveUsePalette(final boolean usePalette) { setUsePalette(usePalette); } + public void setAndSaveSortOrder(final String sortOrder) { + this.sortOrder = sortOrder; + saveSortOrder(sortOrder); + setSortOrder(sortOrder); + } + /** * @return whether the palette option should be available for the current item layout or not */ @@ -132,6 +146,12 @@ protected void applyRecyclerViewPaddingForLayoutRes(@NonNull RecyclerView recycl protected abstract void setGridSize(int gridSize); + protected abstract String loadSortOrder(); + + protected abstract void saveSortOrder(String sortOrder); + + protected abstract void setSortOrder(String sortOrder); + protected int getMaxGridSizeForList() { if (isLandscape()) { return getActivity().getResources().getInteger(R.integer.default_list_columns_land); diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AbsLibraryPagerRecyclerViewFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AbsLibraryPagerRecyclerViewFragment.java index 2b441d59a..bd08cabdc 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AbsLibraryPagerRecyclerViewFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AbsLibraryPagerRecyclerViewFragment.java @@ -1,13 +1,13 @@ package com.kabouzeid.gramophone.ui.fragments.mainactivity.library.pager; import android.os.Bundle; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.StringRes; -import android.support.design.widget.AppBarLayout; -import android.support.design.widget.AppBarLayout.OnOffsetChangedListener; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import com.google.android.material.appbar.AppBarLayout; +import com.google.android.material.appbar.AppBarLayout.OnOffsetChangedListener; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -27,8 +27,6 @@ */ public abstract class AbsLibraryPagerRecyclerViewFragment extends AbsLibraryPagerFragment implements OnOffsetChangedListener { - public static final String TAG = AbsLibraryPagerRecyclerViewFragment.class.getSimpleName(); - private Unbinder unbinder; @BindView(R.id.container) diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AlbumsFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AlbumsFragment.java index 0837e08db..ad8a75958 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AlbumsFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/AlbumsFragment.java @@ -2,10 +2,10 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.Loader; -import android.support.v7.widget.GridLayoutManager; +import androidx.annotation.NonNull; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.recyclerview.widget.GridLayoutManager; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.adapter.album.AlbumAdapter; @@ -16,12 +16,12 @@ import com.kabouzeid.gramophone.util.PreferenceUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ -public class AlbumsFragment extends AbsLibraryPagerRecyclerViewCustomGridSizeFragment implements LoaderManager.LoaderCallbacks> { - public static final String TAG = AlbumsFragment.class.getSimpleName(); +public class AlbumsFragment extends AbsLibraryPagerRecyclerViewCustomGridSizeFragment implements LoaderManager.LoaderCallbacks> { private static final int LOADER_ID = LoaderIds.ALBUMS_FRAGMENT; @@ -41,7 +41,7 @@ protected GridLayoutManager createLayoutManager() { protected AlbumAdapter createAdapter() { int itemLayoutRes = getItemLayoutRes(); notifyLayoutResChanged(itemLayoutRes); - ArrayList dataSet = getAdapter() == null ? new ArrayList() : getAdapter().getDataSet(); + List dataSet = getAdapter() == null ? new ArrayList<>() : getAdapter().getDataSet(); return new AlbumAdapter( getLibraryFragment().getMainActivity(), dataSet, @@ -55,6 +55,21 @@ protected int getEmptyMessage() { return R.string.no_albums; } + @Override + protected String loadSortOrder() { + return PreferenceUtil.getInstance(getActivity()).getAlbumSortOrder(); + } + + @Override + protected void saveSortOrder(String sortOrder) { + PreferenceUtil.getInstance(getActivity()).setAlbumSortOrder(sortOrder); + } + + @Override + protected void setSortOrder(String sortOrder) { + getLoaderManager().restartLoader(LOADER_ID, null, this); + } + @Override public boolean loadUsePalette() { return PreferenceUtil.getInstance(getActivity()).albumColoredFooters(); @@ -102,27 +117,27 @@ public void onMediaStoreChanged() { } @Override - public Loader> onCreateLoader(int id, Bundle args) { + public Loader> onCreateLoader(int id, Bundle args) { return new AsyncAlbumLoader(getActivity()); } @Override - public void onLoadFinished(Loader> loader, ArrayList data) { + public void onLoadFinished(Loader> loader, List data) { getAdapter().swapDataSet(data); } @Override - public void onLoaderReset(Loader> loader) { - getAdapter().swapDataSet(new ArrayList()); + public void onLoaderReset(Loader> loader) { + getAdapter().swapDataSet(new ArrayList<>()); } - private static class AsyncAlbumLoader extends WrappedAsyncTaskLoader> { + private static class AsyncAlbumLoader extends WrappedAsyncTaskLoader> { public AsyncAlbumLoader(Context context) { super(context); } @Override - public ArrayList loadInBackground() { + public List loadInBackground() { return AlbumLoader.getAllAlbums(getContext()); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/ArtistsFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/ArtistsFragment.java index b4dbc956a..8437cbf95 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/ArtistsFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/ArtistsFragment.java @@ -2,10 +2,10 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.Loader; -import android.support.v7.widget.GridLayoutManager; +import androidx.annotation.NonNull; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.recyclerview.widget.GridLayoutManager; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.adapter.artist.ArtistAdapter; @@ -16,13 +16,12 @@ import com.kabouzeid.gramophone.util.PreferenceUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ -public class ArtistsFragment extends AbsLibraryPagerRecyclerViewCustomGridSizeFragment implements LoaderManager.LoaderCallbacks> { - - public static final String TAG = ArtistsFragment.class.getSimpleName(); +public class ArtistsFragment extends AbsLibraryPagerRecyclerViewCustomGridSizeFragment implements LoaderManager.LoaderCallbacks> { private static final int LOADER_ID = LoaderIds.ARTISTS_FRAGMENT; @@ -43,7 +42,7 @@ protected GridLayoutManager createLayoutManager() { protected ArtistAdapter createAdapter() { int itemLayoutRes = getItemLayoutRes(); notifyLayoutResChanged(itemLayoutRes); - ArrayList dataSet = getAdapter() == null ? new ArrayList() : getAdapter().getDataSet(); + List dataSet = getAdapter() == null ? new ArrayList<>() : getAdapter().getDataSet(); return new ArtistAdapter( getLibraryFragment().getMainActivity(), dataSet, @@ -62,6 +61,21 @@ public void onMediaStoreChanged() { getLoaderManager().restartLoader(LOADER_ID, null, this); } + @Override + protected String loadSortOrder() { + return PreferenceUtil.getInstance(getActivity()).getArtistSortOrder(); + } + + @Override + protected void saveSortOrder(String sortOrder) { + PreferenceUtil.getInstance(getActivity()).setArtistSortOrder(sortOrder); + } + + @Override + protected void setSortOrder(String sortOrder) { + getLoaderManager().restartLoader(LOADER_ID, null, this); + } + @Override protected int loadGridSize() { return PreferenceUtil.getInstance(getActivity()).getArtistGridSize(getActivity()); @@ -105,29 +119,29 @@ protected void setGridSize(int gridSize) { @Override - public Loader> onCreateLoader(int id, Bundle args) { + public Loader> onCreateLoader(int id, Bundle args) { return new AsyncArtistLoader(getActivity()); } @Override - public void onLoadFinished(Loader> loader, ArrayList data) { + public void onLoadFinished(Loader> loader, List data) { getAdapter().swapDataSet(data); } @Override - public void onLoaderReset(Loader> loader) { - getAdapter().swapDataSet(new ArrayList()); + public void onLoaderReset(Loader> loader) { + getAdapter().swapDataSet(new ArrayList<>()); } - private static class AsyncArtistLoader extends WrappedAsyncTaskLoader> { + private static class AsyncArtistLoader extends WrappedAsyncTaskLoader> { public AsyncArtistLoader(Context context) { super(context); } @Override - public ArrayList loadInBackground() { + public List loadInBackground() { return ArtistLoader.getAllArtists(getContext()); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/GenresFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/GenresFragment.java index 60ada36d6..bedfb4180 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/GenresFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/GenresFragment.java @@ -2,10 +2,10 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.Loader; -import android.support.v7.widget.LinearLayoutManager; +import androidx.annotation.NonNull; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.recyclerview.widget.LinearLayoutManager; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.adapter.GenreAdapter; @@ -15,10 +15,9 @@ import com.kabouzeid.gramophone.model.Genre; import java.util.ArrayList; +import java.util.List; -public class GenresFragment extends AbsLibraryPagerRecyclerViewFragment implements LoaderManager.LoaderCallbacks> { - - public static final String TAG = GenresFragment.class.getSimpleName(); +public class GenresFragment extends AbsLibraryPagerRecyclerViewFragment implements LoaderManager.LoaderCallbacks> { private static final int LOADER_ID = LoaderIds.GENRES_FRAGMENT; @@ -37,7 +36,7 @@ protected LinearLayoutManager createLayoutManager() { @NonNull @Override protected GenreAdapter createAdapter() { - ArrayList dataSet = getAdapter() == null ? new ArrayList() : getAdapter().getDataSet(); + List dataSet = getAdapter() == null ? new ArrayList<>() : getAdapter().getDataSet(); return new GenreAdapter(getLibraryFragment().getMainActivity(), dataSet, R.layout.item_list_no_image); } @@ -52,27 +51,28 @@ public void onMediaStoreChanged() { } @Override - public Loader> onCreateLoader(int id, Bundle args) { + @NonNull + public Loader> onCreateLoader(int id, Bundle args) { return new GenresFragment.AsyncGenreLoader(getActivity()); } @Override - public void onLoadFinished(Loader> loader, ArrayList data) { + public void onLoadFinished(@NonNull Loader> loader, List data) { getAdapter().swapDataSet(data); } @Override - public void onLoaderReset(Loader> loader) { - getAdapter().swapDataSet(new ArrayList()); + public void onLoaderReset(@NonNull Loader> loader) { + getAdapter().swapDataSet(new ArrayList<>()); } - private static class AsyncGenreLoader extends WrappedAsyncTaskLoader> { + private static class AsyncGenreLoader extends WrappedAsyncTaskLoader> { public AsyncGenreLoader(Context context) { super(context); } @Override - public ArrayList loadInBackground() { + public List loadInBackground() { return GenreLoader.getAllGenres(getContext()); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/PlaylistsFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/PlaylistsFragment.java index 359b956bd..b69f4add0 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/PlaylistsFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/PlaylistsFragment.java @@ -2,10 +2,11 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.Loader; -import android.support.v7.widget.LinearLayoutManager; + +import androidx.annotation.NonNull; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.recyclerview.widget.LinearLayoutManager; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.adapter.PlaylistAdapter; @@ -18,13 +19,12 @@ import com.kabouzeid.gramophone.model.smartplaylist.MyTopTracksPlaylist; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ -public class PlaylistsFragment extends AbsLibraryPagerRecyclerViewFragment implements LoaderManager.LoaderCallbacks> { - - public static final String TAG = PlaylistsFragment.class.getSimpleName(); +public class PlaylistsFragment extends AbsLibraryPagerRecyclerViewFragment implements LoaderManager.LoaderCallbacks> { private static final int LOADER_ID = LoaderIds.PLAYLISTS_FRAGMENT; @@ -43,7 +43,7 @@ protected LinearLayoutManager createLayoutManager() { @NonNull @Override protected PlaylistAdapter createAdapter() { - ArrayList dataSet = getAdapter() == null ? new ArrayList() : getAdapter().getDataSet(); + List dataSet = getAdapter() == null ? new ArrayList<>() : getAdapter().getDataSet(); return new PlaylistAdapter(getLibraryFragment().getMainActivity(), dataSet, R.layout.item_list_single_row, getLibraryFragment()); } @@ -58,27 +58,27 @@ public void onMediaStoreChanged() { } @Override - public Loader> onCreateLoader(int id, Bundle args) { + public Loader> onCreateLoader(int id, Bundle args) { return new AsyncPlaylistLoader(getActivity()); } @Override - public void onLoadFinished(Loader> loader, ArrayList data) { + public void onLoadFinished(Loader> loader, List data) { getAdapter().swapDataSet(data); } @Override - public void onLoaderReset(Loader> loader) { - getAdapter().swapDataSet(new ArrayList()); + public void onLoaderReset(Loader> loader) { + getAdapter().swapDataSet(new ArrayList<>()); } - private static class AsyncPlaylistLoader extends WrappedAsyncTaskLoader> { + private static class AsyncPlaylistLoader extends WrappedAsyncTaskLoader> { public AsyncPlaylistLoader(Context context) { super(context); } - private static ArrayList getAllPlaylists(Context context) { - ArrayList playlists = new ArrayList<>(); + private static List getAllPlaylists(Context context) { + List playlists = new ArrayList<>(); playlists.add(new LastAddedPlaylist(context)); playlists.add(new HistoryPlaylist(context)); @@ -90,8 +90,8 @@ private static ArrayList getAllPlaylists(Context context) { } @Override - public ArrayList loadInBackground() { + public List loadInBackground() { return getAllPlaylists(getContext()); } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/SongsFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/SongsFragment.java index 0b9e30231..8477b0474 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/SongsFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/mainactivity/library/pager/SongsFragment.java @@ -2,10 +2,10 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.Loader; -import android.support.v7.widget.GridLayoutManager; +import androidx.annotation.NonNull; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; +import androidx.recyclerview.widget.GridLayoutManager; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.adapter.song.ShuffleButtonSongAdapter; @@ -17,13 +17,12 @@ import com.kabouzeid.gramophone.util.PreferenceUtil; import java.util.ArrayList; +import java.util.List; /** * @author Karim Abou Zeid (kabouzeid) */ -public class SongsFragment extends AbsLibraryPagerRecyclerViewCustomGridSizeFragment implements LoaderManager.LoaderCallbacks> { - - public static final String TAG = SongsFragment.class.getSimpleName(); +public class SongsFragment extends AbsLibraryPagerRecyclerViewCustomGridSizeFragment implements LoaderManager.LoaderCallbacks> { private static final int LOADER_ID = LoaderIds.SONGS_FRAGMENT; @@ -45,7 +44,7 @@ protected SongAdapter createAdapter() { int itemLayoutRes = getItemLayoutRes(); notifyLayoutResChanged(itemLayoutRes); boolean usePalette = loadUsePalette(); - ArrayList dataSet = getAdapter() == null ? new ArrayList() : getAdapter().getDataSet(); + List dataSet = getAdapter() == null ? new ArrayList<>() : getAdapter().getDataSet(); if (getGridSize() <= getMaxGridSizeForList()) { return new ShuffleButtonSongAdapter( @@ -73,6 +72,21 @@ public void onMediaStoreChanged() { getLoaderManager().restartLoader(LOADER_ID, null, this); } + @Override + protected String loadSortOrder() { + return PreferenceUtil.getInstance(getActivity()).getSongSortOrder(); + } + + @Override + protected void saveSortOrder(String sortOrder) { + PreferenceUtil.getInstance(getActivity()).setSongSortOrder(sortOrder); + } + + @Override + protected void setSortOrder(String sortOrder) { + getLoaderManager().restartLoader(LOADER_ID, null, this); + } + @Override protected int loadGridSize() { return PreferenceUtil.getInstance(getActivity()).getSongGridSize(getActivity()); @@ -115,27 +129,27 @@ protected void setGridSize(int gridSize) { } @Override - public Loader> onCreateLoader(int id, Bundle args) { + public Loader> onCreateLoader(int id, Bundle args) { return new AsyncSongLoader(getActivity()); } @Override - public void onLoadFinished(Loader> loader, ArrayList data) { + public void onLoadFinished(Loader> loader, List data) { getAdapter().swapDataSet(data); } @Override - public void onLoaderReset(Loader> loader) { - getAdapter().swapDataSet(new ArrayList()); + public void onLoaderReset(Loader> loader) { + getAdapter().swapDataSet(new ArrayList<>()); } - private static class AsyncSongLoader extends WrappedAsyncTaskLoader> { + private static class AsyncSongLoader extends WrappedAsyncTaskLoader> { public AsyncSongLoader(Context context) { super(context); } @Override - public ArrayList loadInBackground() { + public List loadInBackground() { return SongLoader.getAllSongs(getContext()); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/AbsPlayerFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/AbsPlayerFragment.java index dd0c8b023..6de995d71 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/AbsPlayerFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/AbsPlayerFragment.java @@ -2,8 +2,8 @@ import android.content.Context; import android.content.Intent; -import android.support.annotation.Nullable; -import android.support.v7.widget.Toolbar; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.Toolbar; import android.view.MenuItem; import android.view.View; @@ -23,7 +23,6 @@ import com.kabouzeid.gramophone.util.NavigationUtil; public abstract class AbsPlayerFragment extends AbsMusicServiceFragment implements Toolbar.OnMenuItemClickListener, PaletteColorHolder { - public static final String TAG = AbsPlayerFragment.class.getSimpleName(); private Callbacks callbacks; private static boolean isToolbarShown = true; @@ -133,7 +132,12 @@ protected void checkToggleToolbar(@Nullable final View toolbar) { } protected String getUpNextAndQueueTime() { - return getResources().getString(R.string.up_next) + " • " + MusicUtil.getReadableDurationString(MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.getPosition())); + final long duration = MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.getPosition()); + + return MusicUtil.buildInfoString( + getResources().getString(R.string.up_next), + MusicUtil.getReadableDurationString(duration) + ); } public abstract void onShow(); diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/MiniPlayerFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/MiniPlayerFragment.java index ccfe700ea..e1468296f 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/MiniPlayerFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/MiniPlayerFragment.java @@ -4,7 +4,8 @@ import android.content.res.ColorStateList; import android.graphics.PorterDuff; import android.os.Bundle; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import android.view.GestureDetector; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -53,7 +54,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) { @Nullable @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_mini_player, container, false); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/NowPlayingScreen.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/NowPlayingScreen.java index 32f002470..196c25f7a 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/NowPlayingScreen.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/NowPlayingScreen.java @@ -1,7 +1,7 @@ package com.kabouzeid.gramophone.ui.fragments.player; -import android.support.annotation.DrawableRes; -import android.support.annotation.StringRes; +import androidx.annotation.DrawableRes; +import androidx.annotation.StringRes; import com.kabouzeid.gramophone.R; diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlayerAlbumCoverFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlayerAlbumCoverFragment.java index 92ff5792a..814483bd5 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlayerAlbumCoverFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlayerAlbumCoverFragment.java @@ -2,7 +2,8 @@ import android.animation.Animator; import android.os.Bundle; -import android.support.v4.view.ViewPager; +import androidx.annotation.NonNull; +import androidx.viewpager.widget.ViewPager; import android.view.GestureDetector; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -33,7 +34,6 @@ * @author Karim Abou Zeid (kabouzeid) */ public class PlayerAlbumCoverFragment extends AbsMusicServiceFragment implements ViewPager.OnPageChangeListener, MusicProgressViewUpdateHelper.Callback { - public static final String TAG = PlayerAlbumCoverFragment.class.getSimpleName(); public static final int VISIBILITY_ANIM_DURATION = 300; @@ -58,15 +58,14 @@ public class PlayerAlbumCoverFragment extends AbsMusicServiceFragment implements private MusicProgressViewUpdateHelper progressViewUpdateHelper; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_player_album_cover, container, false); unbinder = ButterKnife.bind(this, view); return view; } @Override - public void onViewCreated(View view, Bundle savedInstanceState) { + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); viewPager.addOnPageChangeListener(this); viewPager.setOnTouchListener(new View.OnTouchListener() { diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/card/CardPlayerFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/card/CardPlayerFragment.java index 71f827289..f7f6074e1 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/card/CardPlayerFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/card/CardPlayerFragment.java @@ -9,13 +9,14 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.support.annotation.ColorInt; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.CardView; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.Toolbar; +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.cardview.widget.CardView; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.appcompat.widget.Toolbar; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.Menu; @@ -48,6 +49,7 @@ import com.kabouzeid.gramophone.ui.activities.base.AbsSlidingMusicPanelActivity; import com.kabouzeid.gramophone.ui.fragments.player.AbsPlayerFragment; import com.kabouzeid.gramophone.ui.fragments.player.PlayerAlbumCoverFragment; +import com.kabouzeid.gramophone.util.ImageUtil; import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.Util; import com.kabouzeid.gramophone.util.ViewUtil; @@ -59,7 +61,6 @@ import butterknife.Unbinder; public class CardPlayerFragment extends AbsPlayerFragment implements PlayerAlbumCoverFragment.Callbacks, SlidingUpPanelLayout.PanelSlideListener { - public static final String TAG = CardPlayerFragment.class.getSimpleName(); private Unbinder unbinder; @@ -99,7 +100,7 @@ public class CardPlayerFragment extends AbsPlayerFragment implements PlayerAlbum private Impl impl; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (Util.isLandscape(getResources())) { impl = new LandscapeImpl(this); } else { @@ -112,7 +113,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa } @Override - public void onViewCreated(final View view, Bundle savedInstanceState) { + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); impl.init(); @@ -293,7 +294,7 @@ protected void onPostExecute(Boolean isFavorite) { if (activity != null) { int res = isFavorite ? R.drawable.ic_favorite_white_24dp : R.drawable.ic_favorite_border_white_24dp; int color = ToolbarContentTintHelper.toolbarContentColor(activity, Color.TRANSPARENT); - Drawable drawable = Util.getTintedVectorDrawable(activity, res, color); + Drawable drawable = ImageUtil.getTintedVectorDrawable(activity, res, color); toolbar.getMenu().findItem(R.id.action_toggle_favorite) .setIcon(drawable) .setTitle(isFavorite ? getString(R.string.action_remove_from_favorites) : getString(R.string.action_add_to_favorites)); @@ -336,11 +337,11 @@ protected void onPostExecute(Lyrics l) { if (toolbar != null && activity != null) if (toolbar.getMenu().findItem(R.id.action_show_lyrics) == null) { int color = ToolbarContentTintHelper.toolbarContentColor(activity, Color.TRANSPARENT); - Drawable drawable = Util.getTintedVectorDrawable(activity, R.drawable.ic_comment_text_outline_white_24dp, color); + Drawable drawable = ImageUtil.getTintedVectorDrawable(activity, R.drawable.ic_comment_text_outline_white_24dp, color); toolbar.getMenu() .add(Menu.NONE, R.id.action_show_lyrics, Menu.NONE, R.string.action_show_lyrics) .setIcon(drawable) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); } } } @@ -463,10 +464,9 @@ public BaseImpl(CardPlayerFragment fragment) { public AnimatorSet createDefaultColorChangeAnimatorSet(int newColor) { Animator backgroundAnimator; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - int topMargin = fragment.getResources().getDimensionPixelSize(R.dimen.status_bar_padding); //noinspection ConstantConditions int x = (int) (fragment.playbackControlsFragment.playPauseFab.getX() + fragment.playbackControlsFragment.playPauseFab.getWidth() / 2 + fragment.playbackControlsFragment.getView().getX()); - int y = (int) (topMargin + fragment.playbackControlsFragment.playPauseFab.getY() + fragment.playbackControlsFragment.playPauseFab.getHeight() / 2 + fragment.playbackControlsFragment.getView().getY()); + int y = (int) (fragment.playbackControlsFragment.playPauseFab.getY() + fragment.playbackControlsFragment.playPauseFab.getHeight() / 2 + fragment.playbackControlsFragment.getView().getY() + fragment.playbackControlsFragment.progressSlider.getHeight()); float startRadius = Math.max(fragment.playbackControlsFragment.playPauseFab.getWidth() / 2, fragment.playbackControlsFragment.playPauseFab.getHeight() / 2); float endRadius = Math.max(fragment.colorBackground.getWidth(), fragment.colorBackground.getHeight()); fragment.colorBackground.setBackgroundColor(newColor); @@ -552,10 +552,9 @@ public boolean onMenuItemClick(MenuItem item) { @Override public void setUpPanelAndAlbumCoverHeight() { WidthFitSquareLayout albumCoverContainer = fragment.getView().findViewById(R.id.album_cover_container); - int topMargin = fragment.getResources().getDimensionPixelSize(R.dimen.status_bar_padding); - final int availablePanelHeight = fragment.slidingUpPanelLayout.getHeight() - fragment.getView().findViewById(R.id.player_content).getHeight() + topMargin; - final int minPanelHeight = (int) ViewUtil.convertDpToPixel(72 + 24, fragment.getResources()) + topMargin; + final int availablePanelHeight = fragment.slidingUpPanelLayout.getHeight() - fragment.getView().findViewById(R.id.player_content).getHeight() + (int) ViewUtil.convertDpToPixel(8, fragment.getResources()); + final int minPanelHeight = (int) ViewUtil.convertDpToPixel(72 + 24, fragment.getResources()); if (availablePanelHeight < minPanelHeight) { albumCoverContainer.getLayoutParams().height = albumCoverContainer.getHeight() - (minPanelHeight - availablePanelHeight); albumCoverContainer.forceSquare(false); @@ -569,7 +568,7 @@ public void setUpPanelAndAlbumCoverHeight() { public void updateCurrentSong(Song song) { currentSong = song; currentSongViewHolder.title.setText(song.title); - currentSongViewHolder.text.setText(song.artistName); + currentSongViewHolder.text.setText(MusicUtil.getSongInfoString(song)); } @Override @@ -595,8 +594,7 @@ public void init() { @Override public void setUpPanelAndAlbumCoverHeight() { - int topMargin = fragment.getResources().getDimensionPixelSize(R.dimen.status_bar_padding); - int panelHeight = fragment.slidingUpPanelLayout.getHeight() - fragment.playbackControlsFragment.getView().getHeight() + topMargin; + int panelHeight = fragment.slidingUpPanelLayout.getHeight() - fragment.playbackControlsFragment.getView().getHeight(); fragment.slidingUpPanelLayout.setPanelHeight(panelHeight); ((AbsSlidingMusicPanelActivity) fragment.getActivity()).setAntiDragView(fragment.slidingUpPanelLayout.findViewById(R.id.player_panel)); @@ -605,7 +603,7 @@ public void setUpPanelAndAlbumCoverHeight() { @Override public void updateCurrentSong(Song song) { fragment.toolbar.setTitle(song.title); - fragment.toolbar.setSubtitle(song.artistName); + fragment.toolbar.setSubtitle(MusicUtil.getSongInfoString(song)); } @Override @@ -620,4 +618,4 @@ public void animateColorChange(int newColor) { animatorSet.start(); } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/card/CardPlayerPlaybackControlsFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/card/CardPlayerPlaybackControlsFragment.java index 44b66859c..d0cdc1e72 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/card/CardPlayerPlaybackControlsFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/card/CardPlayerPlaybackControlsFragment.java @@ -3,7 +3,8 @@ import android.graphics.Color; import android.graphics.PorterDuff; import android.os.Bundle; -import android.support.design.widget.FloatingActionButton; +import androidx.annotation.NonNull; +import com.google.android.material.floatingactionbutton.FloatingActionButton; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -68,8 +69,7 @@ public void onCreate(Bundle savedInstanceState) { } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_card_player_playback_controls, container, false); } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/flat/FlatPlayerFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/flat/FlatPlayerFragment.java index 4b5de378a..948d043cf 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/flat/FlatPlayerFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/flat/FlatPlayerFragment.java @@ -8,12 +8,13 @@ import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Bundle; -import android.support.annotation.ColorInt; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.Toolbar; +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.appcompat.widget.Toolbar; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.Menu; @@ -45,6 +46,7 @@ import com.kabouzeid.gramophone.ui.activities.base.AbsSlidingMusicPanelActivity; import com.kabouzeid.gramophone.ui.fragments.player.AbsPlayerFragment; import com.kabouzeid.gramophone.ui.fragments.player.PlayerAlbumCoverFragment; +import com.kabouzeid.gramophone.util.ImageUtil; import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.Util; import com.kabouzeid.gramophone.util.ViewUtil; @@ -56,7 +58,6 @@ import butterknife.Unbinder; public class FlatPlayerFragment extends AbsPlayerFragment implements PlayerAlbumCoverFragment.Callbacks, SlidingUpPanelLayout.PanelSlideListener { - public static final String TAG = FlatPlayerFragment.class.getSimpleName(); private Unbinder unbinder; @@ -95,7 +96,7 @@ public class FlatPlayerFragment extends AbsPlayerFragment implements PlayerAlbum private Impl impl; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (Util.isLandscape(getResources())) { impl = new LandscapeImpl(this); } else { @@ -108,7 +109,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa } @Override - public void onViewCreated(final View view, Bundle savedInstanceState) { + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); impl.init(); @@ -288,7 +289,7 @@ protected void onPostExecute(Boolean isFavorite) { if (activity != null) { int res = isFavorite ? R.drawable.ic_favorite_white_24dp : R.drawable.ic_favorite_border_white_24dp; int color = ToolbarContentTintHelper.toolbarContentColor(activity, Color.TRANSPARENT); - Drawable drawable = Util.getTintedVectorDrawable(activity, res, color); + Drawable drawable = ImageUtil.getTintedVectorDrawable(activity, res, color); toolbar.getMenu().findItem(R.id.action_toggle_favorite) .setIcon(drawable) .setTitle(isFavorite ? getString(R.string.action_remove_from_favorites) : getString(R.string.action_add_to_favorites)); @@ -331,11 +332,11 @@ protected void onPostExecute(Lyrics l) { if (toolbar != null && activity != null) if (toolbar.getMenu().findItem(R.id.action_show_lyrics) == null) { int color = ToolbarContentTintHelper.toolbarContentColor(activity, Color.TRANSPARENT); - Drawable drawable = Util.getTintedVectorDrawable(activity, R.drawable.ic_comment_text_outline_white_24dp, color); + Drawable drawable = ImageUtil.getTintedVectorDrawable(activity, R.drawable.ic_comment_text_outline_white_24dp, color); toolbar.getMenu() .add(Menu.NONE, R.id.action_show_lyrics, Menu.NONE, R.string.action_show_lyrics) .setIcon(drawable) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); } } } @@ -547,7 +548,7 @@ public void setUpPanelAndAlbumCoverHeight() { public void updateCurrentSong(Song song) { currentSong = song; currentSongViewHolder.title.setText(song.title); - currentSongViewHolder.text.setText(song.artistName); + currentSongViewHolder.text.setText(MusicUtil.getSongInfoString(song)); } @Override @@ -575,7 +576,7 @@ public void setUpPanelAndAlbumCoverHeight() { @Override public void updateCurrentSong(Song song) { fragment.toolbar.setTitle(song.title); - fragment.toolbar.setSubtitle(song.artistName); + fragment.toolbar.setSubtitle(MusicUtil.getSongInfoString(song)); } @Override @@ -587,4 +588,4 @@ public void animateColorChange(int newColor) { animatorSet.start(); } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/flat/FlatPlayerPlaybackControlsFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/flat/FlatPlayerPlaybackControlsFragment.java index c28d8fa5b..b24ef929b 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/flat/FlatPlayerPlaybackControlsFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/flat/FlatPlayerPlaybackControlsFragment.java @@ -7,7 +7,8 @@ import android.graphics.Color; import android.graphics.PorterDuff; import android.os.Bundle; -import android.support.v4.view.animation.FastOutSlowInInterpolator; +import androidx.annotation.NonNull; +import androidx.interpolator.view.animation.FastOutSlowInInterpolator; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -76,13 +77,12 @@ public void onCreate(Bundle savedInstanceState) { } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_flat_player_playback_controls, container, false); } @Override - public void onViewCreated(View view, Bundle savedInstanceState) { + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); unbinder = ButterKnife.bind(this, view); setUpMusicControllers(); diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/ArtistSignatureUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/ArtistSignatureUtil.java index cef28810a..25269a69f 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/ArtistSignatureUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/ArtistSignatureUtil.java @@ -3,7 +3,7 @@ import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.bumptech.glide.signature.StringSignature; diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/CalendarUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/CalendarUtil.java index 0cd4751a3..e951a2365 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/CalendarUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/CalendarUtil.java @@ -29,6 +29,18 @@ public long getElapsedToday() { } /** + * Returns the time elapsed so far last N days in milliseconds. + * + * @return Time elapsed since N days in milliseconds. + */ + public long getElapsedDays(int numDays) { + long elapsed = getElapsedToday(); + elapsed += numDays * MS_PER_DAY; + + return elapsed; + } + + /** * Returns the time elapsed so far this week in milliseconds. * * @return Time elapsed this week in milliseconds. diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/CustomArtistImageUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/CustomArtistImageUtil.java index d46cfbc55..6e640ff7a 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/CustomArtistImageUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/CustomArtistImageUtil.java @@ -7,7 +7,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.widget.Toast; import com.bumptech.glide.Glide; @@ -122,7 +122,12 @@ public boolean hasCustomArtistImage(Artist artist) { } private static String getFileName(Artist artist) { - return String.format(Locale.US, "#%d#%s.jpeg", artist.getId(), artist.getName()); + String artistName = artist.getName(); + if (artistName == null) + artistName = ""; + // replace everything that is not a letter or a number with _ + artistName = artistName.replaceAll("[^a-zA-Z0-9]", "_"); + return String.format(Locale.US, "#%d#%s.jpeg", artist.getId(), artistName); } public static File getFile(Artist artist) { diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/FileUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/FileUtil.java index 0648b629c..7661a58ea 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/FileUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/FileUtil.java @@ -3,8 +3,8 @@ import android.content.Context; import android.database.Cursor; import android.provider.MediaStore; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import android.webkit.MimeTypeMap; import com.kabouzeid.gramophone.loader.SongLoader; @@ -32,7 +32,7 @@ private FileUtil() { } @NonNull - public static ArrayList matchFilesWithMediaStore(@NonNull Context context, @Nullable List files) { + public static List matchFilesWithMediaStore(@NonNull Context context, @Nullable List files) { return SongLoader.getSongs(makeSongCursor(context, files)); } @@ -153,11 +153,8 @@ public static boolean fileIsMimeType(File file, String mimeType, MimeTypeMap mim return false; } String fileTypeMainType = fileType.substring(0, fileTypeDelimiter); - if (fileTypeMainType.equals(mimeTypeMainType)) { - return true; - } + return fileTypeMainType.equals(mimeTypeMainType); } - return false; } public static String stripExtension(String str) { @@ -194,4 +191,13 @@ public static String safeGetCanonicalPath(File file) { return file.getAbsolutePath(); } } + + public static File safeGetCanonicalFile(File file) { + try { + return file.getCanonicalFile(); + } catch (IOException e) { + e.printStackTrace(); + return file.getAbsoluteFile(); + } + } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/ImageUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/ImageUtil.java index d3f32bffe..5fa9ea8f5 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/ImageUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/ImageUtil.java @@ -1,7 +1,23 @@ package com.kabouzeid.gramophone.util; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.Bitmap; -import android.support.annotation.NonNull; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.os.Build; +import androidx.annotation.AttrRes; +import androidx.annotation.ColorInt; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; + +import java.io.InputStream; + +import com.kabouzeid.appthemehelper.util.TintHelper; /** * @author Karim Abou Zeid (kabouzeid) @@ -58,4 +74,48 @@ public static Bitmap resizeBitmap(@NonNull Bitmap src, int maxForSmallerSize) { return Bitmap.createScaledBitmap(src, dstWidth, dstHeight, false); } + + public static Bitmap createBitmap(Drawable drawable) { + return createBitmap(drawable, 1f); + } + + public static Bitmap createBitmap(Drawable drawable, float sizeMultiplier) { + Bitmap bitmap = Bitmap.createBitmap((int) (drawable.getIntrinsicWidth() * sizeMultiplier), (int) (drawable.getIntrinsicHeight() * sizeMultiplier), Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(bitmap); + drawable.setBounds(0, 0, c.getWidth(), c.getHeight()); + drawable.draw(c); + return bitmap; + } + + public static Drawable getVectorDrawable(@NonNull Resources res, @DrawableRes int resId, @Nullable Resources.Theme theme) { + if (Build.VERSION.SDK_INT >= 21) { + return res.getDrawable(resId, theme); + } + return VectorDrawableCompat.create(res, resId, theme); + } + + public static Drawable getTintedVectorDrawable(@NonNull Resources res, @DrawableRes int resId, @Nullable Resources.Theme theme, @ColorInt int color) { + return TintHelper.createTintedDrawable(getVectorDrawable(res, resId, theme), color); + } + + public static Drawable getTintedVectorDrawable(@NonNull Context context, @DrawableRes int id, @ColorInt int color) { + return TintHelper.createTintedDrawable(getVectorDrawable(context.getResources(), id, context.getTheme()), color); + } + + public static Drawable getVectorDrawable(@NonNull Context context, @DrawableRes int id) { + return getVectorDrawable(context.getResources(), id, context.getTheme()); + } + + public static Drawable resolveDrawable(@NonNull Context context, @AttrRes int drawableAttr) { + TypedArray a = context.obtainStyledAttributes(new int[]{drawableAttr}); + Drawable drawable = a.getDrawable(0); + a.recycle(); + return drawable; + } + + public static Bitmap resize(InputStream stream, int scaledWidth, int scaledHeight) { + final Bitmap bitmap = BitmapFactory.decodeStream(stream); + return Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true); + + } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java index 869a33230..2b6aa4987 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java @@ -11,9 +11,9 @@ import android.provider.BaseColumns; import android.provider.MediaStore; import android.provider.Settings; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.content.FileProvider; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.FileProvider; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; @@ -22,6 +22,7 @@ import com.kabouzeid.gramophone.helper.MusicPlayerRemote; import com.kabouzeid.gramophone.loader.PlaylistLoader; import com.kabouzeid.gramophone.loader.SongLoader; +import com.kabouzeid.gramophone.model.Album; import com.kabouzeid.gramophone.model.Artist; import com.kabouzeid.gramophone.model.Genre; import com.kabouzeid.gramophone.model.Playlist; @@ -32,21 +33,19 @@ import org.jaudiotagger.tag.FieldKey; import java.io.File; -import java.io.FileFilter; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.regex.Pattern; /** * @author Karim Abou Zeid (kabouzeid) */ public class MusicUtil { - public static final String TAG = MusicUtil.class.getSimpleName(); public static Uri getMediaStoreAlbumCoverUri(int albumId) { - final Uri sArtworkUri = Uri - .parse("content://media/external/audio/albumart"); + final Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart"); return ContentUris.withAppendedId(sArtworkUri, albumId); } @@ -58,7 +57,6 @@ public static Uri getSongFileUri(int songId) { @NonNull public static Intent createShareSongFileIntent(@NonNull final Song song, Context context) { try { - return new Intent() .setAction(Intent.ACTION_SEND) .putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName(), new File(song.data))) @@ -72,81 +70,112 @@ public static Intent createShareSongFileIntent(@NonNull final Song song, Context } } - public static void setRingtone(@NonNull final Context context, final int id) { - final ContentResolver resolver = context.getContentResolver(); - final Uri uri = getSongFileUri(id); - try { - final ContentValues values = new ContentValues(2); - values.put(MediaStore.Audio.AudioColumns.IS_RINGTONE, "1"); - values.put(MediaStore.Audio.AudioColumns.IS_ALARM, "1"); - resolver.update(uri, values, null, null); - } catch (@NonNull final UnsupportedOperationException ignored) { - return; - } - try { - Cursor cursor = resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, - new String[]{MediaStore.MediaColumns.TITLE}, - BaseColumns._ID + "=?", - new String[]{String.valueOf(id)}, - null); - try { - if (cursor != null && cursor.getCount() == 1) { - cursor.moveToFirst(); - Settings.System.putString(resolver, Settings.System.RINGTONE, uri.toString()); - final String message = context.getString(R.string.x_has_been_set_as_ringtone, cursor.getString(0)); - Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - } catch (SecurityException ignored) { - } - } @NonNull public static String getArtistInfoString(@NonNull final Context context, @NonNull final Artist artist) { int albumCount = artist.getAlbumCount(); int songCount = artist.getSongCount(); - String albumString = albumCount == 1 ? context.getResources().getString(R.string.album) : context.getResources().getString(R.string.albums); - String songString = songCount == 1 ? context.getResources().getString(R.string.song) : context.getResources().getString(R.string.songs); - return albumCount + " " + albumString + " • " + songCount + " " + songString; + + return MusicUtil.buildInfoString( + MusicUtil.getAlbumCountString(context, albumCount), + MusicUtil.getSongCountString(context, songCount) + ); + } + + @NonNull + public static String getAlbumInfoString(@NonNull final Context context, @NonNull final Album album) { + int songCount = album.getSongCount(); + + return MusicUtil.buildInfoString( + album.getArtistName(), + MusicUtil.getSongCountString(context, songCount) + ); + } + + @NonNull + public static String getSongInfoString(@NonNull final Song song) { + return MusicUtil.buildInfoString( + song.artistName, + song.albumName + ); } @NonNull public static String getGenreInfoString(@NonNull final Context context, @NonNull final Genre genre) { int songCount = genre.songCount; - String songString = songCount == 1 ? context.getResources().getString(R.string.song) : context.getResources().getString(R.string.songs); - return songCount + " " + songString; + return MusicUtil.getSongCountString(context, songCount); } @NonNull public static String getPlaylistInfoString(@NonNull final Context context, @NonNull List songs) { - final int songCount = songs.size(); + final long duration = getTotalDuration(context, songs); + + return MusicUtil.buildInfoString( + MusicUtil.getSongCountString(context, songs.size()), + MusicUtil.getReadableDurationString(duration) + ); + } + + @NonNull + public static String getSongCountString(@NonNull final Context context, int songCount) { final String songString = songCount == 1 ? context.getResources().getString(R.string.song) : context.getResources().getString(R.string.songs); + return songCount + " " + songString; + } + @NonNull + public static String getAlbumCountString(@NonNull final Context context, int albumCount) { + final String albumString = albumCount == 1 ? context.getResources().getString(R.string.album) : context.getResources().getString(R.string.albums); + return albumCount + " " + albumString; + } + + @NonNull + public static String getYearString(int year) { + return year > 0 ? String.valueOf(year) : "-"; + } + + public static long getTotalDuration(@NonNull final Context context, @NonNull List songs) { long duration = 0; for (int i = 0; i < songs.size(); i++) { duration += songs.get(i).duration; } - - return songCount + " " + songString + " • " + MusicUtil.getReadableDurationString(duration); + return duration; } public static String getReadableDurationString(long songDurationMillis) { long minutes = (songDurationMillis / 1000) / 60; long seconds = (songDurationMillis / 1000) % 60; if (minutes < 60) { - return String.format("%01d:%02d", minutes, seconds); + return String.format(Locale.getDefault(), "%01d:%02d", minutes, seconds); } else { long hours = minutes / 60; minutes = minutes % 60; - return String.format("%d:%02d:%02d", hours, minutes, seconds); + return String.format(Locale.getDefault(), "%d:%02d:%02d", hours, minutes, seconds); } } + /** + * Build a concatenated string from the provided arguments + * The intended purpose is to show extra annotations + * to a music library item. + * Ex: for a given album --> buildInfoString(album.artist, album.songCount) + */ + @NonNull + public static String buildInfoString(@Nullable final String string1, @Nullable final String string2) + { + // Skip empty strings + if (TextUtils.isEmpty(string1)) { + //noinspection ConstantConditions + return TextUtils.isEmpty(string2) ? "" : string2; + } + if (TextUtils.isEmpty(string2)) { + //noinspection ConstantConditions + return TextUtils.isEmpty(string1) ? "" : string1; + } + + return string1 + " • " + string2; + } + //iTunes uses for example 1002 for track 2 CD1 or 3011 for track 11 CD3. //this method converts those values to normal tracknumbers public static int getFixedTrackNumber(int trackNumberToFix) { @@ -277,6 +306,7 @@ public static void toggleFavorite(@NonNull final Context context, @NonNull final public static boolean isArtistNameUnknown(@Nullable String artistName) { if (TextUtils.isEmpty(artistName)) return false; + if (artistName.equals(Artist.UNKNOWN_ARTIST_DISPLAY_NAME)) return true; artistName = artistName.trim().toLowerCase(); return artistName.equals("unknown") || artistName.equals(""); } @@ -314,7 +344,7 @@ public static String getLyrics(Song song) { String filename = Pattern.quote(FileUtil.stripExtension(file.getName())); String songtitle = Pattern.quote(song.title); - final ArrayList patterns = new ArrayList<>(); + final List patterns = new ArrayList<>(); patterns.add(Pattern.compile(String.format(format, filename), Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE)); patterns.add(Pattern.compile(String.format(format, songtitle), Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE)); diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/NavigationUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/NavigationUtil.java index 66950c72b..a6709e012 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/NavigationUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/NavigationUtil.java @@ -4,10 +4,10 @@ import android.content.ActivityNotFoundException; import android.content.Intent; import android.media.audiofx.AudioEffect; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.ActivityOptionsCompat; -import android.support.v4.util.Pair; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.ActivityOptionsCompat; +import androidx.core.util.Pair; import android.widget.Toast; import com.kabouzeid.gramophone.R; @@ -29,7 +29,11 @@ public static void goToArtist(@NonNull final Activity activity, final int artist intent.putExtra(ArtistDetailActivity.EXTRA_ARTIST_ID, artistId); //noinspection unchecked - activity.startActivity(intent, ActivityOptionsCompat.makeSceneTransitionAnimation(activity, sharedElements).toBundle()); + if (sharedElements != null && sharedElements.length > 0) { + activity.startActivity(intent, ActivityOptionsCompat.makeSceneTransitionAnimation(activity, sharedElements).toBundle()); + } else { + activity.startActivity(intent); + } } public static void goToAlbum(@NonNull final Activity activity, final int albumId, @Nullable Pair... sharedElements) { @@ -37,7 +41,11 @@ public static void goToAlbum(@NonNull final Activity activity, final int albumId intent.putExtra(AlbumDetailActivity.EXTRA_ALBUM_ID, albumId); //noinspection unchecked - activity.startActivity(intent, ActivityOptionsCompat.makeSceneTransitionAnimation(activity, sharedElements).toBundle()); + if (sharedElements != null && sharedElements.length > 0) { + activity.startActivity(intent, ActivityOptionsCompat.makeSceneTransitionAnimation(activity, sharedElements).toBundle()); + } else { + activity.startActivity(intent); + } } public static void goToGenre(@NonNull final Activity activity, final Genre genre, @Nullable Pair... sharedElements) { diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/PhonographColorUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/PhonographColorUtil.java index 77d6e0c93..0143d3faf 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/PhonographColorUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/PhonographColorUtil.java @@ -1,9 +1,9 @@ package com.kabouzeid.gramophone.util; import android.graphics.Bitmap; -import android.support.annotation.ColorInt; -import android.support.annotation.Nullable; -import android.support.v7.graphics.Palette; +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; +import androidx.palette.graphics.Palette; import com.kabouzeid.appthemehelper.util.ColorUtil; diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java index 7f900cac3..9eadddc1c 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/PlaylistsUtil.java @@ -8,12 +8,14 @@ import android.os.Environment; import android.provider.BaseColumns; import android.provider.MediaStore; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import android.widget.Toast; import com.kabouzeid.gramophone.R; +import com.kabouzeid.gramophone.adapter.PlaylistAdapter; import com.kabouzeid.gramophone.helper.M3UWriter; +import com.kabouzeid.gramophone.helper.menu.PlaylistMenuHelper; import com.kabouzeid.gramophone.model.Playlist; import com.kabouzeid.gramophone.model.PlaylistSong; import com.kabouzeid.gramophone.model.Song; @@ -21,6 +23,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import static android.provider.MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI; @@ -82,7 +85,7 @@ public static int createPlaylist(@NonNull final Context context, @Nullable final return id; } - public static void deletePlaylists(@NonNull final Context context, @NonNull final ArrayList playlists) { + public static void deletePlaylists(@NonNull final Context context, @NonNull final List playlists) { final StringBuilder selection = new StringBuilder(); selection.append(MediaStore.Audio.Playlists._ID + " IN ("); for (int i = 0; i < playlists.size(); i++) { @@ -104,7 +107,6 @@ public static void addToPlaylist(@NonNull final Context context, final Song song helperList.add(song); addToPlaylist(context, helperList, playlistId, showToastOnFinish); } - public static void addToPlaylist(@NonNull final Context context, @NonNull final List songs, final int playlistId, final boolean showToastOnFinish) { final int size = songs.size(); final ContentResolver resolver = context.getContentResolver(); @@ -140,6 +142,20 @@ public static void addToPlaylist(@NonNull final Context context, @NonNull final } } + public static void addToPlaylistWithoutDuplicates(@NonNull final Context context, @NonNull final List songs, final int[] songIds, final int playlistId, final boolean showToastOnFinish){ + boolean[] isSongInPlaylist = doPlaylistContains(context, playlistId, songIds); + addToPlaylistWithoutDuplicates(context, songs, playlistId, isSongInPlaylist, showToastOnFinish); + } + public static void addToPlaylistWithoutDuplicates(@NonNull final Context context, @NonNull final List songs, final int playlistId, final boolean[] isSongInPlaylist,final boolean showToastOnFinish){ + ArrayList helperSongs = new ArrayList(); + for(int i = 0; i < songs.size(); i++){ + if(!isSongInPlaylist[i]){ + helperSongs.add(songs.get(i)); + } + } + addToPlaylist(context, helperSongs, playlistId, showToastOnFinish); + } + @NonNull public static ContentValues[] makeInsertItems(@NonNull final List songs, final int offset, int len, final int base) { if (offset + len > songs.size()) { @@ -204,6 +220,254 @@ public static boolean doPlaylistContains(@NonNull final Context context, final l } return false; } + public static boolean[] doPlaylistContains(@NonNull final Context context, final long playlistId, final int[] songIds) { + if (playlistId != -1) { + final int[] songIdsOriginalOrder = songIds; + int[] songIdsSorted = songIds.clone(); + java.util.Arrays.sort(songIdsSorted); + + boolean[] PlaylistContainsSongSorted = new boolean[songIdsSorted.length]; + boolean[] PlaylistContainsSongOriginalOrder; + + try { + Cursor playlistSongs = context.getContentResolver().query( + MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId), + new String[]{MediaStore.Audio.Playlists.Members.AUDIO_ID}, null, new String[]{}, MediaStore.Audio.Playlists.Members.AUDIO_ID + " ASC"); + + if (playlistSongs != null && playlistSongs.getCount() > 0) { + playlistSongs.moveToNext(); //goes to first element + + int songIndex = 0; + int playlistIndex = 0; + while (true) + { + if(songIndex >= songIdsSorted.length){ + break; + } + int playlistSong = playlistSongs.getInt(0); + if(songIdsSorted[songIndex] > playlistSong) + { + playlistIndex++; + if(playlistIndex < playlistSongs.getCount()) + { + playlistSongs.moveToNext(); + } + else + { + break; + } + } + else if (songIdsSorted[songIndex] < playlistSong) + { + PlaylistContainsSongSorted[songIndex] = false; + songIndex++; + } + else if(songIdsSorted[songIndex] == playlistSong) + { + PlaylistContainsSongSorted[songIndex] = true; + songIndex++; + } + } + playlistSongs.close(); + } + } catch (SecurityException ignored) { + } + //long[] partTimes = new long[songIds.length];//TODO: remove stopwatch + //long startTime = System.currentTimeMillis();//TODO: remove stopwatch + //Revert the sorting to return the right results + PlaylistContainsSongOriginalOrder = PlaylistContainsSongSorted.clone(); + for(int i = 0; i < songIdsSorted.length; i++){ + //partTimes[i] = System.currentTimeMillis();//TODO: remove stopwatch + if(songIdsOriginalOrder[i] == songIdsSorted[i]){ + continue; + } + else{ + int indexOfSongInSortedArray = i; + int compareValue; + + int max = songIds.length; + int min = -1; + while(max - min > 15){ //this is a random number which I hope will result in a good efficiency. Tests showed it should be below 100. + compareValue = Integer.compare(songIdsOriginalOrder[i], songIdsSorted[indexOfSongInSortedArray]); + if(compareValue > 0){ + min = indexOfSongInSortedArray; + } + else{ + max = indexOfSongInSortedArray; + } + indexOfSongInSortedArray = (min + max)/2; + } + indexOfSongInSortedArray = min + 1; + while (songIdsOriginalOrder[i] != songIdsSorted[indexOfSongInSortedArray]){ + indexOfSongInSortedArray++; + } + PlaylistContainsSongOriginalOrder[i] = PlaylistContainsSongSorted[indexOfSongInSortedArray]; + } + } + /* + long stopTimeTotal = System.currentTimeMillis();//TODO: remove stopwatch + long[] diffs = new long[songIds.length];//TODO: remove stopwatch + diffs[songIds.length-1] = stopTimeTotal - partTimes[songIds.length-1]; + long min = diffs[songIds.length-1]; //TODO: remove stopwatch + long max = diffs[songIds.length-1]; //TODO: remove stopwatch + long sum = diffs[songIds.length-1]; //TODO: remove stopwatch + for (int i = 0; i < songIdsSorted.length - 1; i++) { + diffs[i] = partTimes[i+1] - partTimes[i]; + sum += diffs[i]; + min = Math.min(diffs[i], min); + max = Math.max(diffs[i], max); + } + float mean = (float)sum / partTimes.length; + long TotalTime = stopTimeTotal - startTime;//TODO: remove stopwatch + */ + return PlaylistContainsSongOriginalOrder; + } + else{ + throw new IllegalArgumentException("Must be a non-negative integer"); + } + } + public static int doPlaylistContainsCount(@NonNull final Context context, final long playlistId, final int[] songIds) { + return playlistContainsCount(context, doPlaylistContains(context, playlistId, songIds)); + } + public static int playlistContainsCount(@NonNull final Context context, boolean[] isSongInPlaylist) { + int count = 0; + for(boolean isThisSongInPlaylist : isSongInPlaylist){ + if(isThisSongInPlaylist){ + count++; + } + } + return count; + } + public static boolean doPlaylistContainsAnySong(@NonNull final Context context, final long playlistId, final int[] songIds) { + if (playlistId != -1) { + int[] songIdsSorted = songIds.clone(); + java.util.Arrays.sort(songIdsSorted); + + boolean result = false; + + try { + Cursor playlistSongs = context.getContentResolver().query( + MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId), + new String[]{MediaStore.Audio.Playlists.Members.AUDIO_ID}, null, new String[]{}, MediaStore.Audio.Playlists.Members.AUDIO_ID + " ASC"); + + if (playlistSongs != null && playlistSongs.getCount() > 0) { + playlistSongs.moveToNext(); //goes to first element + + int songIndex = 0; + int playlistIndex = 0; + while (true) + { + if(songIndex >= songIdsSorted.length){ + break; + } + int playlistSong = playlistSongs.getInt(0); + if(songIdsSorted[songIndex] > playlistSong) + { + playlistIndex++; + if(playlistIndex < playlistSongs.getCount()) + { + playlistSongs.moveToNext(); + } + else + { + break; + } + } + else if (songIdsSorted[songIndex] < playlistSong) + { + songIndex++; + } + else if(songIdsSorted[songIndex] == playlistSong) + { + result = true; + songIndex++; + break; + } + } + playlistSongs.close(); + } + } catch (SecurityException ignored) { + } + + return result; + } + else{ + throw new IllegalArgumentException("Must be a non-negative integer"); + } + } + public static boolean playlistContainsAnySong(@NonNull final Context context, boolean[] isSongInPlaylist) { + for(boolean isThisSongInPlaylist : isSongInPlaylist){ + if(isThisSongInPlaylist){ + return true; + } + } + return false; + } + public static boolean doPlaylistContainsAllSongs(@NonNull final Context context, final long playlistId, final int[] songIds) { + if (playlistId != -1) { + int[] songIdsSorted = songIds.clone(); + java.util.Arrays.sort(songIdsSorted); + + boolean result = true; + + try { + Cursor playlistSongs = context.getContentResolver().query( + MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId), + new String[]{MediaStore.Audio.Playlists.Members.AUDIO_ID}, null, new String[]{}, MediaStore.Audio.Playlists.Members.AUDIO_ID + " ASC"); + + if (playlistSongs != null && playlistSongs.getCount() > 0) { + playlistSongs.moveToNext(); //goes to first element + + int songIndex = 0; + int playlistIndex = 0; + while (true) + { + if(songIndex >= songIdsSorted.length){ + break; + } + int playlistSong = playlistSongs.getInt(0); + if(songIdsSorted[songIndex] > playlistSong) + { + playlistIndex++; + if(playlistIndex < playlistSongs.getCount()) + { + playlistSongs.moveToNext(); + } + else + { + break; + } + } + else if (songIdsSorted[songIndex] < playlistSong) + { + result = false; + songIndex++; + break; + } + else if(songIdsSorted[songIndex] == playlistSong) + { + songIndex++; + } + } + playlistSongs.close(); + } + } catch (SecurityException ignored) { + } + + return result; + } + else{ + throw new IllegalArgumentException("Must be a non-negative integer"); + } + } + public static boolean playlistContainsAllSongs(@NonNull final Context context, boolean[] isSongInPlaylist) { + for(boolean isThisSongInPlaylist : isSongInPlaylist){ + if(!isThisSongInPlaylist){ + return false; + } + } + return true; + } public static boolean moveItem(@NonNull final Context context, int playlistId, int from, int to) { return MediaStore.Audio.Playlists.Members.moveItem(context.getContentResolver(), diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java index b1d6c2c10..66e4234f4 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java @@ -3,13 +3,16 @@ import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.support.annotation.StyleRes; +import androidx.annotation.NonNull; +import androidx.annotation.StyleRes; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; + import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.helper.SortOrder; import com.kabouzeid.gramophone.model.CategoryInfo; @@ -19,6 +22,7 @@ import java.io.File; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.List; public final class PreferenceUtil { public static final String GENERAL_THEME = "general_theme"; @@ -66,6 +70,7 @@ public final class PreferenceUtil { public static final String LAST_SLEEP_TIMER_VALUE = "last_sleep_timer_value"; public static final String NEXT_SLEEP_TIMER_ELAPSED_REALTIME = "next_sleep_timer_elapsed_real_time"; + public static final String SLEEP_TIMER_FINISH_SONG = "sleep_timer_finish_music"; public static final String IGNORE_MEDIA_STORE_ARTWORK = "ignore_media_store_artwork"; @@ -82,6 +87,8 @@ public final class PreferenceUtil { public static final String LIBRARY_CATEGORIES = "library_categories"; + private static final String REMEMBER_SHUFFLE = "remember_shuffle"; + private static PreferenceUtil sInstance; private final SharedPreferences mPreferences; @@ -97,6 +104,20 @@ public static PreferenceUtil getInstance(@NonNull final Context context) { return sInstance; } + public static boolean isAllowedToDownloadMetadata(final Context context) { + switch (getInstance(context).autoDownloadImagesPolicy()) { + case "always": + return true; + case "only_wifi": + final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo(); + return netInfo != null && netInfo.getType() == ConnectivityManager.TYPE_WIFI && netInfo.isConnectedOrConnecting(); + case "never": + default: + return false; + } + } + public void registerOnSharedPreferenceChangedListener(SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener) { mPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); } @@ -105,8 +126,15 @@ public void unregisterOnSharedPreferenceChangedListener(SharedPreferences.OnShar mPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); } + @StyleRes public int getGeneralTheme() { - return getThemeResFromPrefValue(mPreferences.getString(GENERAL_THEME, "")); + return getThemeResFromPrefValue(mPreferences.getString(GENERAL_THEME, "light")); + } + + public void setGeneralTheme(String theme) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putString(GENERAL_THEME, theme); + editor.commit(); } @StyleRes @@ -215,6 +243,12 @@ public final String getArtistSortOrder() { return mPreferences.getString(ARTIST_SORT_ORDER, SortOrder.ArtistSortOrder.ARTIST_A_Z); } + public void setArtistSortOrder(final String sortOrder) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putString(ARTIST_SORT_ORDER, sortOrder); + editor.commit(); + } + public final String getArtistSongSortOrder() { return mPreferences.getString(ARTIST_SONG_SORT_ORDER, SortOrder.ArtistSongSortOrder.SONG_A_Z); } @@ -227,6 +261,12 @@ public final String getAlbumSortOrder() { return mPreferences.getString(ALBUM_SORT_ORDER, SortOrder.AlbumSortOrder.ALBUM_A_Z); } + public void setAlbumSortOrder(final String sortOrder) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putString(ALBUM_SORT_ORDER, sortOrder); + editor.commit(); + } + public final String getAlbumSongSortOrder() { return mPreferences.getString(ALBUM_SONG_SORT_ORDER, SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST); } @@ -235,6 +275,12 @@ public final String getSongSortOrder() { return mPreferences.getString(SONG_SORT_ORDER, SortOrder.SongSortOrder.SONG_A_Z); } + public void setSongSortOrder(final String sortOrder) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putString(SONG_SORT_ORDER, sortOrder); + editor.commit(); + } + public final String getGenreSortOrder() { return mPreferences.getString(GENRE_SORT_ORDER, SortOrder.GenreSortOrder.GENRE_A_Z); } @@ -252,6 +298,10 @@ public long getLastAddedCutoff() { interval = calendarUtil.getElapsedWeek(); break; + case "past_seven_days": + interval = calendarUtil.getElapsedDays(7); + break; + case "past_three_months": interval = calendarUtil.getElapsedMonths(3); break; @@ -289,6 +339,16 @@ public void setNextSleepTimerElapsedRealtime(final long value) { editor.apply(); } + public boolean getSleepTimerFinishMusic() { + return mPreferences.getBoolean(SLEEP_TIMER_FINISH_SONG, false); + } + + public void setSleepTimerFinishMusic(final boolean value) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putBoolean(SLEEP_TIMER_FINISH_SONG, value); + editor.apply(); + } + public void setAlbumGridSize(final int gridSize) { final SharedPreferences.Editor editor = mPreferences.edit(); editor.putInt(ALBUM_GRID_SIZE, gridSize); @@ -407,6 +467,10 @@ public final boolean introShown() { return mPreferences.getBoolean(INTRO_SHOWN, false); } + public final boolean rememberShuffle() { + return mPreferences.getBoolean(REMEMBER_SHUFFLE, true); + } + public final String autoDownloadImagesPolicy() { return mPreferences.getString(AUTO_DOWNLOAD_IMAGES_POLICY, "only_wifi"); } @@ -435,9 +499,9 @@ public final boolean initializedBlacklist() { return mPreferences.getBoolean(INITIALIZED_BLACKLIST, false); } - public void setLibraryCategoryInfos(ArrayList categories) { + public void setLibraryCategoryInfos(List categories) { Gson gson = new Gson(); - Type collectionType = new TypeToken>() { + Type collectionType = new TypeToken>() { }.getType(); final SharedPreferences.Editor editor = mPreferences.edit(); @@ -445,11 +509,11 @@ public void setLibraryCategoryInfos(ArrayList categories) { editor.apply(); } - public ArrayList getLibraryCategoryInfos() { + public List getLibraryCategoryInfos() { String data = mPreferences.getString(LIBRARY_CATEGORIES, null); if (data != null) { Gson gson = new Gson(); - Type collectionType = new TypeToken>() { + Type collectionType = new TypeToken>() { }.getType(); try { @@ -462,8 +526,8 @@ public ArrayList getLibraryCategoryInfos() { return getDefaultLibraryCategoryInfos(); } - public ArrayList getDefaultLibraryCategoryInfos() { - ArrayList defaultCategoryInfos = new ArrayList<>(5); + public List getDefaultLibraryCategoryInfos() { + List defaultCategoryInfos = new ArrayList<>(5); defaultCategoryInfos.add(new CategoryInfo(CategoryInfo.Category.SONGS, true)); defaultCategoryInfos.add(new CategoryInfo(CategoryInfo.Category.ALBUMS, true)); defaultCategoryInfos.add(new CategoryInfo(CategoryInfo.Category.ARTISTS, true)); diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/RingtoneManager.java b/app/src/main/java/com/kabouzeid/gramophone/util/RingtoneManager.java new file mode 100644 index 000000000..5e2a9ae22 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/util/RingtoneManager.java @@ -0,0 +1,77 @@ +package com.kabouzeid.gramophone.util; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.provider.BaseColumns; +import android.provider.MediaStore; +import android.provider.Settings; +import android.widget.Toast; + +import androidx.annotation.NonNull; + +import com.afollestad.materialdialogs.MaterialDialog; +import com.kabouzeid.gramophone.R; + +public class RingtoneManager { + + + public static boolean requiresDialog(@NonNull Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return !Settings.System.canWrite(context); + } + return false; + } + + public static MaterialDialog showDialog(Context context) { + return new MaterialDialog.Builder(context) + .title(R.string.dialog_ringtone_title) + .content(R.string.dialog_ringtone_message) + .positiveText(android.R.string.ok) + .negativeText(android.R.string.cancel) + .onPositive((dialog, which) -> { + Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS); + intent.setData(Uri.parse("package:" + context.getPackageName())); + context.startActivity(intent); + }) + .show(); + } + + public void setRingtone(@NonNull final Context context, final int id) { + final ContentResolver resolver = context.getContentResolver(); + final Uri uri = MusicUtil.getSongFileUri(id); + try { + final ContentValues values = new ContentValues(2); + values.put(MediaStore.Audio.AudioColumns.IS_RINGTONE, "1"); + values.put(MediaStore.Audio.AudioColumns.IS_ALARM, "1"); + resolver.update(uri, values, null, null); + } catch (@NonNull final UnsupportedOperationException ignored) { + return; + } + + try { + Cursor cursor = resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, + new String[]{MediaStore.MediaColumns.TITLE}, + BaseColumns._ID + "=?", + new String[]{String.valueOf(id)}, + null); + try { + if (cursor != null && cursor.getCount() == 1) { + cursor.moveToFirst(); + Settings.System.putString(resolver, Settings.System.RINGTONE, uri.toString()); + final String message = context.getString(R.string.x_has_been_set_as_ringtone, cursor.getString(0)); + Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + } catch (SecurityException ignored) { + } + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/SwipeAndDragHelper.java b/app/src/main/java/com/kabouzeid/gramophone/util/SwipeAndDragHelper.java index 3e28263d0..51c1dca04 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/SwipeAndDragHelper.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/SwipeAndDragHelper.java @@ -1,8 +1,8 @@ package com.kabouzeid.gramophone.util; import android.graphics.Canvas; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.helper.ItemTouchHelper; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.ItemTouchHelper; public class SwipeAndDragHelper extends ItemTouchHelper.Callback { diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/Util.java b/app/src/main/java/com/kabouzeid/gramophone/util/Util.java index 5d2924416..4500b89f4 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/Util.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/Util.java @@ -7,16 +7,10 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Point; -import android.graphics.drawable.Drawable; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; import android.os.Build; -import android.support.annotation.AttrRes; -import android.support.annotation.ColorInt; -import android.support.annotation.DrawableRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.graphics.drawable.VectorDrawableCompat; +import androidx.annotation.AttrRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import android.util.TypedValue; import android.view.Display; import android.view.View; @@ -24,7 +18,6 @@ import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; -import com.kabouzeid.appthemehelper.util.TintHelper; import com.kabouzeid.gramophone.R; /** @@ -80,13 +73,6 @@ public static boolean isLandscape(@NonNull final Resources resources) { return resources.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; } - public static Drawable resolveDrawable(@NonNull Context context, @AttrRes int drawableAttr) { - TypedArray a = context.obtainStyledAttributes(new int[]{drawableAttr}); - Drawable drawable = a.getDrawable(0); - a.recycle(); - return drawable; - } - public static int resolveDimensionPixelSize(@NonNull Context context, @AttrRes int dimenAttr) { TypedArray a = context.obtainStyledAttributes(new int[]{dimenAttr}); int dimensionPixelSize = a.getDimensionPixelSize(0, 0); @@ -94,25 +80,6 @@ public static int resolveDimensionPixelSize(@NonNull Context context, @AttrRes i return dimensionPixelSize; } - public static Drawable getVectorDrawable(@NonNull Context context, @DrawableRes int id) { - return getVectorDrawable(context.getResources(), id, context.getTheme()); - } - - public static Drawable getVectorDrawable(@NonNull Resources res, @DrawableRes int resId, @Nullable Resources.Theme theme) { - if (Build.VERSION.SDK_INT >= 21) { - return res.getDrawable(resId, theme); - } - return VectorDrawableCompat.create(res, resId, theme); - } - - public static Drawable getTintedVectorDrawable(@NonNull Context context, @DrawableRes int id, @ColorInt int color) { - return TintHelper.createTintedDrawable(getVectorDrawable(context.getResources(), id, context.getTheme()), color); - } - - public static Drawable getTintedVectorDrawable(@NonNull Resources res, @DrawableRes int resId, @Nullable Resources.Theme theme, @ColorInt int color) { - return TintHelper.createTintedDrawable(getVectorDrawable(res, resId, theme), color); - } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public static boolean isRTL(@NonNull Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { @@ -121,18 +88,4 @@ public static boolean isRTL(@NonNull Context context) { } else return false; } - public static boolean isAllowedToDownloadMetadata(final Context context) { - switch (PreferenceUtil.getInstance(context).autoDownloadImagesPolicy()) { - case "always": - return true; - case "only_wifi": - final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo(); - return netInfo != null && netInfo.getType() == ConnectivityManager.TYPE_WIFI && netInfo.isConnectedOrConnecting(); - case "never": - default: - return false; - } - } - -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/ViewUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/ViewUtil.java index db92315a9..ff8b596ed 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/ViewUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/ViewUtil.java @@ -12,8 +12,7 @@ import android.graphics.drawable.RippleDrawable; import android.graphics.drawable.StateListDrawable; import android.os.Build; -import android.support.annotation.ColorInt; -import android.support.v4.view.ViewCompat; +import androidx.annotation.ColorInt; import android.util.DisplayMetrics; import android.view.View; import android.view.animation.PathInterpolator; @@ -70,8 +69,8 @@ public static Drawable createSelectorDrawable(Context context, @ColorInt int col } public static boolean hitTest(View v, int x, int y) { - final int tx = (int) (ViewCompat.getTranslationX(v) + 0.5f); - final int ty = (int) (ViewCompat.getTranslationY(v) + 0.5f); + final int tx = (int) (v.getTranslationX() + 0.5f); + final int ty = (int) (v.getTranslationY() + 0.5f); final int left = v.getLeft() + tx; final int right = v.getRight() + tx; final int top = v.getTop() + ty; @@ -96,4 +95,4 @@ public static float convertPixelsToDp(float px, Resources resources) { DisplayMetrics metrics = resources.getDisplayMetrics(); return px / metrics.density; } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/views/BreadCrumbLayout.java b/app/src/main/java/com/kabouzeid/gramophone/views/BreadCrumbLayout.java index d5dccff95..155076601 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/views/BreadCrumbLayout.java +++ b/app/src/main/java/com/kabouzeid/gramophone/views/BreadCrumbLayout.java @@ -5,8 +5,8 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; -import android.support.annotation.ColorInt; -import android.support.annotation.NonNull; +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; diff --git a/app/src/main/java/com/kabouzeid/gramophone/views/IconImageView.java b/app/src/main/java/com/kabouzeid/gramophone/views/IconImageView.java index 14e92b9be..d9bae1e3c 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/views/IconImageView.java +++ b/app/src/main/java/com/kabouzeid/gramophone/views/IconImageView.java @@ -2,7 +2,7 @@ import android.content.Context; import android.graphics.PorterDuff; -import android.support.v7.widget.AppCompatImageView; +import androidx.appcompat.widget.AppCompatImageView; import android.util.AttributeSet; import com.kabouzeid.appthemehelper.util.ATHUtil; diff --git a/app/src/main/java/com/kabouzeid/gramophone/views/PlayPauseDrawable.java b/app/src/main/java/com/kabouzeid/gramophone/views/PlayPauseDrawable.java index d8f91dc22..f624ee1b6 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/views/PlayPauseDrawable.java +++ b/app/src/main/java/com/kabouzeid/gramophone/views/PlayPauseDrawable.java @@ -13,7 +13,7 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.util.Property; import android.view.animation.DecelerateInterpolator; diff --git a/app/src/main/java/com/kabouzeid/gramophone/views/StatusBarMarginFrameLayout.java b/app/src/main/java/com/kabouzeid/gramophone/views/StatusBarMarginFrameLayout.java new file mode 100644 index 000000000..878cab919 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/views/StatusBarMarginFrameLayout.java @@ -0,0 +1,33 @@ +package com.kabouzeid.gramophone.views; + +import android.content.Context; +import android.os.Build; +import android.util.AttributeSet; +import android.view.WindowInsets; +import android.widget.FrameLayout; + +public class StatusBarMarginFrameLayout extends FrameLayout { + + + public StatusBarMarginFrameLayout(Context context) { + super(context); + } + + public StatusBarMarginFrameLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public StatusBarMarginFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public WindowInsets onApplyWindowInsets(WindowInsets insets) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); + lp.topMargin = insets.getSystemWindowInsetTop(); + setLayoutParams(lp); + } + return super.onApplyWindowInsets(insets); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/views/StatusBarView.java b/app/src/main/java/com/kabouzeid/gramophone/views/StatusBarView.java new file mode 100644 index 000000000..b1f0797e8 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/views/StatusBarView.java @@ -0,0 +1,34 @@ +package com.kabouzeid.gramophone.views; + +import android.content.Context; +import android.os.Build; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; + +public class StatusBarView extends View { + + public StatusBarView(Context context) { + super(context); + } + + public StatusBarView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public StatusBarView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public WindowInsets onApplyWindowInsets(WindowInsets insets) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + ViewGroup.LayoutParams lp = getLayoutParams(); + lp.height = insets.getSystemWindowInsetTop(); + setLayoutParams(lp); + } + return super.onApplyWindowInsets(insets); + } + +} diff --git a/app/src/main/res/drawable/ic_album_white_24dp.xml b/app/src/main/res/drawable/ic_album_white_24dp.xml new file mode 100644 index 000000000..6aa0cec65 --- /dev/null +++ b/app/src/main/res/drawable/ic_album_white_24dp.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/drawable/ic_event_white_24dp.xml b/app/src/main/res/drawable/ic_event_white_24dp.xml new file mode 100644 index 000000000..72a255c71 --- /dev/null +++ b/app/src/main/res/drawable/ic_event_white_24dp.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/ic_google_circles_communities_white_24dp.xml b/app/src/main/res/drawable/ic_google_circles_communities_white_24dp.xml deleted file mode 100644 index 58ee29f91..000000000 --- a/app/src/main/res/drawable/ic_google_circles_communities_white_24dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_google_plus_white_24dp.xml b/app/src/main/res/drawable/ic_google_plus_white_24dp.xml deleted file mode 100644 index 6c2c66b03..000000000 --- a/app/src/main/res/drawable/ic_google_plus_white_24dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_music_note_white_24dp.xml b/app/src/main/res/drawable/ic_music_note_white_24dp.xml new file mode 100644 index 000000000..2644404d5 --- /dev/null +++ b/app/src/main/res/drawable/ic_music_note_white_24dp.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/ic_scanner_white_24dp.xml b/app/src/main/res/drawable/ic_scanner_white_24dp.xml new file mode 100644 index 000000000..f2493cd39 --- /dev/null +++ b/app/src/main/res/drawable/ic_scanner_white_24dp.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/ic_select_all_white_24dp.xml b/app/src/main/res/drawable/ic_select_all_white_24dp.xml new file mode 100644 index 000000000..57fc53b2e --- /dev/null +++ b/app/src/main/res/drawable/ic_select_all_white_24dp.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/layout-land/fragment_card_player.xml b/app/src/main/res/layout-land/fragment_card_player.xml index 813846dec..234fedb62 100644 --- a/app/src/main/res/layout-land/fragment_card_player.xml +++ b/app/src/main/res/layout-land/fragment_card_player.xml @@ -15,7 +15,7 @@ - @@ -70,18 +70,19 @@ - @@ -116,7 +117,7 @@ android:textAppearance="@style/TextAppearance.AppCompat.Body2" android:textColor="?android:textColorSecondary" /> - - + diff --git a/app/src/main/res/layout-land/fragment_flat_player.xml b/app/src/main/res/layout-land/fragment_flat_player.xml index 6762967ec..2498679aa 100644 --- a/app/src/main/res/layout-land/fragment_flat_player.xml +++ b/app/src/main/res/layout-land/fragment_flat_player.xml @@ -11,19 +11,19 @@ android:elevation="@dimen/toolbar_elevation" tools:ignore="UnusedAttribute"> - - - @@ -95,7 +95,7 @@ android:textAppearance="@style/TextAppearance.AppCompat.Body2" android:textColor="?android:textColorSecondary" /> - - diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index 5795ba59c..d2a36bd8e 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -8,7 +8,7 @@ - diff --git a/app/src/main/res/layout/activity_album_detail.xml b/app/src/main/res/layout/activity_album_detail.xml index 3b313d792..1dd1b7608 100644 --- a/app/src/main/res/layout/activity_album_detail.xml +++ b/app/src/main/res/layout/activity_album_detail.xml @@ -1,87 +1,220 @@ - + android:layout_height="match_parent" + android:orientation="vertical"> - - - + - - - + android:elevation="@dimen/toolbar_elevation" + tools:ignore="UnusedAttribute"> - - - - + - + - + android:layout_height="match_parent"> - + + android:orientation="vertical"> - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + android:layout_height="match_parent" + android:background="@android:color/transparent" /> - + + + + - \ No newline at end of file + diff --git a/app/src/main/res/layout/activity_album_tag_editor.xml b/app/src/main/res/layout/activity_album_tag_editor.xml index c7f1926c9..8a4559a3c 100644 --- a/app/src/main/res/layout/activity_album_tag_editor.xml +++ b/app/src/main/res/layout/activity_album_tag_editor.xml @@ -29,7 +29,7 @@ android:scaleType="centerCrop" android:src="@drawable/default_album_art" android:transitionName="@string/transition_album_art" - tools:ignore="UnusedAttribute" /> + tools:ignore="ContentDescription,UnusedAttribute" /> @@ -47,7 +47,7 @@ android:paddingTop="8dp" tools:ignore="UnusedAttribute"> - @@ -63,7 +63,7 @@ android:textAppearance="@style/TextAppearance.AppCompat.Title" android:textColor="@color/ate_primary_text_dark" /> - + @@ -76,7 +76,7 @@ android:orientation="vertical" android:padding="16dp"> - @@ -92,9 +92,9 @@ android:singleLine="true" android:textAppearance="@style/TextAppearance.AppCompat.Title" /> - + - @@ -110,9 +110,9 @@ android:singleLine="true" android:textAppearance="@style/TextAppearance.AppCompat.Title" /> - + - @@ -128,7 +128,7 @@ android:singleLine="true" android:textAppearance="@style/TextAppearance.AppCompat.Title" /> - + @@ -138,17 +138,17 @@ - - + + android:layout_height="match_parent" + android:orientation="vertical"> - - - + - - - + android:elevation="@dimen/toolbar_elevation" + tools:ignore="UnusedAttribute"> - - - - + - + - + android:layout_height="match_parent"> - + + android:orientation="vertical"> - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + android:layout_height="match_parent" + android:background="@android:color/transparent" /> - + + + + - \ No newline at end of file + diff --git a/app/src/main/res/layout/activity_bug_report.xml b/app/src/main/res/layout/activity_bug_report.xml index e5dced333..d170b22c3 100644 --- a/app/src/main/res/layout/activity_bug_report.xml +++ b/app/src/main/res/layout/activity_bug_report.xml @@ -7,17 +7,17 @@ android:focusableInTouchMode="true" android:orientation="vertical"> - - + android:layout_height="?android:attr/actionBarSize" /> - + - @@ -57,7 +57,7 @@ - - \ No newline at end of file + diff --git a/app/src/main/res/layout/activity_genre_detail.xml b/app/src/main/res/layout/activity_genre_detail.xml index c59b2d6c3..313cfe788 100644 --- a/app/src/main/res/layout/activity_genre_detail.xml +++ b/app/src/main/res/layout/activity_genre_detail.xml @@ -20,7 +20,7 @@ android:elevation="@dimen/toolbar_elevation" tools:ignore="UnusedAttribute"> - @@ -28,7 +28,7 @@ + android:layout_height="?android:attr/actionBarSize" /> @@ -52,4 +52,4 @@ android:textSize="@dimen/empty_text_size" android:visibility="gone" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/activity_main_content.xml b/app/src/main/res/layout/activity_main_content.xml index a9ee9c50e..a9e82bc64 100644 --- a/app/src/main/res/layout/activity_main_content.xml +++ b/app/src/main/res/layout/activity_main_content.xml @@ -1,5 +1,22 @@ - \ No newline at end of file + android:layout_height="match_parent" + android:orientation="vertical"> + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main_drawer_layout.xml b/app/src/main/res/layout/activity_main_drawer_layout.xml index ead1e1e63..36cd1fed8 100644 --- a/app/src/main/res/layout/activity_main_drawer_layout.xml +++ b/app/src/main/res/layout/activity_main_drawer_layout.xml @@ -1,22 +1,20 @@ - + android:layout_height="match_parent"> - + android:layout_height="match_parent" /> - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_playlist_detail.xml b/app/src/main/res/layout/activity_playlist_detail.xml index 450b88190..dd879b53e 100644 --- a/app/src/main/res/layout/activity_playlist_detail.xml +++ b/app/src/main/res/layout/activity_playlist_detail.xml @@ -1,56 +1,50 @@ - - - - + - - - - - - - - - - - + + + + + android:layout_height="?android:attr/actionBarSize" /> - + + + + + - \ No newline at end of file + diff --git a/app/src/main/res/layout/activity_preferences.xml b/app/src/main/res/layout/activity_preferences.xml index 653ae7bc1..2f25857ed 100644 --- a/app/src/main/res/layout/activity_preferences.xml +++ b/app/src/main/res/layout/activity_preferences.xml @@ -6,7 +6,7 @@ - diff --git a/app/src/main/res/layout/activity_purchase.xml b/app/src/main/res/layout/activity_purchase.xml index 43a0d19a4..de7374538 100644 --- a/app/src/main/res/layout/activity_purchase.xml +++ b/app/src/main/res/layout/activity_purchase.xml @@ -8,7 +8,7 @@ - - - + - + android:src="@drawable/promo_banner" + tools:ignore="ContentDescription" /> - + - \ No newline at end of file + diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index b4268a140..4b3b12ae6 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -6,7 +6,7 @@ - @@ -14,7 +14,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + android:src="@drawable/default_album_art" + tools:ignore="ContentDescription" /> - @@ -54,9 +55,9 @@ android:textAppearance="@style/TextAppearance.AppCompat.Title" android:textColor="@color/ate_primary_text_dark" /> - + - @@ -71,7 +72,7 @@ android:singleLine="true" android:textAppearance="@style/TextAppearance.AppCompat.Title" android:textColor="@color/ate_primary_text_dark" /> - + @@ -84,7 +85,7 @@ android:orientation="vertical" android:padding="16dp"> - @@ -100,9 +101,9 @@ android:singleLine="true" android:textAppearance="@style/TextAppearance.AppCompat.Title" /> - + - @@ -114,12 +115,13 @@ android:fontFamily="sans-serif" android:gravity="center_vertical" android:hint="@string/genre" + android:inputType="text|textCapWords" android:singleLine="true" android:textAppearance="@style/TextAppearance.AppCompat.Title" /> - + - @@ -135,9 +137,9 @@ android:singleLine="true" android:textAppearance="@style/TextAppearance.AppCompat.Title" /> - + - @@ -153,9 +155,9 @@ android:singleLine="true" android:textAppearance="@style/TextAppearance.AppCompat.Title" /> - + - @@ -170,7 +172,7 @@ android:inputType="textMultiLine" android:textAppearance="@style/TextAppearance.AppCompat.Title" /> - + @@ -178,7 +180,7 @@ - - - - - - + diff --git a/app/src/main/res/layout/bug_report_card_report.xml b/app/src/main/res/layout/bug_report_card_report.xml index 5f4f37f42..56940021b 100644 --- a/app/src/main/res/layout/bug_report_card_report.xml +++ b/app/src/main/res/layout/bug_report_card_report.xml @@ -1,5 +1,5 @@ - - - - + - - - + - - - + - - - + @@ -185,4 +185,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/card_about_app.xml b/app/src/main/res/layout/card_about_app.xml index 38abd7302..1778914bb 100644 --- a/app/src/main/res/layout/card_about_app.xml +++ b/app/src/main/res/layout/card_about_app.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/card_author.xml b/app/src/main/res/layout/card_author.xml index 93482056a..6ce79c99c 100644 --- a/app/src/main/res/layout/card_author.xml +++ b/app/src/main/res/layout/card_author.xml @@ -1,5 +1,5 @@ - - - - - - - - - - \ No newline at end of file + diff --git a/app/src/main/res/layout/card_special_thanks.xml b/app/src/main/res/layout/card_special_thanks.xml index 0136d4ac7..5d70a1be1 100644 --- a/app/src/main/res/layout/card_special_thanks.xml +++ b/app/src/main/res/layout/card_special_thanks.xml @@ -1,5 +1,5 @@ - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/card_support_development.xml b/app/src/main/res/layout/card_support_development.xml index 97ac41651..dbe7c4e46 100644 --- a/app/src/main/res/layout/card_support_development.xml +++ b/app/src/main/res/layout/card_support_development.xml @@ -1,5 +1,5 @@ - - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/app/src/main/res/layout/dialog_sleep_timer.xml b/app/src/main/res/layout/dialog_sleep_timer.xml index cf0968582..374bae1cd 100644 --- a/app/src/main/res/layout/dialog_sleep_timer.xml +++ b/app/src/main/res/layout/dialog_sleep_timer.xml @@ -1,28 +1,42 @@ - + android:layout_height="wrap_content" + android:orientation="vertical"> - + + + - + + + + - - + android:layout_marginEnd="16dp" + android:layout_marginStart="16dp" + android:text="@string/finish_current_music_sleep_timer" /> + diff --git a/app/src/main/res/layout/fragment_card_player.xml b/app/src/main/res/layout/fragment_card_player.xml index 37ffc8561..c8c6d2171 100644 --- a/app/src/main/res/layout/fragment_card_player.xml +++ b/app/src/main/res/layout/fragment_card_player.xml @@ -6,8 +6,7 @@ - - + + + + + + + + - + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/status_bar_padding" + android:orientation="vertical"> - @@ -126,7 +138,7 @@ android:textAppearance="@style/TextAppearance.AppCompat.Body2" android:textColor="?android:textColorSecondary" /> - - + - + diff --git a/app/src/main/res/layout/fragment_card_player_playback_controls.xml b/app/src/main/res/layout/fragment_card_player_playback_controls.xml index d12ce3fe2..ae2969f49 100644 --- a/app/src/main/res/layout/fragment_card_player_playback_controls.xml +++ b/app/src/main/res/layout/fragment_card_player_playback_controls.xml @@ -123,7 +123,7 @@ android:layout_height="56dp" android:layout_centerInParent="true" /> - - - @@ -62,7 +62,7 @@ android:layout_height="@dimen/toolbar_scrim_height" android:background="@drawable/toolbar_gradient"> - @@ -123,7 +123,7 @@ android:textAppearance="@style/TextAppearance.AppCompat.Body2" android:textColor="?android:textColorSecondary" /> - - - - - - - - - - + app:layout_scrollFlags="scroll|enterAlways"> - + - + - + - + - + - + - + - - - - - + android:clipToPadding="false" + android:scrollbars="none" /> - + - + - + diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index e402e90fb..9dbb04640 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -1,67 +1,50 @@ - - - - - - - - - - - - - - - + app:layout_scrollFlags="scroll|enterAlways"> - + - + - + android:layout_height="?android:attr/actionBarSize" /> - + - + android:layout_height="@dimen/tab_height" + app:tabContentStart="72dp" + app:tabMode="scrollable" /> - + + + - + diff --git a/app/src/main/res/layout/fragment_player_album_cover.xml b/app/src/main/res/layout/fragment_player_album_cover.xml index 17e13007f..1d140e637 100644 --- a/app/src/main/res/layout/fragment_player_album_cover.xml +++ b/app/src/main/res/layout/fragment_player_album_cover.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - @@ -46,7 +46,7 @@ - - + diff --git a/app/src/main/res/layout/navigation_drawer_header.xml b/app/src/main/res/layout/navigation_drawer_header.xml index 8e77bef64..c12a71776 100644 --- a/app/src/main/res/layout/navigation_drawer_header.xml +++ b/app/src/main/res/layout/navigation_drawer_header.xml @@ -1,4 +1,4 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/preference_dialog_library_categories.xml b/app/src/main/res/layout/preference_dialog_library_categories.xml index 4782ec71d..e3f54417e 100644 --- a/app/src/main/res/layout/preference_dialog_library_categories.xml +++ b/app/src/main/res/layout/preference_dialog_library_categories.xml @@ -1,11 +1,12 @@ - - - + + diff --git a/app/src/main/res/layout/preference_dialog_now_playing_screen.xml b/app/src/main/res/layout/preference_dialog_now_playing_screen.xml index fe602a11d..c66c835de 100644 --- a/app/src/main/res/layout/preference_dialog_now_playing_screen.xml +++ b/app/src/main/res/layout/preference_dialog_now_playing_screen.xml @@ -5,7 +5,7 @@ android:layout_height="match_parent" android:orientation="vertical"> - - + android:layout_height="?android:attr/actionBarSize" /> @@ -27,4 +27,4 @@ android:layout_alignTop="@id/dummy_statusbar_actionbar" android:background="@drawable/toolbar_gradient" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/status_bar.xml b/app/src/main/res/layout/status_bar.xml index 87c5e1d51..83c3d4fd7 100644 --- a/app/src/main/res/layout/status_bar.xml +++ b/app/src/main/res/layout/status_bar.xml @@ -1,5 +1,5 @@ - + app:showAsAction="ifRoom" /> + app:showAsAction="ifRoom" /> + app:showAsAction="ifRoom" /> - \ No newline at end of file + + + diff --git a/app/src/main/res/menu/menu_drawer.xml b/app/src/main/res/menu/menu_drawer.xml index 6d992efc6..7466ad79c 100644 --- a/app/src/main/res/menu/menu_drawer.xml +++ b/app/src/main/res/menu/menu_drawer.xml @@ -14,6 +14,14 @@ android:title="@string/folders" /> + + + + + diff --git a/app/src/main/res/menu/menu_folders.xml b/app/src/main/res/menu/menu_folders.xml index 79b0b5e2a..52563a933 100644 --- a/app/src/main/res/menu/menu_folders.xml +++ b/app/src/main/res/menu/menu_folders.xml @@ -2,6 +2,12 @@ + + + + + + + + app:showAsAction="ifRoom" /> + app:showAsAction="ifRoom" /> + app:showAsAction="ifRoom" /> + app:showAsAction="ifRoom" /> - \ No newline at end of file + + + diff --git a/app/src/main/res/menu/menu_playlists_selection.xml b/app/src/main/res/menu/menu_playlists_selection.xml index aea5a2db0..2aaf3b3de 100644 --- a/app/src/main/res/menu/menu_playlists_selection.xml +++ b/app/src/main/res/menu/menu_playlists_selection.xml @@ -6,24 +6,36 @@ android:id="@+id/action_play_next" android:icon="@drawable/ic_redo_white_24dp" android:title="@string/action_play_next" - app:showAsAction="always" /> + app:showAsAction="ifRoom" /> + app:showAsAction="ifRoom" /> + app:showAsAction="ifRoom" /> + + + app:showAsAction="ifRoom" /> + + - \ No newline at end of file + diff --git a/app/src/main/res/menu/menu_playlists_songs_selection.xml b/app/src/main/res/menu/menu_playlists_songs_selection.xml index ff356b620..252933ebf 100644 --- a/app/src/main/res/menu/menu_playlists_songs_selection.xml +++ b/app/src/main/res/menu/menu_playlists_songs_selection.xml @@ -6,24 +6,30 @@ android:id="@+id/action_play_next" android:icon="@drawable/ic_redo_white_24dp" android:title="@string/action_play_next" - app:showAsAction="always" /> + app:showAsAction="ifRoom" /> + app:showAsAction="ifRoom" /> + app:showAsAction="ifRoom" /> + app:showAsAction="ifRoom" /> - \ No newline at end of file + + + diff --git a/app/src/main/res/menu/menu_search.xml b/app/src/main/res/menu/menu_search.xml index 36115c24a..0277f89e2 100644 --- a/app/src/main/res/menu/menu_search.xml +++ b/app/src/main/res/menu/menu_search.xml @@ -5,6 +5,6 @@ android:id="@+id/search" android:icon="@drawable/ic_search_white_24dp" android:title="@string/action_search" - app:actionViewClass="android.support.v7.widget.SearchView" + app:actionViewClass="androidx.appcompat.widget.SearchView" app:showAsAction="ifRoom|collapseActionView" /> \ No newline at end of file diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index e4da28ce6..7eb213639 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1,75 +1,83 @@ - ماذا تريد ان تشارك؟ - تستمع حاليا إلى %1$s لـ %2$s + ماذا تريد أن تشارك؟ + تستمع حالياً إلى %1$s من %2$s. ملف الصوت - تعيين %1$s كنغمة رنين - تعيين كنغمة رنين + تعيين %1$s كنغمة الرنين. + تعيين كنغمة الرنين مشاركة "الإعدادات" - سماح - "حول التطبيق " - امسح قائمة التشغيل - إضافة إلى الأغاني المفضلة - إزالة من الأغاني المفضلة + منح + حول + مسح قائمة التشغيل + إضافة إلى المفضلة + إزالة من المفضلة بحث استمع للتالي - استمع - أضف إلى قائمة الإنتظار - أزل من قائمة الانتظار - أضف إلى قائمة التشغيل - تعديل معلومات الملف + تشغيل + تشغيل/إيقاف مؤقت + السابق + التالي + إضافة إلى قائمة الانتظار + إزالة من قائمة الانتظار + إضافة إلى قائمة التشغيل... + محرر الوسوم حذف من الجهاز التفاصيل إعادة التسمية حذف فحص + تعيين كمسار البداية الألبومات الفنانون + الأنواع الأغاني قوائم التشغيل - \u0644\u0627 \u064a\u0645\u0643\u0646 \u062a\u0634\u063a\u064a\u0644 \u0627\u0644\u0623\u063a\u0646\u064a\u0629 - \u0644\u0645 \u064a\u062a\u0645 \u0627\u0644\u0639\u062b\u0648\u0631 \u0639\u0644\u0649 \u0627\u0644\u0633\u064a\u0631\u0629 \u0627\u0644\u0630\u0627\u062a\u064a\u0629 \u0644\u0644\u0641\u0646\u0627\u0646 - سيرة ذاتية - لم يتم منح التطبيق إذن Audio focus + \u0644\u0645 \u064a\u062a\u0645 \u0627\u0644\u062a\u0645\u0643\u0646 \u0645\u0646 \u062a\u0634\u063a\u064a\u0644 \u0627\u0644\u0623\u063a\u0646\u064a\u0629. + \u0644\u0645 \u064a\u062a\u0645 \u0627\u0644\u0639\u062b\u0648\u0631 \u0639\u0644\u0649 \u0627\u0644\u0633\u064a\u0631\u0629 \u0627\u0644\u0630\u0627\u062a\u064a\u0629 \u0644\u0644\u0641\u0646\u0627\u0646. + \u0644\u0645 \u064a\u062a\u0645 \u0627\u0644\u0639\u062b\u0648\u0631 \u0639\u0644\u0649 \u0635\u0641\u062d\u0629 \u0648\u064a\u0643\u064a \u0644\u0647\u0630\u0627 \u0627\u0644\u0623\u0644\u0628\u0648\u0645. + السيرة الذاتية + ويكي + تم رفض Audio focus. الألبوم الفنان النوع فنان الألبوم السنة - "مقطع (2 للمقطع 2 أو 3004 لـCD3 للمقطع 4)" - حقل اسم الألبوم أو اسم الفنان فارغ - يتم حفظ التغييرات... - حفظ إلى الملف... + "المقطع (2 للمقطع 2 أو 3004 لـCD3 للمقطع 4)" + كلمات الأغاني + حقل العنوان أو الفنان فارغ. + جار حفظ التغييرات التفاصيل اسم الملف مسار الملف الحجم الصيغة - المدة - معدل bit + الطول + معدل البت معدل الترميز - اذهب إلى الفنان - اذهب إلى الألبوم - قائمة الانتظار + الذهاب إلى الفنان + الذهاب إلى الألبوم + قائمة التشغيل لا توجد نتائج - يتم التحديث... - "تمت الإضافة إلى قائمة الانتظار" - تم إضافة %1$d عنوان إلى قائمة الانتظار - حذف من قائمة التشغيل + جار التحديث... + "تمت إضافة عنوان واحد إلى قائمة التشغيل." + تمت إضافة %1$d عناوين إلى قائمة التشغيل. + إزالة من قائمة التشغيل حجم الشبكة - عدد الحقول (في الوضع الأفقي) - تم إضافة %1$d أغنية إلى قائمة التشغيل %2$s - تم إنشاء قائمة التشغيل %1$s - تم حذف %1$d أغنية - \u0644\u0627 \u064a\u0645\u0643\u0646 \u0625\u0646\u0634\u0627\u0621 \u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u062a\u0634\u063a\u064a\u0644 - %1$s ؟]]> - %1$s \u061f]]> - %1$d ؟]]> - %1$s ؟]]> - %1$d ؟]]> - %1$s من قائمة التشغيل ؟]]> - %1$d الأغاني من قائمة التشغيل؟]]> + حجم الشبكة (في الوضع الأفقي) + تم إدراج %1$d أغاني إلى قائمة التشغيل %2$s. + تم إنشاء قائمة التشغيل %1$s. + تم حذف %1$d أغاني. + قائمة التشغيل %1$s موجودة بالفعل. + \u0644\u0645 \u064a\u062a\u0645 \u0627\u0644\u062a\u0645\u0643\u0646 \u0645\u0646 \u0625\u0646\u0634\u0627\u0621 \u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u062a\u0634\u063a\u064a\u0644. + %1$s؟]]> + %1$s\u061f \u0644\u0627 \u064a\u0645\u0643\u0646 \u0627\u0644\u062a\u0631\u0627\u062c\u0639 \u0639\u0646 \u0630\u0644\u0643!]]> + %1$d قوائم تشغيل؟]]> + %1$s؟]]> + %1$d أغاني؟]]> + %1$s من قائمة التشغيل؟]]> + %1$d أغاني من قائمة التشغيل؟]]> حذف الأغنية حذف الأغاني إزالة الأغنية من قائمة التشغيل @@ -79,134 +87,212 @@ حذف قوائم التشغيل مسح قائمة التشغيل حفظ كملف - "أضف إلى قائمة التشغيل" - بعثرة الكل - بعثرة الألبوم - تشغيل أغاني الفنان عشوائيا - تشغيل قائمة التشغيل عشوائيا - مسح قائمة الانتظار - اعرض كلمات الأغنية + حفظ كملفات + "إضافة إلى قائمة التشغيل" + خلط الكل + خلط الألبوم + خلط الفنان + خلط قائمة التشغيل + مسح قائمة التشغيل + حفظ قائمة التشغيل + الذهاب إلى مسار البداية + عرض كلمات الأغاني فاتح - داكن - أسود (شاشات AMOLED) + غامق + أسود (AMOLED) دائماً - فقط عبر Wi-Fi - "ليس للأبد " + فقط على Wi-Fi + أبداً + اليوم + هذا الأسبوع + هذا الشهر + الأشهر الثلاثة السابقة + هذه السنة المعادل الألوان - مشغل الآن - السمة العامة + يتم تشغيله الآن + التنسيق العام الصوت + المكتبة الصور - قفل الشاشة + شاشة القفل + قوائم التشغيل + الإشعار شريط تنقل ملون - أظهر صورة الألبوم - صورة األبوم شافة + اختصارات تطبيق ملونة + عرض غلاف الألبوم + تحميل البيانات الوصفية تلقائياً + تغييم صورة الألبوم إشعار ملون - تجاهل صور متجر الوسائط + تصميم الإشعار الكلاسيكي + تجاهل أغلفة مخزن الوسائط تشغيل بدون فواصل خفض الصوت عند فقدان التركيز + فترة قائمة تشغيل المضافة مؤخراً + عرض كلمات الأغاني المتزامنة + تذكر آخر لسان لا يوجد معدل صوت - "شغل مقطعاً، ثم حاول مجدداً" + "شغل أغنية أولاً، ثم حاول مرة أخرى." حذف مسح - حذف - إعادة التسمية + إزالة + إعادة تسمية إنشاء - %1$d محددة + إعادة تعيين + تم تحديد %1$d اللون الأساسي اللون الثانوي - تحديد لون أساسي. الأزرق النيلي هو اللون الافتراضي. - تحديد اللون الثانوي. الوردي هو اللون الثانوي الافتراضي. + لون التنسيق الأساسي، الافتراضي هو الأزرق النيلي. + لون التنسيق الثانوي، الافتراضي هو الزهري. تحديث الصورة - قائمة التشغيل فارغة + قائمة تشغيل فارغة لا توجد قوائم تشغيل لا توجد ألبومات لا توجد أغاني لا يوجد فنانون + لا توجد أنواع فارغ اسم قائمة التشغيل الأغنية - تستخدم ألبوم الأغاني الحالية كخلفية لشاشة القفل - تغييم صورة الألبوم في شاشة القفل. قد تحدث مشاكل مع الواجهات المبوبة و تطبييقات الطرف الثالث - "\u062a\u0644\u0648\u064a\u0646 \u0627\u0644\u0627\u0634\u0639\u0627\u0631\u0627\u062a \u0628\u0644\u0648\u0646 \u063a\u0644\u0627\u0641 \u0627\u0644\u0623\u0644\u0628\u0648\u0645." - "إزالة المقاطع التي الصامتة بين الاغنيتين. هذا يمكن ان يسبب مشاكل في التشغيل في بعض الاجهرة" - قد يرفع جودة صورة الألبوم ولكن يبطء من سرعة تحميلها. فعلها فقط إذا تواجه مشكلة في جودة صور الألبوم - تلوين شريط التنقل باللون الأساسي - الإشعارات، التنقل، إلى آخره. - "\u0644\u0627 \u064a\u0645\u0643\u0646 \u062a\u062d\u0645\u064a\u0644 \u063a\u0644\u0627\u0641 \u0623\u0644\u0628\u0648\u0645 \u0645\u0637\u0627\u0628\u0642 ." - إبحث في مكتبتك... + استعمال غلاف ألبوم الأغنية الحالية كخلفية لشاشة القفل. + تغييم غلاف الألبوم في شاشة القفل. قد يسبب مشاكل مع التطبيقات والأدوات الذكية من طرف ثالث. + استعمال تصميم الإشعار الكلاسيكي. + "\u062a\u0644\u0648\u064a\u0646 \u0627\u0644\u0625\u0634\u0639\u0627\u0631 \u0628\u0644\u0648\u0646 \u063a\u0644\u0627\u0641 \u0627\u0644\u0623\u0644\u0628\u0648\u0645 \u0627\u0644\u0628\u0627\u0631\u0632." + "قد يسبب مشاكل في التشغيل على بعض الأجهزة." + قد يرفع جودة أغلفة الألبومات لكنه يسبب بطئاً في زمن تحميل الصور. قم بتفعيل هذا الخيار إذا كانت لديك مشاكل مع الصور منخفضة الدقة. + تلوين شريط التنقل باللون الأساسي. + تلوين اختصارات التطبيق باللون الأساسي. + الإشعارات، التنقل، إلخ. + حالياً كلمات الأغاني المتزامنة بصيغة LRC هي المدعومة فقط. سواء كانت مدمجة أو كملف منفصل. + الذهاب إلى آخر لسان مفتوح عند التشغيل + "\u0644\u0645 \u064a\u062a\u0645 \u0627\u0644\u062a\u0645\u0643\u0646 \u0645\u0646 \u062a\u062d\u0645\u064a\u0644 \u063a\u0644\u0627\u0641 \u0623\u0644\u0628\u0648\u0645 \u0645\u0637\u0627\u0628\u0642." + البحث في مكتبتك... المفضلة - آخر ما تمت إضافته + المضافة مؤخراً السجل أفضل الأغاني إزالة الغلاف - التنزيل من Last.fm + التحميل من Last.fm اختيار من التخزين الداخلي البحث في الويب مؤقت النوم ضبط إلغاء المؤقت الحالي تم إلقاء مؤقت النوم - تم ضبط مؤقت النوم لـ %d دقيقة من الآن. - قائمة تشغيل جديدة ... + تم ضبط مؤقت النوم لـ%d دقائق من الآن. + قائمة تشغيل جديدة... قائمة تشغيل جديدة هوامش ملونة شكر خاص إلى سجل التغييرات - الرخص - أذونات مرفوضة - تم رفض الإذن للوصول إلى الذاكرة الخارجية + التراخيص + تم رفض الصلاحيات. + تم رفض صلاحية الوصول إلى الذاكرة الخارجية. دعم التطوير + شراء Phonograph Pro شكراً لك! - النسخة + تمت استعادة عملية الشراء السابقة. يرجى إعادة تشغيل التطبيق للاستفادة من جميع المميزات. + الإصدار المؤلف - أضف إلى دوائر جوجل + - تابع في تويتر - زر موقعنا - أبلغ عن وجود مشكلة - أبلغ عن وجود مشاكل أو طلب ميزات جديدة - انضم لمجموعة جوجل + - إذا كنت تحتاج إلى مساعدة أو لديك أسئلة؛ مجتمع Phonograph في جوجل+ هو المكان المناسب لطرحها + كتابة بريد إلكتروني + متابعة على تويتر + Fork على GitHub + زيارة الموقع + الإبلاغ عن مشاكل + الإبلاغ عن مشاكل أو طلب ميزات جديدة. ترجمة - ساعدنا في ترجمة Phonograph إلى لغتك الأم + ساعد في ترجمة Phonograph إلى لغتك الأم. قيّم - اترك تقييماً إيجابياً في متجر جوجل بلاي إذا أعجبك التطبيق. - تبرع لنا - إذا كنت ترى أنني أستحق أن أحصل على جزاء مقابل عملي، يمكنك أن تكافئني ببضعة دولارات هنا. - من أجل محرك السمات وميزات جميلة أخرى - من أجل شعار التطبيق المطابق لفلسفة ماتيريال ديزاين - من أجل وضع توضيحات البرنامج المتواجدة على المتجر وأيضاً صورة الألبوم الإفتراضية - لمساعدتي في تصميم البرنامج + اترك تقييماً إيجابياً على Goole Play إذا أعجبك Phonograph. + تبرع + إذا كنت تعتقد أنني أستحق أجراً على عملي، يمكنك أن تترك لي بضع دولارات هنا. + من أجل محرك التنسيقات وبعض الأشياء الرائعة الأخرى. + من أجل أيقونة التطبيق الجميلة بتصميم الماتيريال. + من أجل الرسوم التوضيحية على متجر Play وغلاف الألبوم الفارغ. + من أجل مساعدته لي في التصميم. الموقع - يتم تحميل المنتجات... + جار تحميل المنتجات... التالي المظهر المقدمة - "مرحباً في Phonograph، مشغل موسيقى خفيف وأنيق على منصة أندرويد." - اسحب البطاقة في شاشة \"تستمع الآن\" لإظهار جميع المقطوعات على قائمة الانتظار - إعادة ترتيب القائمة بواسطة سحب المقطوعة من رقمها + "مرحباً بك في Phonograph، مشغل موسيقى جميل وخفيف للأندرويد. " + اسحب البطاقة في شاشة \"يتم تشغيله الآن\" لإظهار قائمة التشغيل بالكامل. + إعادة ترتيب قائمة التشغيل عبر سحب الأغاني من أرقامها. المكتبة - الملفات - تم الحفظ في قائمة التشغيل %s. - فشل الحفظ في القائمة التشغيل (%s). + المجلدات + تم حفظ قائمة التشغيل إلى %s. + فشل حفظ قائمة التشغيل (%s). + تم حفظ %1$d قوائم تشغيل إلى %2$s. + تم حفظ %1$d قوائم تشغيل إلى %2$s، فشل حفظ %3$d. + %s غير مدرج في مخزن الوسائط.]]> + بعض الملفات غير مدرجة في مخزن الوسائط. لا يوجد شيء للفحص - لم يتم فحص ملفات %d. - بلغ عن مشكلة + تم فحص %1$d من %2$d ملفات. + تعذر فحص %d ملفات. + جار إدراج الملفات + %s هو مسار البداية الجديد. + Phonograph - كبير + Phonograph - كلاسيكي + Phonograph - صغير + Phonograph - بطاقة + إبلاغ عن مشكلة المشكلة تسجيل دخول العنوان الوصف معلومات الجهاز - أرسل باستخدام حساب GitHub - أرسل يدوياً + إرسال باستخدام حساب GitHub + إرسال يدوياً اسم المستخدم كلمة المرور - فضلاً أدخل عنوان المشكلة - فضلاً أدخل وصف المشكلة - فضلاً أدخل اسم مستخدم صالح لحساب GitHub - فضلاً أدخل كلمة مرور صالح لحساب GitHub - يتم رفع المشكلة إلى GitHub + يرجى إدخال عنوان المشكلة + يرجى إدخل وصف المشكلة + يرجى إدخال اسم مستخدم GitHub صالح + يرجى إدخال كلمة مرور GitHub صالحة + جار رفع البلاغ إلى GitHub... غير قادر علي إرسال تقرير - اسم مستخدم او كلمة مرور خاطئه + اسم مستخدم أو كلمة مرور خاطئة + رمز وصول غير صالح. يرجى التواصل مع مطور التطبيق. + لم يتم تفعيل الأخطاء للمستودع المختار. يرجى التواصل مع مطور التطبيق. + حدث خطأ غير متوقع. يرجى التواصل مع مطور التطبيق. + تم نسخ بيانات الجهاز إلى الحافظة. + يتم استعمال بيانات حسابك للمصادقة فقط. + سيتم توجيهك إلى موقع تعقب الأخطاء. + خلط + أفضل الأغاني + المضافة مؤخراً + قائمة التشغيل فارغة + إشعار التشغيل يوفر أزرار تحكم للتشغيل/الإيقاف المؤقت إلخ. + إشعار التشغيل + فقط أول 5 ألوان هي المتوفرة في النسخة المجانية. + التنسيق الأسود هي ميزة في Phonograph Pro. + مؤقت النوم هي ميزة في Phonograph Pro + عرض المجلدات هي ميزة في Phonograph Pro. + جار استعادة عملية الشراء... + لم يتم التمكن من استعادة عملية الشراء. + شراء + استعادة + لم يتم العثور على عملية شراء. + من أجل مساهماته في الكود المصدري. + من أجل إنشاءه تصميم صفحة الألبومات. + إضافة + القائمة السوداء + إزالة من القائمة السوداء + %1$s من القائمة السوداء؟]]> + مسح القائمة السوداء + هل تريد مسح القائمة السوداء؟ + سيتم إخفاء محتوى المجلدات التي في القائمة السوداء من مكتبتك. + إعادة تعيين صورة الفنان + تعيين صورة الفنان + أقسام المكتبة + تخصيص رؤية وترتيب أقسام المكتبة. + عليك أن تختار قسماً واحداً على الأقل. + فحص المسار + فحص الوسائط + ترتيب الفرز + تصاعدي + تنازلي + الفنان + الألبوم + السنة diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 6de0b0bb6..667d3028b 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -45,7 +45,6 @@ Текст на песента Заглавието или артистът са празни Запазване на промените - Запазване като файл... Детайли Име на файл Местоположение на файла @@ -169,13 +168,10 @@ Благодаря Ви! Версия Автор - Добави в Гугъл Плюс Последвай в Twitter Посети уебсайт Съобщи за бъгове Съобщи за бъгове или предложи нови идеи - Присъедини се към обществото в Гугъл Плюс - Ако имате нужда от помощ или някакви въпроси, посете обществото в Гугъл Плюс Преведи Преведете Phonograph на вашият роден език. Оцени diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 9ec9d7887..58e5d0e45 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -47,8 +47,7 @@ "Stopa (2 pro stopu 2; 3004 pro stopu 4 na CD3)" Text Název alba nebo interpreta je prázdný. - Ukládání změn... - Ukládání do souboru... + Ukládání změn Podrobnosti Název souboru Umístění souboru @@ -67,33 +66,35 @@ Odstranit z playlistu Velikost mřížky Velikost mřížky (na šiřku) - Vložit %1$d do playlistu %2$s. + Vloženo %1$d do playlistu %2$s. Vytvořen playlist %1$s Odstraněno %1$d skladeb. Seznam skladeb %1$s již existuje. Nelze vytvo\u0159it playlist. - %1$s ?]]> + %1$s ?]]> %1$s? Tato operace je nevratn\u00e1!]]> - %1$d playlistů?]]> - %1$s ? ]]> - %1$d skladeb?]]> - %1$s z playlistu?]]> - %1$d skladeb z playlistu?]]> + %1$d playlistů?]]> + %1$s ?]]> + %1$d skladeb?]]> + %1$s z playlistu?]]> + %1$d skladeb z playlistu?]]> Vymazat skladbu - Smazat skladby + Vymazat skladby Odstranit skladbu z playlistu Odstranit skladby z playlistu Přejmenovat playlist Smazat playlist - Smazat playlisty - Vyčistit playlisty + Vymazat playlisty + Vyčistit playlist Uložit jako soubor + Uložit jako soubor "Přidat do playlistu" Přehrát náhodně Přehrát album náhodně Přehrát skladby interpreta náhodně Přehrát playlist náhodně Vyčistit frontu přehrávání + Uložit frontu přehrávání Jít na domovskou složku Zobrazit text Světlý @@ -119,7 +120,7 @@ Oznámení Barevné záhlaví Barevné zkratky aplikace - Obal alba na odemykací obrazovce + Zobrazit obal alba Automaticky stahovat metadata Rozostřit obal alba Barevné notifikace @@ -127,7 +128,9 @@ Ignorovat obaly Media Storu Přehrávání bez mezer Snížit hlasitost při ztrátě priority zvuku + Poslední přidaný interval playlistu Zobrazit synchronizovaný text + Pamatovat poslední kartu "Nenalezen žádný ekvalizér. " "Nejdříve přehrajte píseň, poté zkuste znovu. " Smazat @@ -135,6 +138,7 @@ Odstranit Přejmenovat Vytvořit + Resetovat %1$d vybrána Hlavní barva Vedlejší barva @@ -150,24 +154,26 @@ Prázdné Pojmenování seznamu skladeb Skladba - Obaly alb se budou zobrazovat na zamykací obrazovce. Pro aplikaci může být nutné aplikaci restartovat. + Používá aktuální obal alba jako tapetu zamykací obrazovky. Rozostří obal alba na uzamčené obrazovce. Může být problematické s aplikacemi a widgety třetích stran. Použije klasický vzhled oznámení. - "Notifikace jsou zbarveny podle obalu alba." - "Odstraní ticho mezi skladbami. Deaktivace může vyřešit problémy s přehráváním." + "Zbarv\u00ed ozn\u00e1men\u00ed do barev podle obalu alba." + "Může způsobit problémy s přehráváním u některých zařízení." "Může zvýšit kvalitu obalu alba, ale způsobí pomalejší načítání. Povolte pouze pokud máte problémy s nízkým rozlišením obalu. " - Zvolte, kde se má zobrazovat barevné záhlaví. + Barvy navigačního panelu v primární barvě Zbarví zkratky aplikace primární barvou Oznámení, navigace atd. - "Nelze st\u00e1hnout obal alba." + V současné době jsou podporovány pouze synchronizované texty ve formátu LRC. Buď vestavěný nebo jako samostatný soubor. + Přejděte na poslední otevřenou kartu při spuštění + "Nelze st\u00e1hnout odpov\u00eddaj\u00edc\u00ed obal alba." Vyhledat zvukové soubory... Oblíbené - Naposledy přidané + Naposledy přidáno Historie "Moje nejposlouchanější skladby " Odstranit obal Stáhnout z Last.fm - Vybrat ze souborů + Vybrat z místního úložiště Najít na webu Určit ukončení přehrávání Nastavit @@ -185,16 +191,15 @@ Podpořte vývoj Zakoupit Phonograph Pro "Díky! " + Předchozí nákup byl obnoven. Restartujte aplikaci pro využití všech funkcí. Verze Autor Napsat e-mail - Přidat do kruhů Google Plus Sledovat na Twitteru + Fork na GitHubu Navštívit stránku Ohlásit chybu "Nahlásit chyby nebo požádat o nové funkce. " - Přidejte se ke komunitě Google Plus - Pokud potřebujete pomoc nebo máte otázky, komunita Phonographu na Google Plus je to správné místo. Překládejte "Pomozte nám přeložit Phonograph do vašeho mateřského jazyka. " Ohodnotit @@ -217,7 +222,10 @@ "Složky " Uloženo do playlistu %s. Chyba v uložení playlistu (%s). + Uloženo %1$d playlistů do %2$s. + Uloženo %1$d playlistů do %2$s, nepodařilo se uložit %3$d. %s není zobrazeno v seznamu souborů.]]> + Některé soubory nejsou zobrazeny v obchodě s médii. Nic ke skenování. Naskenováno %1$d z %2$d souborů. Nelze skenovat %d souborů. @@ -226,6 +234,7 @@ Phonograph - Velký Phonograph - Klasický Phonograph - Malý + Phonograph - Karta Nahlásit problém Problém Přihlášení @@ -250,16 +259,40 @@ Vaše údaje k účtu jsou použity pouze k ověření. Budete přesměrováni na webovou stránku pro zadání problému. Náhodně - Nejposlouchanější Skladby - Posledně přidané + Nejposlouchanější skladby + Naposledy přidáno Playlist je prázdný + Oznámení o přehrávání obsahuje akce pro přehrávání/pauzu atd. + Oznámení o přehrávání Ve verzi zdarma je dostupných pouze prvních 5 barev. + Černý motiv je funkce Phonograph Pro. + Časovač vypnutí je funkce Phonograph Pro. + Prohlížení složek je funkce Phonograph Pro. + Obnovení nákupu... + Nákup nelze obnovit. + Zakoupit + Obnovit + Nebyl nalezen žádný nákup. + Pro příspěvky ke zdrojovému kódu. + Pro vytvoření návrhu stránky alba. Přidat + Blacklist Odstranit z blacklistu %1$s z blacklistu?]]> Vyčistit blacklist Chcete vyčistit blacklist? Obsah složek na blacklistu se nezobrazuje v hudební knihovně. + Obnovit obrázek interpreta Nastavit obrázek interpreta + Kategorie knihovny + Nastavit viditelnost a pořadí kategorií knihoven. Musíte zvolit alespoň jednu kategorii. + Skemování adresáře + Skenování médií + Seřazení + Vzestupně + Sestupně + Interpret + Album + Rok diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 64227c288..59f257dc4 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -48,7 +48,6 @@ Songtext Der Titel oder der Interpret ist leer. Speichere Änderungen… - In Datei speichern... Details Dateiname Dateipfad @@ -88,7 +87,9 @@ Wiedergabelisten löschen Wiedergabeliste leeren Als Datei speichern + Als Dateien speichern "Zu Wiedergabeliste hinzufügen" + Alles auswählen Zufällige Wiedergabe Album zufällig wiedergeben Interpret zufällig wiedergeben @@ -105,6 +106,7 @@ Niemals Heute Diese Woche + Letzte 7 Tage Diesen Monat Letzte 3 Monate Dieses Jahr @@ -131,6 +133,7 @@ Intervall der \"Zuletzt hinzugefügt\"-Wiedergabeliste Synchronisierten Songtext anzeigen Letzten Tab merken + Shuffle-Einstellung merken Kein Equalizer gefunden. "Keine Audio ID, spiele etwas ab und versuche es erneut." Löschen @@ -165,6 +168,7 @@ Benachrichtigungen, Navigation etc. Aktuell werden nur synchronisierte Songtexte im LRC-Format unterstützt, entweder eingebettet oder als separate Datei. Zuletzt geöffneten Tab beim Start anzeigen + Shuffle bleibt aktiviert, wenn neue Songs ausgewählt werden "Es konnte kein passendes Album Cover heruntergeladen werden." Durchsuche deine Mediathek Favoriten @@ -195,14 +199,11 @@ Version Autor Eine E-Mail schreiben - Zu Google Plus Kreisen hinzufügen Auf Twitter folgen Auf GitHub forken Website besuchen Fehler melden Melde Fehler oder schlage neue Features vor. - Google Plus Community beitreten - Wenn du Hilfe brauchst oder eine Frage hast, bist du bei der Phonograph Community an der richtigen Adresse. Übersetzen Helfe dabei, Phonograph in deine Sprache zu übersetzen. Bewerten @@ -225,7 +226,10 @@ Ordner Playlist wurde in %s gespeichert. Speichern der Playlist fehlgeschlagen (%s). + %1$d Wiedergabelisten wurden in %2$s gespeichert. + %1$d Wiedergabelisten wurden in %2$s gespeichert, %3$d konnten nicht gespeichert werden. %s ist nicht im Media Store gelistet.]]> + Einige Dateien sind nicht im Medienverzeichnis gelistet. Nichts zu scannen. %1$d von %2$d Dateien gescannt. %d Dateien konnten nicht gescannt werden. @@ -274,6 +278,7 @@ Wiederherstellen Kein Kauf gefunden. Für seinen Beitrag zum Quellcode. + Für die Erstellung des Designs der Album-Seite. Hinzufügen Blacklist Von Blacklist entfernen @@ -286,4 +291,13 @@ Bibliothekskategorien Anzeige und Anordnung der einzelnen Kategorien festlegen. Mindestens eine Kategorie muss ausgewählt sein. + Verzeichnis scannen + Medien scannen + Sortierung + Aufsteigend + Absteigend + Interpret + Album + Jahr + Letzten Song beenden diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 8fa7484b3..ac9d6c956 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -15,7 +15,7 @@ Αναζήτηση Επόμενο Αναπαραγωγή - Αναπ.γή/Παύση + Αναπαραγωγή/Παύση Προηγούμενο Επόμενο Προσθήκη στη σειρά τραγουδιών @@ -30,11 +30,14 @@ Ορισμός ως κατάλογος έναρξης Άλμπουμ Καλλιτέχνες + Είδη Τραγούδια Λίστες αναπαραγωγής \u03a3\u03c5\u03b3\u03b3\u03bd\u03ce\u03bc\u03b7 - \u03a5\u03c0\u03ae\u03c1\u03be\u03b5 \u03c3\u03c6\u03ac\u03bb\u03bc\u03b1 \u03c3\u03c4\u03b7\u03bd \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae\u03c2 \u03a3\u03c5\u03b3\u03b3\u03bd\u03ce\u03bc\u03b7, \u03b4\u03b5\u03bd \u03bc\u03c0\u03bf\u03c1\u03bf\u03cd\u03bc\u03b5 \u03c4\u03b7 \u03b2\u03b9\u03bf\u03b3\u03c1\u03b1\u03c6\u03af\u03b1 \u03b1\u03c5\u03c4\u03bf\u03cd \u03c4\u03bf\u03c5 \u03ba\u03b1\u03bb\u03bb\u03b9\u03c4\u03ad\u03c7\u03bd\u03b7 + \u0394\u03b5\u03bd \u03b2\u03c1\u03ad\u03b8\u03b7\u03ba\u03b5 \u03ba\u03b1\u03c4\u03b1\u03c7\u03ce\u03c1\u03b7\u03c3\u03b7 wiki \u03b3\u03b9\u03b1 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03ac\u03bb\u03bc\u03c0\u03bf\u03c5\u03bc. Βιογραφία + Online εγκυκλοπαίδεια Έγινε άρνηση εστίασης ήχου Άλμπουμ Καλλιτέχνης @@ -45,7 +48,6 @@ Στίχοι Το πεδίο \"Τίτλος\" ή \"Καλλιτέχνης\" είναι άδειο "Γίνεται αποθήκευση αλλαγών... " - Αποθήκευση στο αρχείο... Λεπτομέρειες Όνομα αρχείου Διαδρομή αρχείου @@ -67,6 +69,7 @@ Προστέθηκαν %1$d τραγούδια στην λίστα αναπαραγωγής%2$s Δημιουργήθηκε η λίστα αναπαραγωγής %1$s Διεγράφησαν %1$d τραγούδια + Η λίστα αναπαραγωγής %1$s υπάρχει ήδη. \u0394\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b4\u03c5\u03bd\u03b1\u03c4\u03ae \u03b7 \u03b4\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03bb\u03af\u03c3\u03c4\u03b1\u03c2 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae\u03c2 %1$s;]]> %1$s ; (\u039c\u03b7 \u03b1\u03bd\u03b1\u03c3\u03c4\u03c1\u03ad\u03c8\u03b9\u03bc\u03bf!)]]> @@ -85,12 +88,14 @@ Διαγραφή λιστών αναπαραγωγής Εκκαθάριση λίστας Αποθήκευση ως αρχείο + Αποθήκευση ως αρχεία "Προσθήκη στη λίστα αναπαραγωγής" Τυχαία τραγούδια Τυχαίο άλμπουμ Τυχαίος καλλιτέχνης Τυχαία λίστα αναπαραγωγής Εκκαθάριση της ουράς αναπαραγωγής + Αποθήκευση της σειράς αναπαραγωγής Πηγαίνετε στον κατάλογο έναρξης Εμφάνιση στίχων Ανοιχτό @@ -99,19 +104,29 @@ Πάντα Μόνο σε Wi-Fi Ποτέ + Σήμερα + Αυτή την εβδομάδα + Αυτόν τον μήνα + Τους περασμένους 3 μήνες + Αυτόν τον χρόνο Ισοσταθμιστής Χρώματα - Γίνεται αναπαραγωγή + Παίζει τώρα Γενικό θέμα Ήχος + Βιβλιοθήκη Εικόνες Οθόνη κλειδώματος + Λίστες αναπαραγωγής + Ειδοποιήσεις Έγχρωμη μπάρα πλοήγησης Χρωματιστές συντομεύσεις εφαρμογής Εμφάνιση album art + Αυτόματο κατέβασμα metadata Θάμπωμα πορτρέτων καλλιτεχνών - Χρωματιστή πλοήγηση - Παράληψη των album art στην SD + Χρωματισμένη ειδοποίηση + Κλασικός σχεδιασμός ειδοποιήσεων + Παράληψη των Media Store εξωφύλλων. Εναλλαγή χωρίς παύση Μειώστε την ένταση στην απώλεια εστίασης Δε βρέθηκε ισοσταθμιστής @@ -136,7 +151,7 @@ Όνομα λίστας Τραγούδι Εμφάνιση του album art στην οθόνη κλειδώματος. - Εμφανίζει,θωριασμένα, το album art στην οθόνη κλειδώματος.Μπορεί να δημιουργίσει πρόβλημα με εφαρμογές Τρίτων. + Το θωριασμένο album εξώφυλλο στην οθόνη κλειδώματος μπορεί να δημιουργήσει προβλήματα με τρίτες εφαρμογές και συντομιεύσεις. "\u03a7\u03c1\u03c9\u03bc\u03b1\u03c4\u03af\u03b6\u03b5\u03b9 \u03c4\u03b7\u03bd \u03bc\u03c0\u03ac\u03c1\u03b1 \u03b5\u03b9\u03b4\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c9\u03bd \u03c3\u03c4\u03bf \u03c7\u03c1\u03ce\u03bc\u03b1 \u03c4\u03bf\u03c5 album art \u03c4\u03bf\u03c5 \u03c4\u03c1\u03b1\u03b3\u03bf\u03c5\u03b4\u03b9\u03bf\u03cd" "Μπορεί να δημιουργίσει θέματα αναπαραγωγής σε μερικές συσκευές." Μπορεί να αυξήσει την ποιότητα του εξώφυλλου του άλμπουμ, αλλά προκαλεί αργούς χρόνους φόρτωσης της εικόνας. Ενεργοποιήστε το μόνο αν έχετε προβλήματα με εξώφυλλα χαμηλής ανάλυσης. @@ -170,13 +185,10 @@ Σας ευχαριστώ! Έκδοση Εκδότης - Προσθήκη στους κύκλους σας στο Google Plus Ακολουθήστε στο Twitter Επισκεφθείτε την ιστοσελίδα μας Αναφορά σφαλμάτων Αναφέρετε σφάλματα ή ζητείστε νέα χαρακτηριστικά. - Γίνετε μέλος της κοινότητας μας στο Google Plus - Αν χρειάζεστε βοήθεια ή έχετε ερωτήσεις, η κοινότητά μας για το Phonograph στο Google Plus είναι ένα καλό μέρος για να πάτε. Μεταφράστε Βοηθήστε την μετάφραση του Phonograph στη μητρική σας γλώσσα. Βαθμολογήστε @@ -199,7 +211,10 @@ Φάκελοι Η λίστα αναπαραγωγής αποθηκεύτηκε στο %s. Η λίστα αναπαραγωγής απέτυχε να αποθηκευτεί (%s). + Αποθηκεύτηκαν %1$d λίστες αναπαραγωγής σε %2$s. + Αποθηκεύτηκαν %1$d λίστες αναπαραγωγής σε %2$s, αποτυχία αποθήκευσης %3$d. %s δεν είναι καταχωρημένο στο κατάστημα.]]> + Μερικά αρχεία δεν έχουν κατανεμηθεί στο κατάστημα αρχείων. Δεν υπάρχει κάτι για σάρωση. Έγινε σάρωση σε %1$d από %2$d αρχεία. Δεν μπόρεσε να πραγματοποιηθεί σάρωση σε %d αρχεία. diff --git a/app/src/main/res/values-en-rCA/strings.xml b/app/src/main/res/values-en-rCA/strings.xml index 7c68beec1..85be01391 100644 --- a/app/src/main/res/values-en-rCA/strings.xml +++ b/app/src/main/res/values-en-rCA/strings.xml @@ -47,7 +47,6 @@ Lyrics The title or artist is empty. Saving changes - Saving to file… Details File name File path @@ -189,14 +188,11 @@ Version Author Write an email - Add to Google Plus circles Follow on Twitter Fork on GitHub Visit website Report bugs Report bugs or request new features. - Join the Google Plus community - If you need help or have questions, the Phonograph community on Google Plus is a good place to go. Translate Help translating Phonograph to your native language. Rate diff --git a/app/src/main/res/values-en-rGB/strings.xml b/app/src/main/res/values-en-rGB/strings.xml index 572809583..bdd6daa9a 100644 --- a/app/src/main/res/values-en-rGB/strings.xml +++ b/app/src/main/res/values-en-rGB/strings.xml @@ -30,6 +30,7 @@ Set as start directory Albums Artists + Genres Songs Playlists Couldn\'t play this song. @@ -47,7 +48,6 @@ Lyrics The title or artist is empty. Saving changes - Saving to file... Details File name File path @@ -112,6 +112,7 @@ Now playing General theme Audio + Library Images Lockscreen Playlists @@ -128,6 +129,7 @@ Reduce volume on focus loss Last added playlist interval Show synchronised lyrics + Remember last tab No equaliser found. "Play a song first, then try again." Delete @@ -135,6 +137,7 @@ Remove Rename Create + Reset %1$d selected Primary colour Accent colour @@ -146,6 +149,7 @@ No albums No songs No artists + No genres Empty Playlist name Song @@ -159,6 +163,7 @@ Colours the app shortcuts in the primary colour. Notifications, navigation etc. Currently only synchronised lyrics in LRC format are supported. Either embedded or as a separate file. + Go to the last opened tab on launch "Couldn\u2019t download a matching album cover." Search your library… Favourites @@ -189,14 +194,11 @@ Version Author Write an email - Add to Google Plus circles Follow on Twitter Fork on GitHub Visit website Report bugs Report bugs or request new features. - Join the Google Plus community - If you need help or have questions, the Phonograph community on Google Plus is a good place to go. Translate Help translating Phonograph to your native language. Rate @@ -277,4 +279,7 @@ The content of blacklisted folders is hidden from your library. Reset artist image Set artist image + Library categories + Configure visibility and order of library categories. + You have to select at least one category. diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index d0c922e66..d3b0b3e85 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -48,7 +48,6 @@ Letras El título o artista está vacío Guardando cambios... - Guardando en el archivo... Detalles Nombre del archivo Ubicación del archivo @@ -77,7 +76,7 @@ %1$d listas de reproducción?]]> %1$s?]]> %1$d canciones?]]> - %1$s de la lista de reproducción?]]> + ]]> %1$d canciones de la lista de reproducción?]]> Eliminar canción Eliminar canciones @@ -88,7 +87,9 @@ Eliminar listas de reproducción Vaciar lista de reproducción Guardar como archivo + Español "Añadir a lista de reproducción" + Seleccionar todo Modo aleatorio Reproducción aleatoria del álbum Reproducción aleatoria del artista @@ -105,6 +106,7 @@ Nunca Hoy Esta semana + Últimos 7 días Este mes Últimos 3 meses Este año @@ -113,6 +115,7 @@ Reproducción actual Tema general Audio + Biblioteca Imágenes Pantalla de bloqueo Listas de reproducción @@ -129,6 +132,8 @@ Reducir volumen en la pérdida del enfoque Intervalo de la última lista de reproducción Mostrar letras sincronizadas + Recordar la última pestaña + Recordar aleatorio No se encontró ningún ecualizador "Reproduce una canción primero, luego intentalo otra vez." Borrar @@ -136,6 +141,7 @@ Eliminar Renombrar Crear + Reiniciar %1$d seleccionado Color primario Color secundario @@ -161,6 +167,8 @@ Colorea los accesos directos con el color principal Notificaciones, navegación etc. Actualmente sólo se admiten las letras sincronizadas en formato LRC. Tanto si están embebidas como si están en un archivo aparte. + Ir a la última pestaña abierta en el lanzamiento + Modo aleatorio estará activado cuando se seleccione una nueva lista de canciones "No se puede descargar una car\u00e1tula de \u00e1lbum que coincida." Buscar en la biblioteca... Favoritos @@ -191,14 +199,11 @@ Versión Autor Escribir un email - Añadir a círculos en Google Plus Seguir en Twitter Fork en GitHub Visitar sitio web Reportar errores Reportar errores o sugerir nuevas características. - Unirse a la comunidad de Google Plus - Si necesitas ayuda o tienes preguntas, la comunidad Phonograph en Google Plus es un buen lugar para ir. Traducir Ayuda traduciendo Phonograph a tu idioma. Valorar @@ -221,7 +226,10 @@ Carpetas Lista de reproducción guardada en %s. No se pudo guardar la lista de reproducción (%s). + %1$d de %2$s listas de reproducción guardadas. + Guardada %1$d de %2$s listas de reproducción, fallo al guardar la %3$d. %s no se encuentra listado en los medios almacenados]]> + Español Nada para analizar %1$d de %2$d archivos analizados No se pudieron analizar los archivos %d @@ -255,7 +263,7 @@ La información de tu cuenta se usa solamente para la autenticación Será redirigido a la página web del buscador de problemas. Mezclar - Los mejores temas + Canciones más reproducidas Último agregado Lista de reproducción vacía La notificación de reproducción muestra acciones para reproducir/pausar etc. @@ -270,6 +278,7 @@ Restablecer No purchase found. Por sus contribuciones al código fuente + Para crear el diseño de la página del álbum. Añadir Lista Negra Remover de la lista negra @@ -280,4 +289,16 @@ El contenido de las carpetas de la lista negra está oculto en tu biblioteca" Resetear la imagen del artista Establecer la imagen del artista + Categorías de la biblioteca + Configure la visibilidad y el orden de las categorías de la biblioteca. + Debes seleccionar al menos una categoría. + Escanear directorio + Escanear medios + Orden de clasificación + Ascendente + Descendente + Artista + Álbum + Año + Terminar la última canción diff --git a/app/src/main/res/values-es-rUS/strings.xml b/app/src/main/res/values-es-rUS/strings.xml index 2ab5c8ecd..743674e8a 100644 --- a/app/src/main/res/values-es-rUS/strings.xml +++ b/app/src/main/res/values-es-rUS/strings.xml @@ -48,7 +48,6 @@ Letras El título o artista está vacío. Guardando cambios... - Guardando al archivo... Detalles Nombre del archivo Ubicación del archivo @@ -88,6 +87,7 @@ Eliminar listas de reproducción Limpiar lista de reproducción Guardar como archivo + Guardar como archivos "Agregar a la lista de reproducción" Modo aleatorio Reproducción aleatoria del álbum @@ -195,14 +195,11 @@ Versión Autor Escribir un email - Añadir a círculos de Google+ Seguir en Twitter Fork en GitHub Visitar sitio web Reportar errores Reportar errores o sugerir nuevas características. - Unirse a la comunidad de Google+ - Si necesitas ayuda o tienes preguntas, la comunidad Phonograph en Google+ es un buen lugar para ir. Traducir Ayuda traduciendo Phonograph a tu idioma. Calificar @@ -225,7 +222,9 @@ Carpetas Lista de reproducción guardada en %s. No se pudo guardar lista de reproducción (%s). + %1$d de %2$s listas de reproducción guardadas. %s no se encuentra listado en los medios almacenados.]]> + Algunos archivos no se encuentran listados en los medios almacenados. Nada para escanear. %1$d de %2$d archivos escaneados. No se pudieron escanear los archivos %d. @@ -274,6 +273,7 @@ Restaurar No se encontró la compra. Por sus contribuciones al código fuente. + Por crear el diseño para la ventana del álbum. Agregar Lista negra Remover de Lista negra @@ -283,5 +283,15 @@ El contenido de las carpetas de Lista negra está oculto de tu librería. Reiniciar imagen de Artista Establecer imagen de Artista + Categorías de la biblioteca + Configurar la vista y orden de las categorías de la biblioteca. Tienes que seleccionar al menos una categoría. + Escanear directorio + Escanear medios + Ordenar por + Ascendente + Descendente + Artista + Álbum + Año diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 08b83e27b..42a2b4f31 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -8,11 +8,11 @@ Jaa "Asetukset" Myönnä - Tietoja + Tietoa Tyhjennä soittolista Lisää suosikkeihin Poista suosikeista - Etsi + Hae Toista seuraavaksi Toista Toista/Pysäytä @@ -27,7 +27,7 @@ Nimeä uudelleen Poista Skannaa - Aseta aloitushakemistoksi + Aseta aloituskansioksi Albumit Artistit Tyylilajit @@ -35,6 +35,7 @@ Soittolistat Virhe toistettaessa kappaletta. El\u00e4m\u00e4nkertaa ei l\u00f6ytynyt t\u00e4lle artistille. + Ei l\u00f6ytynyt wikist\u00e4 kohtaa t\u00e4lle albumille. Elämänkerta Wiki Ääntä ei voitu toistaa. @@ -43,11 +44,10 @@ Tyylilaji Albumin artisti Vuosi - "Raita(2 raidalle 2 tai 3004 CD3 raidalle 4)" + "Raita" Sanoitukset Nimi- tai artistikenttä on tyhjä. - Tallennetaan muutokset... - Tallennetaan tiedostoon... + Tallennetaan muutokset Yksityiskohdat Tiedostonimi Tiedostopolku @@ -87,14 +87,16 @@ Poista soittolistat Tyhjennä soittolistat Tallenna tiedostona + Tallenna tiedostoina "Lisää soittolistaan" + Valitse kaikki Sekoita kaikki Sekoita albumit Sekoita artistit Sekoita soittolistat Tyhjennä toistojono Tallenna toistojono - Mene aloitushakemistoon + Mene aloituskansioon Näytä lyriikat Vaalea Tumma @@ -104,6 +106,7 @@ Ei koskaan 1 päivä 1 viikko + Edelliset 7 päivää 1 kuukausi 3 kuukautta 1 vuosi @@ -117,17 +120,20 @@ Lukitusnäyttö Soittolistat Ilmoitukset - Värillinen navigointipalkki + Värillinen navigaatiopalkki + Värilliset sovelluksen pikavalinnat Näytä albumin kansi Metatietojen automaattinen lataus Sumenna albumin kansi Värillinen ilmoitus + Klassinen ilmoituksen ulkonäkö Sivuuta Media Storen albumikannet Tauoton toisto Hiljennä ääntä, kun kohdistus menetetään Viimeksi lisätyt -soittolistan aikaväli Näytä synkronoidut sanoitukset Muista viimeisin välilehti + Muista sekoitus Taajuuskorjainta ei löytynyt. "Toista jokin kappale ensin, koita sitten uudelleen." Poista @@ -135,6 +141,7 @@ Poista Nimeä uudelleen Luo + Nollaa %1$d valittu Ensisijainen väri Aksenttiväri @@ -152,18 +159,21 @@ Kappale Käyttää tämänhetkisen kappaleen albumin kantta lukitusnäytön taustakuvana. Sumentaa albumin kannen lukitusnäytöllä. Saattaa aiheuttaa ongelmia kolmannen osapuolen sovellusten ja widgettien kanssa. + Käytä klassista ilmoituksen ulkonäköä. "V\u00e4ritt\u00e4\u00e4 ilmoituksen albumin eloisalla v\u00e4rill\u00e4." "Saattaa aiheuttaa toisto-ongelmia joillain laitteilla." Saattaa nostaa albumin kannen laatua, mutta aiheuttaa hitaampaa kuvien latausaikaa. Käytä tätä vain, jos sinulla on ongelmia huonolaatuisten taideteosten kanssa. - Värittää navigointipalkin ensisijaisella värillä. + Värittää navigaatiopalkin päävärillä. + Värittää sovelluksen pikavalinnat päävärillä. Ilmoitukset, liikkuminen jne. Nykyiseltään sovellus tukee vain LRC-muodossa olevia sanoituksia, joko sulautettuna tai erillisinä tiedostoina. Palauttaa käynnistyessään viimeksi avoinna olleen välilehden. + Sekoitus-tila pysyy päällä, kun valitset uuden listan kappaleita "Ei voitu ladata albumia vastaavaa albumin kantta." Etsi kirjastostasi... Suosikit Viimeksi lisätyt - Historia + Viimeksi toistettu Eniten kuunnellut Poista kansi Lataa Last.fm-sivustolta @@ -183,18 +193,17 @@ Sinulla ei ole oikeuksia tähän. Oikeus päästä muistikortille evätty. Tue kehitystä + Osta Phonograph Pro Kiitos! + Palautettiin aiempi ostos. Ole hyvä ja käynnistä sovellus uudelleen saadaksesi hyödyn uusista ominaisuuksista. Versio Luoja Kirjoita sähköpostia - Lisää Google Plus -piireihin Seuraa Twitterissä Forkkaa GitHubissa Vieraile verkkosivulla Raportoi bugeja Raportoi bugeja tai ehdota uusia ominaisuuksia. - Liity Google Plus -yhteisöön - Jos tarvitset apua tai sinulla on kysymyksiä, Phonographin Google Plus -yhteisö on hyvä paikka sille. Käännä Auta kääntämään Phonograph sinun äidinkielellesi. Arvostele @@ -204,7 +213,7 @@ Teemamoottorista ja muutamista muista hienoista jutuista. Hienosta material-ikonista. Play-kaupan kuvituksesta ja tyhjän albumin kannesta. - Sovelluksen designin kanssa auttamisesta. + Sovelluksen ulkonäön kanssa auttamisesta. Verkkosivu Ladataan tuotteita... Seuraavaksi @@ -217,15 +226,19 @@ Kansiot Soittolista tallennettu paikkaan %s. Soittolistan tallentaminen epäonnistui (%s). + Tallennettiin soittolistat %1$d paikkaan %2$s. + Tallennettiin soittolistat %1$d paikkaan %2$s, %3$d tallentaminen epäonnistui. %s ei ole listattu Media Storessa.]]> + Jotkin tiedostot eivät ole listattuna Media Storessa. Ei mitään skannattavaa. Skannattiin %1$d %2$d tiedostosta. Ei voitu skannata %d tiedostoa. Listataan tiedostoja - %s on uusi aloitushakemisto. + %s on uusi aloituskansio. Phonograph - Iso Phonograph - Klassikko Phonograph - Pieni + Phonograph - Kortti Raportoi ongelma Ongelma Kirjaudu sisään @@ -250,15 +263,41 @@ Sinun käyttäjätietojasi käytetään vain todentamiseen. Sinut johdatetaan vianseurantaohjelman verkkosivulle. Sekoita + Top raidat + Viimeksi lisätyt Soittolista on tyhjä + Toistoilmoitus sisältää nappeja musiikin toistolle/pysäyttämiselle jne. + Toistoilmoitus Sovelluksen ilmaisversio on rajoitettu 5 ensimmäiseen värivaihtoehtoon. + Musta teema on Phonograph Pro:n ominaisuus. + Herätyskello on Phonograph Pro:n ominaisuus. + Kansionäkymä on Phonograph Pro:n ominaisuus. + Palautetaan ostoa... + Ei voitu palauttaa ostoa. + Osta + Palauta + Ostoa ei löytynyt. Työpanoksesta sovelluksen lähdekoodin parissa. + Albumisivun ulkonäöstä. + Lisää Estolista Poista estolistalta %1$s estolistalta?]]> Tyhjennä estolista Haluatko tyhjentää estolistan? - Estolistalla olevien hakemistojen sisältö piilotetaan kirjastoltasi. + Estolistalla olevien kansioiden sisältö piilotetaan kirjastostasi. + Nollaa artistin kuva + Aseta artistin kuva Kirjaston välilehdet Määrittele kirjaston välilehtien näkyvyyttä ja järjestystä. + Sinun täytyy valita vähintään yksi kategoria. + Skannaa kansio + Skannaa media + Lajittelu järjestys + Nouseva + Laskeva + Artisti + Albumi + Vuosi + Lopeta viimeinen kappale diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index a9c1cb6ec..c97c1518e 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -20,7 +20,7 @@ Suivant Ajouter à la liste de lecture Retirer de la liste de lecture - Ajouter à la liste de lecture... + Ajouter à la playlist... Éditeur d\'informations Supprimer de l\'appareil Détails @@ -48,7 +48,6 @@ Paroles Le champ \"titre\" ou \"artiste\" est vide. Sauvegarde des modifications - Sauvegarde dans le fichier… Détails Nom du fichier Chemin du fichier @@ -88,6 +87,7 @@ Supprimer les playlists Effacer la playlist Sauvegarder en tant que fichier + "Enregistrer le fichier " "Ajouter à la playlist" Lecture aléatoire Lecture aléatoire @@ -122,7 +122,7 @@ Raccourcis de l\'application colorés Afficher la pochette d\'album Télécharger automatiquement les métadonnées - Pochette d\'album floue + Flouter la pochette d\'album Notification colorée Style de notification classic Ignorer le stockage des pochettes @@ -195,14 +195,11 @@ Version Auteur Écrire un email - Ajouter aux cercles Google+ Suivre sur Twitter Afficher sur GitHub Visiter le site internet Signaler des bugs Signalez des bugs ou demandez des nouvelles fonctionnalités. - Rejoindre la communauté Google+ - Si vous avez besoin d\'aide ou si vous avez des questions, la communauté Phonograph sur Google+ est un bon endroit où se rendre. Traduire Aidez à traduire Phonograph dans votre langue maternelle. Évaluer @@ -225,7 +222,9 @@ Dossiers Liste de lecture sauvegardée dans %s. Impossible de sauvegarder la liste de lecture (%s). + Liste de lecture enregistrée %1$d à %2$s %s n\'est pas dans le catalogue de médias.]]> + "Certains fichiers ne sont pas enregistrés dans le stockage média " Rien à analyser. %1$d fichiers scannés sur %2$d. Impossible de scanner %d fichiers. @@ -274,6 +273,7 @@ Restaurer Aucun achat trouvé. Pour ses contributions au code source + Pour créer le design de la page d\'album. Ajouter Liste noire Retirer de la liste noire @@ -286,4 +286,12 @@ Catégories de la bibliothèque Configurer la visibilité et l\'ordre des catégories. Vous devez sélectionner au moins une catégorie. + Scanner le dossier. + Scanner les médias. + Ordre de tri + Ascendant + Descendant + Artiste + Album + Année diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 1bd93a82c..8206cec10 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -48,7 +48,6 @@ מילים הכותרת או האמן ריקים. שומר שינויים - שומר לקובץ... פרטים שם הקובץ מיקום הקובץ @@ -88,6 +87,7 @@ מחיקת רשימות השמעה נקה רשימת השמעה שמור כקובץ + שמור כקבצים "הוסף לרשימת ההשמעה" ערבב הכל ערבב אלבום @@ -195,14 +195,11 @@ גרסה מחבר כתוב אימייל - הוסף למעגלי Google+ עקוב בטוויטר גירסה בGitHub ביקור באתר דווח על באגים דווח על באגים או בקש פיצ\'רים חדשים. - הצטרף לקהילת ה-Google+ - אם אתה צריך עזרה או שיש לך שאלות, קהילת Phonograph ב-Google+ היא מקום טוב ללכת אליו. תרגם עזור לתרגם את Phonograph לשפת האם שלך. דרג @@ -225,7 +222,10 @@ תיקיות רשימת ההשמעה נשמרה ל-%s. לא ניתן היה לשמור את רשימת ההשמעה (%s) + נשמרו %1$d רשימות השמעה ל-%2$s. + נשמרו %1$d רשימות השמעה ל-%2$s. לא ניתן היה לשמור %3$d. %s לא רשום בחנות המדיה.]]> + קבצים מסוימים לא רשומים בחנות המדיה. אין כלום לסרוק. נסרקו %1$d מתוך %2$d קבצים. לא ניתן היה לסרוק %d קבצים. @@ -274,6 +274,7 @@ שחזר לא נמצאה רכישה. על התרומה שלו לקוד המקור. + על יצירת העיצוב של מסך האלבום. הוסף רשימה שחורה הסר מהרשימה השחורה @@ -286,4 +287,12 @@ קטגוריות הספרייה הגדר את הנראות והסדר של הקטגוריות בספרייה. עליך לבחור לפחות קטגוריה אחת. + סרוק ספרייה + סרוק מדיה + סדר המיון + סדר עולה + סדר יורד + אמן + אלבום + שנה diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 2d5de19f8..8ed195f9a 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -48,7 +48,6 @@ Stihovi Polje za naziv ili izvođača je prazno. Spremanje promjena - Spremanje u datoteku... Detalji Naziv datoteke Odredište datoteke @@ -88,6 +87,7 @@ Izbriši popise naslova Očisti popis naslova Spremi kao datoteku + Spremi kao datoteke "Dodaj na popis naslova" Izmiješaj sve Izmiješaj album @@ -195,14 +195,11 @@ Verzija Autor Napiši email - Dodaj u Google Plus krugove Prati na Twitteru Forkaj na GitHubu Posjeti web-stranicu Prijavi pogreške Prijavite pogreške i zatražite nove mogućnosti. - Pridruži se Google Plus zajednici - Ako trebate pomoć ili imate pitanja, trebali bi posjetiti Phonograph zajednicu na Google+. Prevedi Pomozite prevesti Phonograph na svoj jezik. Ocijeni @@ -225,7 +222,10 @@ Mape Popis naslova spremljen u %s. Popis naslova nije spremljen (%s). + %1$d popisa naslova je spremljeno u %2$s. + %1$d popisa naslova je spremljeno u %2$s, %3$d nije uspješno spremljeno. %s nije na popisu media storea.]]> + Neke datoteke nisu u medijskom spremniku. Nema ništa za skeniranje. Skenirano %1$d od %2$d datoteka Nije moguće skenirati %d datoteka. @@ -274,16 +274,25 @@ Vrati Nema pronađenih kupnji. Za njegov doprinos izvornom kodu. + Za stvaranje dizajna stranice albuma. Dodaj - Crna lista - Ukloni sa crne liste - %1$s sa crne liste?]]> - Očisti crnu listu - Želite li očistiti crnu listu? - Sadržaj mapa na crnoj listi je skriven iz vaše biblioteke. + Crni popis + Ukloni sa crnog popisa + %1$s sa crnog popisa?]]> + Očisti crni popis + Želite li očistiti crni popis? + Sadržaj mapa crnog popisa je skriven iz vaše biblioteke. Resetiraj slike izvođača Postavi sliku izvođača Kategorije biblioteke Prilagodite vidljivost i poredak kategorija biblioteke Morate odabrati barem jednu kategoriju + Skeniraj direktorij + Skeniraj medije + Način sortiranja + Uzlazno + Silazno + Izvođač + Album + Godina diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 06283983b..4699974fe 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -48,7 +48,6 @@ Dalszöveg A cím vagy az előadó üres. Változások mentése - Mentés fájlba... Részletek Fájlnév Útvonal @@ -88,6 +87,7 @@ Lejátszási lista törlése Lejátszási lista kiürítése Mentés fájlként + Mentés fájlként "Hozzáadás a lejátszási listához" Összes keverése Album dalainak keverése @@ -195,14 +195,11 @@ Verzió Készítő Írjon e-mailt - Google Plus Twitter Villog a GitHubon Látogassa meg a honlapot Hibajelentés Problémák jelentése, új funkciók kérése. - Csatlakozzon a Google Plus közösséghezGoogle Plus közösség - Ha segítségre lenne szüksége, vagy kérdései vannak, keresse fel a Phonograph közösséget a Google Plus-on. Fordítás Segítsen lefordítani a Phonograph-ot a saját nyelvére. Értékelés @@ -225,7 +222,10 @@ Mappák Lejátszási lista mentve ide: %s. Nem sikerült menteni a lejátszási listát (%s). + Mentés %1$d lejátszási listát %2$s-re. + Mentés %1$d lejátszási listák %2$s, nem sikerült menteni a %3$d. %s nem szerepel a médiatárban.]]> + Egyes fájlok nem szerepelnek a médiában. Nincs mit beolvasni. %1$d/%2$d fájl beolvasva. %d fájl beolvasása nem sikerült. @@ -272,4 +272,27 @@ A vásárlást nem sikerült visszaállítani. Vásárlás Visszaállítás + Nem található vásárlás. + A forráskódhoz való hozzájárulásáért. + Az album oldalának kialakításához. + Hozzáadás + Tiltólista + Eltávolítás a tiltólistáról + %1$s-t a tiltólistáról?]]> + Tiltólista törlése + Biztosan törölni szeretné a tiltólistát? + A tiltólistán lévő mappák tartalma nem szerepel a könyvtárban. + Előadó képének visszaállítása + Előadó képének beállítása + Könyvtár kategóriák + Könyvtár kategóriák láthatóságának és sorrendjének beállítása. + Legalább egy kategóriát ki kell választania. + Szkennelési könyvtár + Szkennelési média + Sorrend + Növekvő + Csökkenő + Előadó + Album + Év diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index 9b2049a9d..e208bdc34 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -1,23 +1,23 @@ "Apa yang ingin Anda bagikan? " - %3$s - Voice - Jadikan %1$s sebagai nada dering anda. + Sedang mendengarkan %1$s oleh %2$s. + Berkas audio + Jadikan %1$s sebagai nada dering Anda. Setel sebagai nada dering Bagikan "Pengaturan" Izinkan Tentang - Yes + Bersihkan daftar putar Tambahkan ke favorit Hapus dari favorit Pencarian Putar selanjutnya Putar Putar/Jeda - Musik Sebelumnya - Musik Selanjutnya + Sebelumnya + Selanjutnya Tambahkan ke antrean putar Hapus dari antrian Tambahkan ke daftar putar... @@ -29,15 +29,15 @@ Pindai Atur sebagai direktori awal Album-album - Artis-artis + Artis Aliran - Lagu-lagu + Lagu Daftar putar Tidak dapat memutar lagu ini. - Tidak dapat menemukan biografi artis ini. - Tidak dapat menemukan info Wiki untuk album ini + Tidak dapat menemukan entri biografi untuk artis ini. + Tidak dapat menemukan entri wiki untuk album ini. Biografi - Wikipedia + Wiki Fokus audio ditolak. Album Artis @@ -48,15 +48,14 @@ Lirik Judul atau artis kosong. Menyimpan perubahan - Menyimpan berkas... Rincian Nama berkas Lokasi berkas Ukuran Jenis Panjang - Bitrate - Sampling rate + Kecepatan bit + Batas frekuensi Pergi ke artis Pergi ke album Antrean putar @@ -70,10 +69,10 @@ Telah menambah %1$d lagu ke daftar putar %2$s. Telah membuat daftar putar %1$s %1$d lagu dihapus - Daftar putar ℅1$s sudah ada + Daftar putar %1$s sudah ada. Tidak dapat membuat daftar putar. }{%1$s}{}?]]> - }{%1$s}{}? Tindakan ini tidak dapat diputarbalik.]]> + }{%1$s}{}? Tindakan ini tidak dapat diurungkan.]]> %1$d daftar putar?]]> %1$s?]]> %1$d lagu?]]> @@ -88,7 +87,9 @@ Hapus daftar-daftar putar Kosongkan daftar putar Simpan sebagai berkas + Simpan sebagai berkas "Tambahkan ke daftar putar" + Pilih semua Acak semua Putar album secara acak Putar artis secara acak @@ -105,8 +106,9 @@ Tidak pernah Hari ini Minggu ini + 7 hari terakhir Bulan ini - Melewati 3 bulan + 3 bulan terakhir Tahun ini Ekualiser Warna @@ -120,17 +122,18 @@ Notifikasi Alat Navigasi Berwarna Pintasan Berwarna - Tampilkan cover album + Tampilkan sampul album Otomatis mengunduh metadata Buramkan sampul album Notifikasi berwarna Desain notifikasi klasik - Abaikan sampul-sampul dari \'Media Store\' android. + Abaikan sampul-sampul pada Media Penyimpanan Pemutaran tanpa jeda Kurangi suara saat ada pemberitahuan - Sela daftar putar terakhir ditambahkan + Sela daftar putar yang terakhir ditambahkan Tampilkan lirik yang tersinkronisasi - Mengingat tab terakhir + Ingat tab terakhir + Ingat putar acak Tidak ada ekualiser yang ditemukan. "Silahkan mainkan sebuah lagu dan coba lagi. " Hapus @@ -156,20 +159,21 @@ Lagu Gunakan sambul album lagu yang sedang diputar sebagai wallpaper layar kunci Buramkan sampul album di layar kunci. Dapat menyebabkan masalah dengan aplikasi pihak ke tiga dan widget - Pakai desain notifikasi klasik + Gunakan desain notifikasi klasik. "Mewarnai notifikasi putar dengan warna yang cerah dari album." "Dapat menyebabkan masalah pemutaran pada beberapa perangkat." "Dapat meningkatkan kualitas sampul album, namun menyebabkan waktu pemuatan lebih lambat. Aktifkan jika Anda bermasalah dengan resolusi gambar album yang rendah. " "Mewarnai bilah navigasi dengan warna primer. " Mewarnai pintasan-pintasan dengan warna utama. Pemberitahuan, navigasi dll. - Saat ini hanya disinkronkan lirik dalam format LRC yang didukung. Antara tertanam atau sebagai file terpisah. - Pergi ke tab terakhir pertama kali buka + Saat ini hanya mendukung lirik dalam format LRC yang disinkronkan. Antara tertanam atau sebagai file terpisah. + Pergi ke tab yang terakhir saat aplikasi dibuka + Mode putar acak akan tetap aktif saat memilih daftar lagu baru "Tidak dapat mengunduh sampul album yang cocok." Mencari pustaka Anda... Favorit Terakhir ditambahkan - Riwayat + Diputar baru-baru ini Sering Diputar Hapus sampul Unduh dari Last.fm @@ -191,18 +195,15 @@ Dukung pengembangan Beli Phonographo Pro Terima kasih! - Mengembalikan pembelian sebelumnya. Mohon buka kembali aplikasi untuk memakai semua fitur + Mengembalikan pembelian sebelumnya. Mohon buka kembali aplikasi untuk memakai semua fitur. Versi Penulis - Tuliskan email - Tambahkan ke lingkaran Google Plus + Tulis pesan elektronik Ikuti di Twitter - Gabung di Github + Salin di Github Kunjungi situs Laporkan masalah Laporkan masalah atau sarankan fitur baru - Gabung di komunitas Google Plus - "Anda dapat meminta bantuan atau mengajukan pertanyaan di komunitas Phonograph di Google Plus. " Terjemahkan "Bantu terjemahkan Phonograph ke dalam bahasa Anda. " Beri nilai @@ -225,7 +226,10 @@ Map-map Daftar putar tersimpan ke %s. Gagal untuk menyimpan daftar putar (%s). + %1$d daftar putar disimpan ke %2$s. + %1$d daftar putar disimpan ke %2$s, gagal menyimpan %3$d. %s tidak tercatat di toko media.]]> + Beberapa berkas tidak tercatat di media penyimpanan. Tidak ada untuk di pindai. %1$d dari %2$d berkas terpindai. Tidak dapat memindai %d berkas. @@ -259,31 +263,41 @@ Data akun Anda hanya digunakan untuk otentikasi. Anda akan ditersukan ke situs web pelacak masalah. Acak - Sering Diputar - @string/last_added + Trek Teratas + Terakhir ditambahkan Daftar putar kosong - Notifikasi bermain memberi tindakan untuk bermain / jeda, dll. - Memainkan notifikasi - Hanya ada 5 warna utama untuk versi gratis - Tema gelap adalah fitur Phonograph Pro - Waktu tidur adalah fitur Phonograph Pro - Tampilan map adalah fitur Phonograph Pro - Mengembalikan pembelian... - Tidak dapat mengembalikan pembelian - Membeli - Mengembalikan - Tidak dapat menemukan pembelian - Untuk kontribusinya terhadap sumber kode + Notifikasi bermain memberi tindakan untuk putar/jeda, dll. + Notifikasi bermain + Hanya 5 warna pertama yang tersedia untuk versi gratis. + Tema gelap adalah fitur Phonograph Pro. + Pengatur waktu tidur adalah fitur Phonograph Pro + Tampilan folder adalah fitur Phonograph Pro. + Memulihkan pembelian... + Tidak dapat memulihkan pembelian. + Pembelian + Pulihkan + Tidak ada pembelian yang ditemukan. + Untuk kontribusinya terhadap kode sumber. + Untuk membuat halaman desain album. Tambah Daftar hitam Hapus dari daftar hitam %1$s dari daftar hitam?]]> Bersihkan daftar hitam Apakah Anda ingin menghapus daftar hitam? - Konten yang sudah di daftar hitamkan tersembunyi dari pustaka anda - Muat ulang gambar artist - Pasang gambar artist + Konten yang sudah di daftar hitamkan tersembunyi dari pustaka Anda. + Atur ulang gambar artist + Pasang gambar artis Kategori perpustakaan - Mengkonfigurasi visibilitas dan susunan kategori perpustakaan. + Konfigurasi visibilitas dan urutan kategori perpustakaan. Anda harus memilih setidaknya satu kategori + Pindai direktori + Pindai media + Tata urutan + Naik + Turun + Artis + Album + Tahun + Selesaikan lagu terakhir diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 9b2049a9d..e208bdc34 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -1,23 +1,23 @@ "Apa yang ingin Anda bagikan? " - %3$s - Voice - Jadikan %1$s sebagai nada dering anda. + Sedang mendengarkan %1$s oleh %2$s. + Berkas audio + Jadikan %1$s sebagai nada dering Anda. Setel sebagai nada dering Bagikan "Pengaturan" Izinkan Tentang - Yes + Bersihkan daftar putar Tambahkan ke favorit Hapus dari favorit Pencarian Putar selanjutnya Putar Putar/Jeda - Musik Sebelumnya - Musik Selanjutnya + Sebelumnya + Selanjutnya Tambahkan ke antrean putar Hapus dari antrian Tambahkan ke daftar putar... @@ -29,15 +29,15 @@ Pindai Atur sebagai direktori awal Album-album - Artis-artis + Artis Aliran - Lagu-lagu + Lagu Daftar putar Tidak dapat memutar lagu ini. - Tidak dapat menemukan biografi artis ini. - Tidak dapat menemukan info Wiki untuk album ini + Tidak dapat menemukan entri biografi untuk artis ini. + Tidak dapat menemukan entri wiki untuk album ini. Biografi - Wikipedia + Wiki Fokus audio ditolak. Album Artis @@ -48,15 +48,14 @@ Lirik Judul atau artis kosong. Menyimpan perubahan - Menyimpan berkas... Rincian Nama berkas Lokasi berkas Ukuran Jenis Panjang - Bitrate - Sampling rate + Kecepatan bit + Batas frekuensi Pergi ke artis Pergi ke album Antrean putar @@ -70,10 +69,10 @@ Telah menambah %1$d lagu ke daftar putar %2$s. Telah membuat daftar putar %1$s %1$d lagu dihapus - Daftar putar ℅1$s sudah ada + Daftar putar %1$s sudah ada. Tidak dapat membuat daftar putar. }{%1$s}{}?]]> - }{%1$s}{}? Tindakan ini tidak dapat diputarbalik.]]> + }{%1$s}{}? Tindakan ini tidak dapat diurungkan.]]> %1$d daftar putar?]]> %1$s?]]> %1$d lagu?]]> @@ -88,7 +87,9 @@ Hapus daftar-daftar putar Kosongkan daftar putar Simpan sebagai berkas + Simpan sebagai berkas "Tambahkan ke daftar putar" + Pilih semua Acak semua Putar album secara acak Putar artis secara acak @@ -105,8 +106,9 @@ Tidak pernah Hari ini Minggu ini + 7 hari terakhir Bulan ini - Melewati 3 bulan + 3 bulan terakhir Tahun ini Ekualiser Warna @@ -120,17 +122,18 @@ Notifikasi Alat Navigasi Berwarna Pintasan Berwarna - Tampilkan cover album + Tampilkan sampul album Otomatis mengunduh metadata Buramkan sampul album Notifikasi berwarna Desain notifikasi klasik - Abaikan sampul-sampul dari \'Media Store\' android. + Abaikan sampul-sampul pada Media Penyimpanan Pemutaran tanpa jeda Kurangi suara saat ada pemberitahuan - Sela daftar putar terakhir ditambahkan + Sela daftar putar yang terakhir ditambahkan Tampilkan lirik yang tersinkronisasi - Mengingat tab terakhir + Ingat tab terakhir + Ingat putar acak Tidak ada ekualiser yang ditemukan. "Silahkan mainkan sebuah lagu dan coba lagi. " Hapus @@ -156,20 +159,21 @@ Lagu Gunakan sambul album lagu yang sedang diputar sebagai wallpaper layar kunci Buramkan sampul album di layar kunci. Dapat menyebabkan masalah dengan aplikasi pihak ke tiga dan widget - Pakai desain notifikasi klasik + Gunakan desain notifikasi klasik. "Mewarnai notifikasi putar dengan warna yang cerah dari album." "Dapat menyebabkan masalah pemutaran pada beberapa perangkat." "Dapat meningkatkan kualitas sampul album, namun menyebabkan waktu pemuatan lebih lambat. Aktifkan jika Anda bermasalah dengan resolusi gambar album yang rendah. " "Mewarnai bilah navigasi dengan warna primer. " Mewarnai pintasan-pintasan dengan warna utama. Pemberitahuan, navigasi dll. - Saat ini hanya disinkronkan lirik dalam format LRC yang didukung. Antara tertanam atau sebagai file terpisah. - Pergi ke tab terakhir pertama kali buka + Saat ini hanya mendukung lirik dalam format LRC yang disinkronkan. Antara tertanam atau sebagai file terpisah. + Pergi ke tab yang terakhir saat aplikasi dibuka + Mode putar acak akan tetap aktif saat memilih daftar lagu baru "Tidak dapat mengunduh sampul album yang cocok." Mencari pustaka Anda... Favorit Terakhir ditambahkan - Riwayat + Diputar baru-baru ini Sering Diputar Hapus sampul Unduh dari Last.fm @@ -191,18 +195,15 @@ Dukung pengembangan Beli Phonographo Pro Terima kasih! - Mengembalikan pembelian sebelumnya. Mohon buka kembali aplikasi untuk memakai semua fitur + Mengembalikan pembelian sebelumnya. Mohon buka kembali aplikasi untuk memakai semua fitur. Versi Penulis - Tuliskan email - Tambahkan ke lingkaran Google Plus + Tulis pesan elektronik Ikuti di Twitter - Gabung di Github + Salin di Github Kunjungi situs Laporkan masalah Laporkan masalah atau sarankan fitur baru - Gabung di komunitas Google Plus - "Anda dapat meminta bantuan atau mengajukan pertanyaan di komunitas Phonograph di Google Plus. " Terjemahkan "Bantu terjemahkan Phonograph ke dalam bahasa Anda. " Beri nilai @@ -225,7 +226,10 @@ Map-map Daftar putar tersimpan ke %s. Gagal untuk menyimpan daftar putar (%s). + %1$d daftar putar disimpan ke %2$s. + %1$d daftar putar disimpan ke %2$s, gagal menyimpan %3$d. %s tidak tercatat di toko media.]]> + Beberapa berkas tidak tercatat di media penyimpanan. Tidak ada untuk di pindai. %1$d dari %2$d berkas terpindai. Tidak dapat memindai %d berkas. @@ -259,31 +263,41 @@ Data akun Anda hanya digunakan untuk otentikasi. Anda akan ditersukan ke situs web pelacak masalah. Acak - Sering Diputar - @string/last_added + Trek Teratas + Terakhir ditambahkan Daftar putar kosong - Notifikasi bermain memberi tindakan untuk bermain / jeda, dll. - Memainkan notifikasi - Hanya ada 5 warna utama untuk versi gratis - Tema gelap adalah fitur Phonograph Pro - Waktu tidur adalah fitur Phonograph Pro - Tampilan map adalah fitur Phonograph Pro - Mengembalikan pembelian... - Tidak dapat mengembalikan pembelian - Membeli - Mengembalikan - Tidak dapat menemukan pembelian - Untuk kontribusinya terhadap sumber kode + Notifikasi bermain memberi tindakan untuk putar/jeda, dll. + Notifikasi bermain + Hanya 5 warna pertama yang tersedia untuk versi gratis. + Tema gelap adalah fitur Phonograph Pro. + Pengatur waktu tidur adalah fitur Phonograph Pro + Tampilan folder adalah fitur Phonograph Pro. + Memulihkan pembelian... + Tidak dapat memulihkan pembelian. + Pembelian + Pulihkan + Tidak ada pembelian yang ditemukan. + Untuk kontribusinya terhadap kode sumber. + Untuk membuat halaman desain album. Tambah Daftar hitam Hapus dari daftar hitam %1$s dari daftar hitam?]]> Bersihkan daftar hitam Apakah Anda ingin menghapus daftar hitam? - Konten yang sudah di daftar hitamkan tersembunyi dari pustaka anda - Muat ulang gambar artist - Pasang gambar artist + Konten yang sudah di daftar hitamkan tersembunyi dari pustaka Anda. + Atur ulang gambar artist + Pasang gambar artis Kategori perpustakaan - Mengkonfigurasi visibilitas dan susunan kategori perpustakaan. + Konfigurasi visibilitas dan urutan kategori perpustakaan. Anda harus memilih setidaknya satu kategori + Pindai direktori + Pindai media + Tata urutan + Naik + Turun + Artis + Album + Tahun + Selesaikan lagu terakhir diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 52978ba13..f2fa806cf 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -48,7 +48,6 @@ Testi Il titolo o l\'artista sono vuoti. Salvando le modifiche - Salvataggio in corso... Dettagli Nome file Percorso file @@ -88,7 +87,9 @@ Elimina playlist Svuota playlist Salva come file + Salva come file "Aggiungi alla playlist" + Seleziona tutto Riproduzione casuale Riproduzione casuale album Riproduzione casuale artista @@ -105,12 +106,13 @@ Mai Oggi Questa settimana + Ultimi 7 giorni Questo mese Ultimi 3 mesi Quest\'anno Equalizzatore Colori - Schermata in riproduzione + Schermata di riproduzione Tema generale Audio Libreria @@ -121,7 +123,7 @@ Barra di navigazione colorata Scorciatoie app colorate Mostra copertina album nella schermata di blocco - Scarica automaticamente i dati + Ottieni automaticamente i metadati Sfoca la copertina Notifica colorata Design di notifica classico @@ -131,6 +133,7 @@ Ultimo intervallo di playlist aggiunto Mostra testi sincronizzati Ricorda ultima scheda + Ricorda casuale Nessun equalizzatore trovato "Riproduci un brano e prova di nuovo." Elimina @@ -165,11 +168,12 @@ Notifiche, navigazione ecc. Attualmente solo i testi sincronizzati in formato LRC sono supportati. Sia integrati o come file separati. All\'avvio vai all\'ultima scheda aperta + La modalità casuale rimarrà attiva quando si seleziona un nuovo elenco di brani "Impossibile scaricare una copertina adatta." Cercando nella tua libreria... Preferiti Aggiunti di recente - Cronologia + Ascoltati di recente Brani preferiti Rimuovi copertina Scarica da Last.fm @@ -195,14 +199,11 @@ Versione Autore Scrivi un\'email - Aggiungi alle cerchie di Google+ Segui su Twitter Vai su GitHub Visita sito web Segnala bug Segnala bug o richiedi nuove funzionalità. - Unisciti alla community di Google+ - Se hai bisogno di aiuto o hai domande, la community di Phonograph su Google+ è un buon posto dove andare. Traduci Aiuta a tradurre Phonograph nella tua lingua. Valuta @@ -225,7 +226,10 @@ Cartelle Playlist salvata in %s. Salvataggio playlist fallito (%s). + Salvate %1$d playlist in %2$s. + Salvate %1$d playlist in %2$s, %3$d non salvato. %s non è elencato nel media store.]]> + Alcuni file non sono elencati nel media store. Nulla da scansionare. Scansione effettuata per %1$d su %2$d file. Non è stato possibile effettuare la scansione di %d file. @@ -275,6 +279,7 @@ Ripristina Nessun acquisto trovato. Per il suo contributo al codice sorgente. + Per creare la pagina dell\'album. Aggiungi Lista nera Rimuovi dalla lista nera @@ -287,4 +292,13 @@ Categorie libreria Configura visibilità e ordine delle categorie della libreria. Devi scegliere almeno una categoria. + Scansiona cartella + Scansiona media + Ordine + Ascendente + Discendente + Artista + Album + Anno + Termina l\'ultimo brano diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 1bd93a82c..8206cec10 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -48,7 +48,6 @@ מילים הכותרת או האמן ריקים. שומר שינויים - שומר לקובץ... פרטים שם הקובץ מיקום הקובץ @@ -88,6 +87,7 @@ מחיקת רשימות השמעה נקה רשימת השמעה שמור כקובץ + שמור כקבצים "הוסף לרשימת ההשמעה" ערבב הכל ערבב אלבום @@ -195,14 +195,11 @@ גרסה מחבר כתוב אימייל - הוסף למעגלי Google+ עקוב בטוויטר גירסה בGitHub ביקור באתר דווח על באגים דווח על באגים או בקש פיצ\'רים חדשים. - הצטרף לקהילת ה-Google+ - אם אתה צריך עזרה או שיש לך שאלות, קהילת Phonograph ב-Google+ היא מקום טוב ללכת אליו. תרגם עזור לתרגם את Phonograph לשפת האם שלך. דרג @@ -225,7 +222,10 @@ תיקיות רשימת ההשמעה נשמרה ל-%s. לא ניתן היה לשמור את רשימת ההשמעה (%s) + נשמרו %1$d רשימות השמעה ל-%2$s. + נשמרו %1$d רשימות השמעה ל-%2$s. לא ניתן היה לשמור %3$d. %s לא רשום בחנות המדיה.]]> + קבצים מסוימים לא רשומים בחנות המדיה. אין כלום לסרוק. נסרקו %1$d מתוך %2$d קבצים. לא ניתן היה לסרוק %d קבצים. @@ -274,6 +274,7 @@ שחזר לא נמצאה רכישה. על התרומה שלו לקוד המקור. + על יצירת העיצוב של מסך האלבום. הוסף רשימה שחורה הסר מהרשימה השחורה @@ -286,4 +287,12 @@ קטגוריות הספרייה הגדר את הנראות והסדר של הקטגוריות בספרייה. עליך לבחור לפחות קטגוריה אחת. + סרוק ספרייה + סרוק מדיה + סדר המיון + סדר עולה + סדר יורד + אמן + אלבום + שנה diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index df9b706f1..9677d503b 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -48,7 +48,6 @@ 歌詞 アルバム名またはアルバムアーティストの欄が空白です 変更を保存しています - ファイルに保存… 詳細情報 ファイル名 パス @@ -88,6 +87,7 @@ プレイリストの複数削除 プレイリストの消去 ファイルに保存 + ファイルを保存 "プレイリストに追加" 全曲シャッフル アルバムをシャッフル @@ -195,14 +195,11 @@ バージョン 製作者 メールを書く - Google+ のサークルに参加 Twitter でフォロー GitHub でフォーク ウェブサイトを開く バグを報告 パグの報告または新機能のリクエスト - GooglePlusコミュニティに参加する - 質問があったり助けが必要だったりする場合は、Phonograph の Google+ コミュニティを訪れてみると良いかもしれません。(英語限定) 翻訳 Phonograph をあなたの母国語に翻訳するのをお手伝いください。 評価 @@ -225,7 +222,10 @@ フォルダ プレイリストを%sに保存しました。 プレイリスト(%s)の保存に失敗しました。 + %1$dプレイリストを%2$sに保存しました + %1$dプレイリストを%2$sに保存しました、%3$dに保存できませんでした。 %sはメディアストアにリストされていません。]]> + メディアストアに追加されていないファイルがあります。 スキャンするものがありません。 %1$d中の%2$dをスキャンしました。 %d個のファイルをスキャンできませんでした。 @@ -274,6 +274,7 @@ 復元 購入履歴がありませんでした。 ソースコードへの貢献。 + Por criar o design da página de álbuns. 追加 ブラックリスト ブラックリストから除去 @@ -286,4 +287,12 @@ ライブラリのカテゴリ ライブラリに表示するカテゴリとその順番を設定します。 最低でも1つのカテゴリを選択を選択してください + ディレクトリをスキャン + メディアをスキャン + 並べ替え + 昇順 + 降順 + Artista + Álbum + Ano diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 699b2f6ee..c4e5e8890 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -48,7 +48,6 @@ 가사 제목이나 아티스트 란이 비었습니다. 변경 사항 저장 중... - 파일에 저장 중... 세부 정보 파일 이름 파일 경로 @@ -88,6 +87,7 @@ 재생 목록 삭제 재생 목록 비우기 파일로 저장 + 파일들로 저장 "재생 목록에 추가" 모든 곡 랜덤 재생 앨범 랜덤 재생 @@ -127,9 +127,9 @@ 클래식 알림 디자인 미디어 스토어 앨범 커버 무시 갭리스 재생 - 포커스 상실 시 볼륨 감소 - 최근 추가됨 재생 목록 기간 - 동기화된 가사 보이기 + 포커스 상실 시 볼륨 감소하기 + 최근 추가된 재생 목록 간격 + 동기화된 가사 보기 마지막 탭 기억 설치된 이퀄라이저가 없습니다. "먼저 노래를 재생한 후 다시 시도해 주세요." @@ -165,7 +165,7 @@ 하단바가 색깔을 주 색상으로 색칠합니다. 주 색상으로 앱 바로가기 색칠 알림 소리, 버튼음 등 - 임베딩 또는 별개로 저장된 LRC 형식의 동기화된 자막 파일만 지원합니다. + 현재는 임베딩 또는 별개로 저장된 LRC 형식의 동기화된 자막 파일만 지원합니다. 마지막으로 연 탭으로 시작 "\uc77c\uce58\ud558\ub294 \uc568\ubc94 \ucee4\ubc84\ub97c \ucc3e\uc744 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4." 저장소 검색... @@ -197,14 +197,11 @@ 버전 제작자 이메일 작성 - Google + 서클에 추가 트위터에서 팔로우하기 깃허브 포크 웹사이트 방문 버그 제보 버그를 제보하거나 새로운 기능을 제안합니다. - Google + 커뮤니티에 가입하세요. - Google + 의 Phonograph 커뮤니티에서 질문을 하거나 도움을 받을 수 있습니다. 번역 Phonograph의 우리말 번역에 기여하세요. 별점 @@ -227,7 +224,10 @@ 폴더 재생 목록을 %s 로 저장했습니다. 재생 목록을 저장하는 과정에서 오류가 발생했습니다. (%s) + %1$d 재생목록을 %2$s에 저장함. + %1$d 재생목록을 %2$s에 저장함 / %3$d 저장 실패 %s 을(를) 찾을 수 없습니다.]]> + 몇몇 파일은 미디어 리스트에 기록되지 않습니다. 스캔할 내용이 없습니다. %2$d 개 중 %1$d 개 파일 스캔 %d 개 파일을 스캔하지 못했습니다. @@ -276,8 +276,9 @@ 복원 구매 내역이 없습니다. 소스 코드에 대한 기여 + 앨범 페이지 디자인 제작 추가 - 제외 목록 + 제외할 목록 제외 목록에서 제거 %1$s 을(를) 제외 목록에서 제거하시겠습니까?]]> 제외 목록 비우기 @@ -285,7 +286,15 @@ 제외 목록에 추가된 폴더의 내용은 보관함에 표시되지 않습니다. 아티스트 이미지 초기화 아티스트 이미지 설정 - 보관함 카테고리 - 보관함 카테고리의 표시 여부 및 배열 순서를 조정합니다. + 라이브러리 카테고리 + 라이브러리 카테고리의 표시 여부 및 배열 순서를 조정합니다. 적어도 하나의 카테고리는 선택해야 합니다. + 디렉토리 스캔하기 + 미디어 스캔 + 순서 정렬하기 + 오름차순 + 내림차순 + 아티스트 + 앨범 + 연도 diff --git a/app/src/main/res/values-land/dimens.xml b/app/src/main/res/values-land/dimens.xml index 347dad540..c460f0bdb 100644 --- a/app/src/main/res/values-land/dimens.xml +++ b/app/src/main/res/values-land/dimens.xml @@ -1,8 +1,8 @@ + 144dp + 112dp 56dp - 180dp - 72dp diff --git a/app/src/main/res/values-sw600dp-land/dimens.xml b/app/src/main/res/values-land/integers.xml similarity index 51% rename from app/src/main/res/values-sw600dp-land/dimens.xml rename to app/src/main/res/values-land/integers.xml index a0a6d8b43..068d4f46e 100644 --- a/app/src/main/res/values-sw600dp-land/dimens.xml +++ b/app/src/main/res/values-land/integers.xml @@ -1,4 +1,4 @@ - 420dp + 1 \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index f0bc6d6f4..cf741e3d7 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -48,7 +48,6 @@ Songteksten De titel of artiest is leeg. Wijzigingen opslaan - Opslaan in bestand… Details Bestandsnaam Bestandspad @@ -88,6 +87,7 @@ Verwijder afspeellijst Afspeellijst legen Opslaan als bestand + Opslaan als bestanden "Toevoegen aan afspeellijst" Shuffle alles Shuffle album @@ -195,14 +195,11 @@ Versie Auteur Schrijf een email - Toevoegen aan Google Plus cirkels Volgen op Twitter Fork op GitHub Bezoek website Rapporteer bugs Rapporteer bugs of verzoek nieuwe functies. - Deelnemen aan de Google Plus communiteit - Als je hulp nodig hebt of vragen hebt, is de Phonograph communiteit op Google Plus een goede bron om te raadplegen. Vertalen Help Phonograph te vertalen naar jouw moedertaal. Beoordelen @@ -225,7 +222,10 @@ Mappen Afspeellijst opgeslagen in %s. Opslaan van afspeellijst (%s) mislukt. + %1$d afspeellijsten opgeslagen in %2$s. + %1$d afspeellijsten opgeslagen in %2$s, opslaan van %3$d mislukt. %s is niet aanwezig in mediaopslag.]]> + Sommige bestanden worden niet vermeld in de mediaopslag. Niets te scannen. %1$d van de %2$d bestanden gescand. Kan %d bestanden niet scannen. @@ -274,6 +274,7 @@ Herstel Geen aankoop gevonden. Voor zijn bijdragen aan de broncode. + Om het albumpagina-uiterlijk te maken. Toevoegen Zwarte lijst Verwijderen van zwarte lijst @@ -285,4 +286,13 @@ Artiest afbeelding instellen Bibliotheek categorieën Configureer de zichtbaarheid en volgorde van categorieën + Je moet tenminste één categorie selecteren. + Map scannen + Media scannen + Sorteervolgorde + Oplopend + Aflopend + Artiest + Album + Jaar diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 9f0d8c4cb..f39fac1cb 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -30,6 +30,7 @@ Ustaw jako katalog startowy Albumy Wykonawcy + Gatunki Utwory Listy odtwarzania Utw\u00f3r nie mo\u017ce zosta\u0107 odegrany @@ -47,7 +48,6 @@ Tekst Pole tytułu lub artysty jest puste Zapisywanie zmian... - Zapisywanie do pliku... Szczegóły Nazwa pliku Ścieżka pliku @@ -87,6 +87,7 @@ Usuń listy odtwarzania Wyczyść listę odtwarzania Zapisz jako plik + Zapisz jako pliki "Dodaj do listy odtwarzania" Wszystkie losowo Losowy album @@ -112,6 +113,7 @@ Obecnie Grane Styl tła motywu Dźwięk + Biblioteka Obrazy Ekran blokady Lista odtwarzania @@ -128,6 +130,7 @@ Zmniejszaj głośność przy powiadomieniach Okres ostatnio dodanych list odtwarzania Pokazuj zsynchronizowany tekst + "Zapamiętaj ostatnią zakładkę " Nie znaleziono korektora "Odegraj utwór, następnie ponów próbę" Usuń @@ -135,6 +138,7 @@ Usuń Zmień Utwórz + Resetuj %1$d zaznaczono Kolor podstawowy Kolor dodatków @@ -146,6 +150,7 @@ Brak albumów Brak utworów Brak wykonawców + Brak gatunków Pusto Nazwa listy odtwarzania Utwór @@ -159,6 +164,7 @@ Kolory skrótów aplikacji w kolorze podstawowym Notyfikacje, nawigacja, itp. Obecnie wspierane są tylko napisy w formacie LRC oraz te osadzone w osobnym pliku. + Idź do ostatniej otwartej zakładki przy starcie "Nie uda\u0142o si\u0119 pobra\u0107 pasuj\u0105cej ok\u0142adki albumu" Szukaj w bibliotece... Ulubione @@ -189,14 +195,11 @@ Wersja O autorze Napisz maila - Dodaj do kręgów Google+ Śledź na Twitterze Aplikacja na GitHub\'ie Odwiedź stronę Zgłoś błędy Zgłaszaj błędy lub propozycje nowych funkcji - Dołącz do społeczności Google+ - Jeśli potrzebujesz pomocy lub masz pytania, społeczność Phonograph na Google+ jest do tego dobrym miejscem Tłumaczenie Pomóż przetłumaczyć Phonograph na Twój ojczysty język Oceń @@ -219,7 +222,10 @@ Foldery Zapisz listę odtwarzania do %s Błąd podczas zapisywania listy odtwarzania (%s) + Zapisano %1$d playliste w %2$s. + Zapisano %1$d playlistę w %2$s, niepowodzenie zapisu %3$d. %s nie figuruje w Media Store]]> + Niektóre pliki nie figuruje w Media Store. Nie ma nic do skanowania Przeskanowano %1$d z %2$d plików Nie można przeskanować %d plików @@ -268,6 +274,7 @@ Przywróć Nie znaleziono zakupu. Za jego wkład w kod źródłowy. + Za stworzenie design\'u strony albumu Dodaj Czarna lista Usuń z czarnej listy @@ -277,4 +284,15 @@ Zawartość czarnej listy jest niewidoczna w twojej bibliotece. Wyczyść obraz artysty Zaktualizuj zdjęcie wykonawcy + Kategorie biblioteki + Ustawia widoczność i kolejność kategorii biblioteki + Musisz wybrać co najmniej jedną kategorię + Skanuj ścieżkę + Skanuj media + Kolejność sortowania + Rosnąco + Malejąco + Artysta + Album + Rok diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 0d8dcadc9..6da5ad03c 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -1,7 +1,7 @@ O que você quer compartilhar? - Atualmente ouvindo %1$s por %2$s. + Atualmente ouvindo {% 1 $ s} em {% 2 $ s}. O arquivo de áudio %1$s foi definido como seu toque de chamada. Definir como toque de chamada @@ -9,11 +9,11 @@ "Configurações" Confirmar Sobre - Limpar playlist + Limpar lista de reprodução Adicionar aos favoritos Remover dos favoritos Pesquisar - Reproduzir à seguir + Reproduzir próxima Reproduzir Reproduzir/Pausar Anterior @@ -27,7 +27,7 @@ Renomear Excluir Escanear - Definir como diretório inicial + Definir como tela inicial Álbuns Artistas Gêneros @@ -44,11 +44,10 @@ Gênero Artista do álbum Ano - "Faixa (2 para à faixa 2 ou 3004 para a quarta faixa do CD3)" + "Música (2 para a música 2 ou 3004 para a música 4 do CD3)" Letra O título ou artista está vazio. Salvando alterações - Salvando no arquivo... Detalhes Nome do arquivo Endereço do arquivo @@ -59,23 +58,23 @@ Taxa de amostragem Ir para o artista Ir para o álbum - Fila de Reprodução + Fila de reprodução Sem resultados Atualizando... "Adicionado 1 título à lista de reprodução" - %1$d faixas adicionadas à fila de reprodução. + Adicionados {% 1 $ d} títulos á fila de reprodução Remover da playlist Número de colunas Número de colunas (horizontal) - Inserido %1$d músicas na playlist %2$s. - A playlist %1$s foi criada - "%1$d músicas excluídas. " - Uma playlist de nome %1$s já existe. - N\u00e3o foi poss\u00edvel criar playlist. + Inserido {%1$d} músicas na lista de reprodução {%2$s}. + Lista de reprodução criada {% 1 $ s}. + Excluídas {%1$s} músicas. + A lista de reprodução %1$s já existe. + N\u00e3o foi poss\u00edvel criar a lista de reprodu\u00e7\u00e3o. %1$s? ]]> - %1$s? Isto n\u00e3o pode ser desfeito!]]> - %1$d playlists?]]> - %1$s?]]> + }{%1$s}{}? Isso n\u00e3o pode ser desfeito!]]> + }{%1$d}{} listas de reprodução?]]> + }{%1$s}{}?]]> %1$d faixas?]]> %1$s da playlist?]]> %1$d músicas da playlist?]]> @@ -88,7 +87,9 @@ Excluir playlists Limpar playlist Salvar como arquivo + Salvar como arquivos "Adicionar à playlist" + Selecionar tudo Misturar todas Misturar álbum Misturar artista @@ -105,6 +106,7 @@ Nunca Hoje Essa semana + Última semana Esse mês Últimos 3 meses Esse ano @@ -131,6 +133,7 @@ Intervalo de adição das playlists mais recentes Exibir letras sincronizadas Lembrar última aba + Memorizar aleatorização Nenhum equalizador encontrado. "Sem identificação de áudio, reproduza algo e tente novamente." Excluir @@ -165,11 +168,12 @@ Notificações, navegação, etc. Atualmente damos suporte apenas a letras de música no formato LRC. Tanto as encorporadas quanto as anexadas por um arquivo separado. Ir para a última aba aberta ao iniciar + Modo aleatório continuará ativo quando uma nova lista de músicas for selecionada "N\u00e3o foi poss\u00edvel baixar uma capa do \u00e1lbum correspondente." Pesquisar na sua biblioteca... Favoritos Última Adição - Histórico + Reproduzidas recentemente Minhas melhores faixas Remover capa Baixar do Last.fm @@ -195,14 +199,11 @@ Versão Autor Escreva um email - Adicionar aos círculos do Google+ Seguir no Twitter Fazer um Fork no GitHub Visitar site Reportar bugs Reportar bugs ou pedir por novas funções. - Entre na comunidade do Google+ - Se você precisa de ajuda ou tem alguma dúvida, a comunidade do Phonograph no Google+ é um bom lugar pra ir. Traduzir Ajude a traduzir o Phonograph para sua língua nativa. Nota @@ -225,7 +226,10 @@ Pastas Salvo à playlist %s. Erro ao salvar a playlist (%s). + Salvado % 1 $ d lista de reprodução para%2$s. + Salvado %1$d listas de reprodução para %2$s, não foi possível salvar %3$d. %s não está listado no armazenamento de mídia.]]> + Alguns arquivos não estão listados no armazenamento de mídia. Nada para escanear. Foram escaneados %1$d dos arquivos %2$d. Não foi possível escanear os arquivos em %d. @@ -274,6 +278,7 @@ Restituir Nenhuma compra feita. Pela sua contribuição com o código-fonte. + Por criar o design da página de álbuns. Adicionar Lista Negra Remover da lista negra @@ -286,4 +291,13 @@ Categorias da biblioteca Configurar a visibilidade e a ordem das categorias da biblioteca. Você deve selecionar ao menos uma categoria. + Escanear pasta + Escanear mídia + Embaralhar ordem + Ascendente + Descendente + Artista + Álbum + Ano + Finalizar última música diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 62a60dfc5..af6b290cf 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -41,7 +41,6 @@ "Faixa (2 para faixa 2 ou 3004 para CD3 faixa 4)" O título do álbum ou o artista está vazio. A gravar alterações - A gravar no ficheiro... Detalhes Nome do ficheiro Local do ficheiro @@ -162,13 +161,10 @@ Obrigado! Versão Autor - Adicionar aos seus círculos do Google Plus Seguir no Twitter Visitar sítio na Internet Reportar erros Reportar erros ou sugerir novas funcionalidades. - Junte-se à comunidade Google Plus - Se necessitar de ajuda ou tiver dúvidas, a comunidade Phonograph no Google Plus é um bom sítio para ir. Traduzir Ajude a traduzir o Phonograph para o seu idioma nativo. Classifique diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 1f21d0048..fbc8a94d3 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -48,7 +48,6 @@ Versuri Titlul sau artistul lipseste. Se salvează schimbările - Salvând în fișier... Detalii Nume fișier Cale fișier @@ -88,6 +87,7 @@ Ștergeți listele de redare Ştergeţi lista de redare Salveaza ca fișier + Salvează ca fișiere "Adăugaţi la lista de redare" Amestecare tot Amestecare album @@ -195,14 +195,11 @@ Versiune Autor Scrieţi un e-mail - Adăugaţi în cercurile Google lus Urmăriţi pe Twitter Fork pe GitHub Vizitati site-ul web Raportaţi probleme Raportați probleme sau solicitați noi funcții. - Alăturați-vă comunității Google Plus - Dacă aveți nevoie de ajutor sau aveți întrebări, intraţi pe comunitatea Phonograph de pe Google Plus. Traducere Ajutaţi la traducerea Phonograph în limba maternă. Evaluaţi @@ -225,7 +222,10 @@ Dosare Listă de redare salvată în %s. Salvarea listei de redare (%s) a eșuat. + Au fost salvate %1$d liste de redare în %2$s. + Au fost salvate %1$d liste de redare în %2$s, salvarea %3$d eșuând. %s nu este listat(ă) în stocarea media.]]> + Unele fișiere nu sunt afișate în librăria media. Nimic de scanat. Scanate %1$d din %2$d fişiere. Nu s-au putut scana %d fişiere. @@ -237,7 +237,7 @@ Phonograph - Card Raportați o problemă Problemă - Login + Logare Titlu Descriere Informaţii dispozitiv @@ -274,6 +274,7 @@ Restabilire Nu s-a găsit nicio achiziție. Pentru contribuțiile sale la codul sursă. + Pentru crearea designului paginii albume Adăugare Listă neagră Eliminați din lista neagră @@ -286,4 +287,12 @@ Categorii Configurați vizibilitatea și ordinea categoriilor din bibliotecă. Trebuie să selectați cel puțin o categorie. + Scaneaza locatia + scanare media + Ordine de sortare + crescător + descrescător + Artist + Album + An diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index f18cae046..377dbf168 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -27,15 +27,15 @@ Переименовать Удалить Сканировать - Назначить начальной папкой + Назначить начальной директорией Альбомы Исполнители Жанры Треки Плейлисты - \u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 \u044d\u0442\u0443 \u043f\u0435\u0441\u043d\u044e. + \u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 \u044d\u0442\u0443 \u043f\u0435\u0441\u043d\u044e. \u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0431\u0438\u043e\u0433\u0440\u0430\u0444\u0438\u044e \u0438\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044f. - \u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0432 \u0432\u0438\u043a\u0438\u043f\u0435\u0434\u0438\u0438 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0430\u043b\u044c\u0431\u043e\u043c\u0430. + \u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0432 \u0412\u0438\u043a\u0438\u043f\u0435\u0434\u0438\u0438 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0430\u043b\u044c\u0431\u043e\u043c\u0430. Биография Википедия В получении аудиофокуса отказано. @@ -46,9 +46,8 @@ Год "Трек (2 для трека 2 или 3004 для трека 4 CD3)" Текст песни - Поля названия альбома или альбом исполнителя пусты + Поля названия альбома или альбом исполнителя пусты. Сохранение изменений - Сохраняем файл... Подробнее Имя файла Расположение @@ -62,19 +61,19 @@ Очередь воспроизведения Нет результатов Обновление... - "Композиция добавлена в очередь воспроизведения" + "Композиция добавлена в очередь воспроизведения." %1$d композиций добавлено в очередь воспроизведения Удалить из плейлиста Размер сетки Размер сетки (ландшафт) - %1$d композиций добавлено в плейлист %2$s - Плейлист %1$s создан - %1$d композиций удалено + %1$d композиций добавлено в плейлист %2$s. + Плейлист %1$s создан. + %1$d композиций удалено. Плейлист %1$s уже существует. - \u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u043b\u0435\u0439\u043b\u0438\u0441\u0442 + \u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u043b\u0435\u0439\u043b\u0438\u0441\u0442. %1$s?]]> %1$s? \u042d\u0442\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043d\u0435\u043e\u0431\u0440\u0430\u0442\u0438\u043c\u043e!]]> - %1$d ?]]> + %1$d плейлиста?]]> %1$s?]]> %1$d песни?]]> %1$s из плейлиста?]]> @@ -88,7 +87,9 @@ Удалить плейлисты Очистить плейлист Сохранить как файл + Сохранить как файлы "Добавить в плейлист" + Выбрать все Перемешать всё Перемешать альбом Перемешать исполнителя @@ -105,6 +106,7 @@ Никогда Сегодня Эта неделя + Последние 7 дней Этот месяц Последние 3 месяца Этот год @@ -121,16 +123,17 @@ Цветная панель управления Цветные шорткаты Показывать обложку альбома - Авто загрузка метаданных - "Размытая обложка альбома " + Автозагрузка метаданных + Размытая обложка альбома Окрашенное уведомление Стандартный дизайн уведомления Игнорировать хранилище обложек альбомов - Воспроизведение без переходов - Уменьшить громкость при уведомлених + Воспроизведение без пауз + Уменьшить громкость при уведомлениях Интервал последнего добавленного плейлиста Показать синхронизированные тексты Запомнить последнюю вкладку + Запомнить перемешивание Эквалайзер не найден. "Сначала воспроизведите песню, а затем попробуйте снова." Удалить @@ -142,7 +145,7 @@ %1$d выбрано Основной цвет Цвет акцента - Основной цвет темы, по умолчанию - индиго. + Основной цвет темы, по умолчанию — индиго. Цвет акцента, по умолчанию — розовый Обновить изображение Пустой плейлист @@ -165,11 +168,12 @@ Уведомления, навигация, т.д. В настоящее время поддерживаются только синхронизированные тексты в формате LRC. Либо встроенные, либо как отдельный файл. Перейти на последнюю открытую вкладку при запуске - "\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0443\u044e \u043e\u0431\u043b\u043e\u0436\u043a\u0443 \u0430\u043b\u044c\u0431\u043e\u043c\u0430." + Режим перемешивания останется включенным при выборе нового списка песен + "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0443\u044e \u043e\u0431\u043b\u043e\u0436\u043a\u0443 \u0430\u043b\u044c\u0431\u043e\u043c\u0430." Поиск в библиотеке... Избранное Последние добавленные - История прослушивания + Недавно воспроизведённые Мои популярные треки Удалить обложку Загрузить из Last.fm @@ -186,7 +190,7 @@ Особая благодарность Список изменений Лицензии - "В разрешениях отказано " + В разрешениях отказано. В разрешении на доступ к внешнему хранилищу отказано. Поддержать разработку Купить Phonograph Pro @@ -195,14 +199,11 @@ Версия Автор Написать на электронную почту - Добавить в круги Google Plus Следить в Twitter Ветвление на GitHub Посетить сайт Отправить отчёт об ошибке Сообщить об ошибках или предложить новые функции. - Вступить в сообщество на Google Plus - Сообщество Phonograph в Google Plus - место, где вы сможете задать вопрос или получить помощь. Перевести Помогите перевести Phonograph на ваш родной язык. Оценить @@ -225,16 +226,19 @@ Папки "Плейлист сохранён в %s " Не удалось сохранить плейлист (%s). - %s не указано в медиа хранилище.]]> + Сохранены %1$d плейлиста в %2$s. + Сохранены %1$d плейлиста в %2$s, не удалось сохранить %3$d. + %s не указано в медиа-хранилище.]]> + Некоторые файлы не указаны в медиа-хранилище. Нечего сканировать. Просканировано %1$d из %2$d файлов. Не удалось просканировать %d файлов. Список файлов %s новая начальная директория. - Phonograph - Большой - Phonograph - Классический - Phonograph - Маленький - Phonograph - Карточка + Phonograph — Большой + Phonograph — Классический + Phonograph — Маленький + Phonograph — Карточка Сообщить об ошибке Ошибка Войти @@ -245,7 +249,7 @@ Отправить вручную Имя пользователя Пароль - Пожалуйста, опишите ошибку здесь + Пожалуйста, опишите проблему здесь Пожалуйста, дайте подробное описание проблемы Пожалуйста, введите действительное имя пользователя GitHub Пожалуйста, введите действительный пароль GitHub @@ -262,8 +266,8 @@ Популярные треки Последние добавленные Плейлист пуст - С помощью уведомления воспроизведения можно воспроизводить/ставить на паузу и т.д. - Уведомление воспроизведения + С помощью уведомления о воспроизведении можно воспроизводить/ставить на паузу и т.д. + Уведомление о воспроизведении Только первые 5 цветов доступны в бесплатной версии. Чёрная тема это функция Phonograph Pro. Таймер сна это функция Phonograph Pro. @@ -272,8 +276,9 @@ Невозможно восстановить покупку. Покупка Восстановление - Покупка не найдена + Покупка не найдена. За его вклад в исходный код. + За создание дизайна показа альбомов. Добавить Чёрный список Убрать из чёрного списка @@ -286,4 +291,13 @@ Категории библиотеки Настройте видимость и порядок категорий библиотеки. Вы должны выбрать хотя бы одну категорию. + Сканировать + Сканировать медиа + Сортировка + По возрастанию + По убыванию + Исполнитель + Альбом + Год + Последняя песня завершилась diff --git a/app/src/main/res/values-sw600dp/dimens.xml b/app/src/main/res/values-sw600dp/dimens.xml index 7eb140f2e..355d33ff5 100644 --- a/app/src/main/res/values-sw600dp/dimens.xml +++ b/app/src/main/res/values-sw600dp/dimens.xml @@ -1,3 +1,4 @@ - 600dp + 176dp + 144dp diff --git a/app/src/main/res/values-sw600dp/integers.xml b/app/src/main/res/values-sw600dp/integers.xml index 826769fc2..aa8af21d0 100644 --- a/app/src/main/res/values-sw600dp/integers.xml +++ b/app/src/main/res/values-sw600dp/integers.xml @@ -8,4 +8,6 @@ 6 8 + + 3 \ No newline at end of file diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 311ca9491..d4002e9ed 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -48,7 +48,6 @@ Şarkı sözleri Albüm başlığı veya albüm sanatçısı bilgisi yok. Değişiklikler kaydediliyor - Dosyaya kaydediliyor... Ayrıntılar Dosya adı Dosya yolu @@ -88,6 +87,7 @@ Çalma listelerini sil Çalma listesini temizle Dosya olarak kaydet + Dosya olarak kaydet "Çalma listesine ekle" Tümünü karıştır Albümü karıştır @@ -125,7 +125,7 @@ Bulanık albüm kapağı Renkli bildirim Klasik bildirim tasarımı - Media Store albüm kapağını yoksay + Medya Deposu albüm kapağını yoksay Aralıksız çalma Odak kaybına neden olacak sesi azalt Son eklenen çalma listesi süresi @@ -195,14 +195,11 @@ Sürüm Yapımcı Bir e-posta yazın - Google+ çevrelerine ekle Twitter\'da takip et GitHub\'a bağlanın Web sitesini ziyaret edin Hataları raporlayın Hataları raporlayın ya da yeni özellikler talep edin. - Google+ grubuna katılın - Eğer yardıma ihtiyacınız varsa veya sorularınız varsa Google+ Phonograph topluluğu en doğru yer. Çeviri Phonograph\'ı kendi dilinize çevirmek için yardımcı olun. Oy ver @@ -225,7 +222,10 @@ Klasörler Çalma listesi %s kaydedildi. (%s) çalma listesine kaydetme başarısız. + %1$d oynatma listesini %2$s konumuna kaydedildi. + %1$d oynatma listesini %2$s konumuna kaydedildi, %3$d kaydedilemedi. %s medya deposu listesinde yok.]]> + Bazı dosyalar medya deposunda listelenmiyor. Taranacak bir şey yok %1$d - %2$d dosya taranıyor. %d dosya taranamadı. @@ -274,6 +274,7 @@ Geri yükle Satın alma bulunamadı. Kaynak koda yaptığı katkılardan dolayı. + Albüm sayfası tasarımı oluşturmak için. Ekle Kara liste Kara listeden kaldır @@ -286,4 +287,12 @@ Kitaplık kategorileri Kitaplık kategorilerinin görünürlüğünü ve sırasını yapılandırma. En az bir kategori seçmeniz gerekiyor. + Tarama dizini + Medyayı tara + Sıralama düzeni + Artan + Azalan + Sanatçı + Albüm + Yıl diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index e3a972e2c..7612e89f1 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -14,7 +14,10 @@ Видалити з улюблених Пошук Відтворити наступною - Відтворити + Грати + Відтворити/Зупинити + Попередня + Наступна Додати до черги відтворення Видалити з черги відтворення Додати до списку відтворення... @@ -24,14 +27,17 @@ Перейменувати Видалити Сканувати - Встановити в якості початкової теки + Встановити як початкову теку Альбоми Виконавці + Жанри Пісні Списки відтворення \u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u0432\u0456\u0434\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u043f\u0456\u0441\u043d\u044e. \u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u0437\u043d\u0430\u0439\u0442\u0438 \u0431\u0456\u043e\u0433\u0440\u0430\u0444\u0456\u044e \u0432\u0438\u043a\u043e\u043d\u0430\u0432\u0446\u044f. + \u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u0437\u043d\u0430\u0439\u0442\u0438 \u043d\u0430 \u0412\u0456\u043a\u0456\u043f\u0435\u0434\u0456\u0457 \u0441\u0442\u0430\u0442\u0442\u044e \u043f\u0440\u043e \u0446\u0435\u0439 \u0430\u043b\u044c\u0431\u043e\u043c. Біографія + Вікіпедія В отриманні аудіофокусу відмовлено. Альбом Виконавець @@ -39,9 +45,9 @@ Альбом виконавця Рік "Доріжка (2 для доріжки 2 або 3004 для CD3 доріжки 4)" + Текст пісні Назва або виконавець відсутні. Збереження змін - Збереження в файл... Подробиці Назва файлу Шлях до файлу @@ -63,6 +69,7 @@ Додано %1$d пісень до списку відтворення %2$s. Створено список відтворення %1$s. Видалено %1$d пісень. + Список відтворення %1$s вже існує. \u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u0441\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0456\u0434\u0442\u0432\u043e\u0440\u0435\u043d\u043d\u044f. %1$s?]]> %1$s? \u0414\u0456\u044e \u043d\u0435 \u043c\u043e\u0436\u043d\u0430 \u0431\u0443\u0434\u0435 \u0441\u043a\u0430\u0441\u0443\u0432\u0430\u0442\u0438!]]> @@ -86,6 +93,7 @@ Перемішати виконавця Перемішати список відтворення Очистити чергу відтворення + Зберегти чергу відтворення Перейти до початкової теки Показати текст Світла @@ -94,20 +102,34 @@ Завжди Тільки через Wi-Fi Ніколи + Сьогодні + Цього тижня + Цього місяця + Минулі 3 місяці + Цього року Еквалайзер Кольори Відтворюється Загальна тема Аудіо + Бібліотека Зображення Екран блокування + Списки відтворення + Сповіщення Кольорова панель навігації + Кольорові ярлики додатків Показати обкладинку альбому + Автоматично завантажені метадані Розмити обкладинку альбому Кольорове сповіщення + Класичне оформлення сповіщення Ігнорувати обкладинки з Медіасховища. Безперервне відтворення Зменшити гучнiсть при отриманнi повiдомлення + Інтервал останнього доданого списку відтворення + Показати синхронізований текст пісні + Запам‘ятати останню вкладку Еквалайзер не знайдено. "Спочатку відтворіть пісню, потім спробуйте ще раз." Видалити @@ -115,6 +137,7 @@ Видалити Перейменувати Створити + Скинути Вибрано {%1$d} Основний колір Колір підкреслення @@ -126,16 +149,21 @@ Немає альбомів Немає пісень Немає виконавців + Немає жанрів Порожньо Назва списку відтворення Пісня Використовувати обкладинку альбому поточної пісні як шпалери екрана блокування. Розмиває обладинку альбому на екрані блокування. Може конфліктувати зі сторонніми додатками та віджетами. + Використовувати класичне оформлення сповіщення. "\u0424\u0430\u0440\u0431\u0443\u0454 \u043f\u043e\u0432\u0456\u0434\u043e\u043c\u043b\u0435\u043d\u043d\u044f \u0432 \u043f\u0435\u0440\u0435\u0432\u0430\u0436\u043d\u0438\u0439 \u043a\u043e\u043b\u0456\u0440 \u043e\u0431\u043a\u043b\u0430\u0434\u0438\u043d\u043a\u0438 \u0430\u043b\u044c\u0431\u043e\u043c\u0443." "Може викликати проблеми з відтворенням на деяких пристроях." Може збільшити якість обкладинки альбому, але збільшує час завантаження зображення. Використовуйте тільки якщо у вас проблеми з обкладинками низької роздільної здатності. Фарбує панель навігації у переважний колір. + Фарбує ярлики додатків у переважний колір. Повiдомлення, навiгацiя, т.д. + Наразі підтримується тільки синхронізований текст у форматі LCR як вбудований або окремий файл. + Перейдіть до останньої відкритої вкладки, щоб запустити "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u0437\u0430\u0432\u0430\u043d\u0442\u0430\u0436\u0438\u0442\u0438 \u0432\u0456\u0434\u043f\u043e\u0432\u0456\u0434\u043d\u0443 \u043e\u0431\u043a\u043b\u0430\u0434\u0438\u043d\u043a\u0443 \u0430\u043b\u044c\u0431\u043e\u043c\u0443." Пошук бібліотеки... Улюблені @@ -160,16 +188,17 @@ У доступі відмовлено. Дозвіл на доступ до зовнішньої пам\'яті відмовлено. Підтримати розробника + Придбайте Phonograph Pro Щиро дякую! + Відновлено попередню покупку. Перезапустіть додаток, щоб скористатися всіма функціями. Версія Автор - Додати в кола Google Plus + Написати листа Слідкувати у Твіттері + Форк на GitHub Відвідати веб-сайт Повідомити про помилку Повідомити про помилку чи попросити нові функції. - Приєднатися до спільноти Google Plus - Якщо вам потрібна допомога або ви маєте будь-які питання, то вам слід долучитися до спільноти Phonograph у Google Plus. Перекласти Допоможіть перекласти Phonograph на свою рідну мову. Оцінити @@ -183,6 +212,7 @@ Веб-сайт Завантаження продуктів... До наступного + Вигляд Знайомство "Вітаємо вас у Phonograph, красивому та легкому музичному програвачу на Android. " Свайпніть картку на екрані «Зараз грає» щоб відкрити всю чергу відтворення. @@ -200,4 +230,33 @@ Phonograph - Великий Phonograph - Класичний Phonograph - Малий + Phonograph – Картки + Повідомити про проблему + Проблема + Ім’я користувача + Заголовок + Опис + Інформація про пристрій + Надіслати через обліковий запис GitHub + Надіслати вручну + Ім‘я користувача + Пароль + Введіть заголовок проблеми + Введіть опис проблеми + Введіть своє дійсне ім’я користувача GitHub + Введіть свій дійсний пароль користувача GitHub + Завантаження звіту на GitHub… + Не вдалося надіслати звіт + Неправильне ім’я користувача або пароль + Недійсний токен доступу. Зв’яжіться з розробником додатка. + Видалити з чорного списку + %1$s з чорного списку?]]> + Очистити чорний список + Дійсно очистити чорний список? + Вміст тек чорного списку приховано з вашої бібліотеки. + Скинути зображення виконавця + Встановити зображення виконавця + Категорії бібліотеки + Налаштувати видимість та порядок категорій бібліотеки. + Виберіть принаймні одну категорію. diff --git a/app/src/main/res/values-v21/fonts.xml b/app/src/main/res/values-v21/fonts.xml deleted file mode 100644 index 75fe3e010..000000000 --- a/app/src/main/res/values-v21/fonts.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-v21/integers.xml b/app/src/main/res/values-v21/integers.xml deleted file mode 100644 index a6b3daec9..000000000 --- a/app/src/main/res/values-v21/integers.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 0c41d731f..8b9285cde 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -7,11 +7,11 @@ Cài làm nhạc chuông Chia sẻ "Cài đặt" - Dành cho + Cho phép Thông tin Dọn danh sách nhạc - Thêm vào yêu thích - Xóa khỏi yêu thích + Thêm vào danh sách yêu thích + Xóa khỏi danh sách yêu thích Tìm kiếm Phát kế tiếp Phát @@ -19,9 +19,9 @@ Trước Kế tiếp Thêm vào danh sách phát - Xoá khỏi danh sách phát + Xóa khỏi hàng đợi phât Thêm vào danh sách nhạc... - Chỉnh sửa thẻ tag + Chỉnh sửa thẻ Xóa từ thiết bị Chi tiết Đổi tên @@ -41,13 +41,12 @@ Album Nghệ sĩ Thể loại - Album của Nghệ sĩ + Album của nghệ sĩ Năm "Số thứ tự (ghi 2 nếu là bài thứ 2 hoặc ghi 3004 nếu là CD 3, bài thứ 4)" Lời bài hát Tiêu đề hoặc nghệ sĩ rỗng. Đang lưu thay đổi - Đang lưu tập tin... Chi tiết Tên tập tin Đường dẫn tập tin @@ -55,7 +54,7 @@ Định dạng Độ dài Lượng dữ liệu - Tỷ lệ lấy mẫu + Tỉ lệ mẫu Đi đến nghệ sĩ Đi đến album Hàng đợi phát @@ -66,17 +65,17 @@ Xóa khỏi danh sách nhạc Kích thước lưới Kích thước lưới (màn hình nằm ngang) - Đã chèn %1$d bài hát vào danh sách nhạc %2$s. + Đã thêm %1$d baifhats vào danh sách nhạc %2$s Đã tạo danh sách nhạc %1$s. - Đã xóa %1$d bài hát. + Đã xóa bài hát %1$d Kh\u00f4ng th\u1ec3 t\u1ea1o danh s\u00e1ch nh\u1ea1c. %1$s?]]> %1$s? \u0110i\u1ec1u n\u00e0y kh\u00f4ng th\u1ec3 kh\u00f4i ph\u1ee5c!]]> - %1$d danh sách nhạc?]]> + %1$d? ]]> %1$s?]]> - %1$d bài hát?]]> + %1$d? ]]> %1$s từ danh sách nhạc này?]]> - %1$d bài hát từ danh sách nhạc?]]> + %1$d từ danh sách nhạc? ]]> Xóa bài hát Xóa các bài hát Di chuyển bài hát từ danh sách nhạc @@ -188,14 +187,11 @@ Phiên bản Tác giả Viết email - Thêm vào vòng kết nối Google Plus Theo dõi trên Twitter Fork trên GitHub Truy cập trang web Báo lỗi Báo lỗi và yêu cầu tính năng mới. - Tham gia cộng đồng Google Plus - Nếu bạn cần trợ giúp hoặc có câu hỏi, hãy đến với cộng đồng Phonograph trên Google Plus. Dịch Hỗ trợ dịch Phonograph sang ngôn ngữ của bạn. Đánh giá diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 43b743979..f134d15fa 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -48,7 +48,6 @@ 歌词 标题或艺术家名称为空。 正在保存更改 - 保存至文件... 详情 文件名 文件路径 @@ -88,7 +87,9 @@ 删除多个播放列表 清空播放列表 另存为 + 另存为 "添加到播放列表" + 全选 随机播放所有歌曲 随机播放此专辑 随机播放此艺术家 @@ -105,6 +106,7 @@ 从不 今日 本周 + 过去7天 本月 过去三个月 本年 @@ -195,14 +197,11 @@ 版本 作者 撰写邮件 - 添加到 Google+ 圈子 在 Twitter 上关注 创建 GitHub 分支 访问网站 提交 bug 提交 bug 或申请添加新功能 - 加入 Google+ 社群 - 如果您需要帮助或遇到问题,Google+ 上的 Phonograph 社群或许是个好去处。 翻译 帮助翻译 Phonograph 。 评分 @@ -225,7 +224,10 @@ 文件夹 已保存播放列表到 %s。 保存播放列表(%s)失败。 + 已保存 %1$d 播放列表到 %2$s。 + 已保存 %1$d 播放列表到 %2$s,保存 %3$d 失败。 %s 没有列在媒体储存中。]]> + 部分文件未在媒体储存中列出 没有可扫描的对象。 已扫描 %1$d/%2$d 个文件。 %d 个文件无法扫描。 @@ -274,6 +276,7 @@ 恢复 未找到购买记录 因他对源代码的贡献。 + 以新增专辑封面设计。 添加 黑名单 从黑名单中移除 @@ -286,4 +289,12 @@ 媒体库类别 配置媒体库类别可见性和顺序。 您必需选择至少一个类别。 + 扫描音乐 + 扫描音乐 + 排序方式 + 按首字符(正序) + 按首字符(倒序) + 艺术家 + 专辑 + 年份 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 17108b42e..49f64747b 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -49,7 +49,6 @@ 歌詞 專輯名稱或演出者欄是空的 正在儲存變更… - 正在儲存為檔案... 詳細資訊 檔案名稱 檔案路徑 @@ -89,7 +88,9 @@ 刪除多個播放清單 清空播放清單 儲存為檔案 + 儲存為檔案 "加入播放清單" + 全選 隨機播放 隨機播放專輯 歌手隨機播放 @@ -106,6 +107,7 @@ 永不 今日 本周 + 過去7天 這個月 過去 3 個月 今年 @@ -132,6 +134,7 @@ 最後新增列表的時間範圍 同步顯示歌詞 記住最後開啟的頁面 + 記住隨機播放 找不到等化器 "請先播放一首歌後再重試一遍。" 刪除 @@ -166,11 +169,12 @@ 通知鈴聲、導航語音等。 目前僅支援 LRC 格式歌詞。內嵌或外掛皆可。 開啟時顯示最後使用的頁面 + 當選擇新歌曲列表時維持隨機播放模式 "\u7121\u6cd5\u4e0b\u8f09\u5408\u9069\u7684\u5c08\u8f2f\u5c01\u9762\u3002" 搜尋音樂庫… 最愛 最後新增 - 記錄 + 最近播放 我的最佳單曲 移除封面 從 Last.fm 下載 @@ -196,14 +200,11 @@ 版本 作者 寫 email 給我們 - 加入 Google+ 社交圈 在 Twitter 上追蹤我們 造訪 GitHub 造訪網站 回報程式錯誤 建議新功能或回報問題 - 加入 Google+ 社交圈 - 如果您需要幫助或遇到問題,Google+ 社交圈的 Phonograph 社群是個發問的好地方。 翻譯 幫助我們將這款 App 翻譯成您的語言 評分 @@ -226,7 +227,10 @@ 文件夾 已儲存播放清單至%s 無法儲存播放清單 \"%s\" + 儲存播放清單 %1$d 至 %2$s + 儲存播放清單 %1$d 至 %2$s , %3$d 儲存失敗 %s未被列在音樂庫裡。]]> + 部分檔案未被列在音樂庫中。 沒有東西可掃描。 已掃描%2$d個檔案夾中的%1$d個。 不能掃描%d。 @@ -275,6 +279,7 @@ 恢復 找不到購買紀錄 原始碼貢獻者 + 創建專輯頁面 新增 黑名單 從黑名單中移除 @@ -287,4 +292,13 @@ 音樂庫類別 調整音樂庫類別的顯示與排序 你至少要選擇 1 個類別 + 掃描檔案 + 掃描音樂 + 排列方式 + 升序排列 + 降序排列 + 演出者 + 專輯 + 年份 + 歌曲全數播放完畢 diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 93cdc8a04..efd506aae 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -12,4 +12,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 685740194..be8a7a974 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,8 +1,13 @@ + + + #E0E0E0 + #34000000 #88000000 @color/md_blue_grey_500 @color/md_grey_50 - \ No newline at end of file + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 25f0dda93..4e4c5da8c 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -2,6 +2,8 @@ 16dp + 176dp + 144dp 72dp 4dp 2dp @@ -9,7 +11,6 @@ 168dp - 360dp 20dp 120dp 104dp diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 09f3d1e1a..6154cc59f 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -7,15 +7,17 @@ Maarten Corpel Michael Cook (Cookicons) Aleksandar Tešić - Eugene Cheung + Eugene Cheung + Adrian Aachen, Germany Google Plus GitHub + Twitter - Card - Flat + Card + Flat donation_1 @@ -28,16 +30,16 @@ body { - background-color: #{bg-color}; - color: #{text-color}; - font-family: sans-serif; - overflow-wrap: break-word; + background-color: #{bg-color}; + color: #{text-color}; + font-family: sans-serif; + overflow-wrap: break-word; } pre { - background-color: #{license-bg-color}; - padding: 1em; - white-space: pre-wrap; + background-color: #{license-bg-color}; + padding: 1em; + white-space: pre-wrap; } - \ No newline at end of file + diff --git a/app/src/main/res/values/fonts.xml b/app/src/main/res/values/fonts.xml deleted file mode 100644 index 90c249e37..000000000 --- a/app/src/main/res/values/fonts.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml deleted file mode 100644 index e2a255c81..000000000 --- a/app/src/main/res/values/ic_launcher_background.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - #E0E0E0 - \ No newline at end of file diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml index 6ac8ad2b3..a57563833 100644 --- a/app/src/main/res/values/ids.xml +++ b/app/src/main/res/values/ids.xml @@ -2,4 +2,18 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml index f12fd38b5..d36d11493 100644 --- a/app/src/main/res/values/integers.xml +++ b/app/src/main/res/values/integers.xml @@ -1,5 +1,6 @@ + 2 4 @@ -9,4 +10,6 @@ 4 6 - \ No newline at end of file + 3 + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 08b524719..bded8aa70 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -48,7 +48,6 @@ Lyrics The title or artist is empty. Saving changes - Saving to file… Details File name File path @@ -88,7 +87,9 @@ Delete playlists Clear playlist Save as file + Save as files "Add to playlist" + Select all Shuffle all Shuffle album Shuffle artist @@ -105,6 +106,7 @@ Never Today This week + Past 7 days This month Past 3 months This year @@ -131,6 +133,7 @@ Last added playlist interval Show synchronized lyrics Remember last tab + Remember shuffle No equalizer found. "Play a song first, then try again." Delete @@ -165,11 +168,12 @@ Notifications, navigation etc. Currently only synchronized lyrics in LRC format are supported. Either embedded or as a separate file. Go to the last opened tab on launch + Shuffle mode will stay on when selecting a new list of songs "Couldn\u2019t download a matching album cover." Search your library… Favorites Last added - History + Recently played My top tracks Remove cover Download from Last.fm @@ -203,14 +207,11 @@ Version Author Write an email - Add to Google Plus circles Follow on Twitter Fork on GitHub Visit website Report bugs Report bugs or request new features. - Join the Google Plus community - If you need help or have questions, the Phonograph community on Google Plus is a good place to go. Translate Help translating Phonograph to your native language. Rate @@ -233,7 +234,10 @@ Folders Saved playlist to %s. Failed to save playlist (%s). + Saved %1$d playlists to %2$s. + Saved %1$d playlists to %2$s, failed to save %3$d. %s is not listed in the media store.]]> + Some files are not listed in the media store. Nothing to scan. Scanned %1$d of %2$d files. Could not scan %d files. @@ -282,6 +286,7 @@ Restore No purchase found. For his contributions to the source code. + For creating the album page design. Add Blacklist Remove from blacklist @@ -294,4 +299,15 @@ Library categories Configure visibility and order of library categories. You have to select at least one category. + Scan directory + Scan media + Sort order + Ascending + Descending + Artist + Album + Year + Finish last song + Set ringtone + Allow phonograph to modify audio settings diff --git a/app/src/main/res/values/strings_activity_settings.xml b/app/src/main/res/values/strings_activity_settings.xml index 986a09dad..e877f40f5 100644 --- a/app/src/main/res/values/strings_activity_settings.xml +++ b/app/src/main/res/values/strings_activity_settings.xml @@ -27,6 +27,7 @@ @string/today @string/this_week + @string/past_seven_days @string/this_month @string/past_three_months @string/this_year @@ -35,6 +36,7 @@ today this_week + past_seven_days this_month past_three_months this_year diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 7252a7b93..8cb384a3c 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -30,7 +30,7 @@