diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 000000000..df6cb062c --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,18 @@ +name: Android CI + +on: [push, pull_request] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: set up JDK 11 + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: '11' + - name: Build with Gradle + run: ./gradlew assembleDebug diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b94e59548..000000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: android - -branches: - only: - - master - -script: "./gradlew clean assembleDebug" - -android: - components: - - platform-tools - - tools - - build-tools-24.0.3 - - android-24 - - extra-android-m2repository \ No newline at end of file diff --git a/README.md b/README.md index 53605457b..c3bc1fbfe 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,41 @@ # Timber -[![Build Status](https://travis-ci.org/naman14/Timber.svg?branch=master)](https://travis-ci.org/naman14/Timber) -[WIP][BETA]-Material Design Music Player +Material Design Music Player -Get it on Google Play -[![Get it on F-Droid](https://guardianproject.info/wp-content/uploads/2014/07/logo-fdroid.png)](https://f-droid.org/repository/browse/?fdid=naman14.timber) +This project is no longer in active development. Please refer to [TimberX](https://github.com/naman14/TimberX) instead -## Screenshots -![](https://raw.githubusercontent.com/naman14/Timber/master/graphics/Screenshot_2015-09-18-12-33-27.png) -![](https://raw.githubusercontent.com/naman14/Timber/master/graphics/Screenshot_2015-08-05-14-23-03.png) -![](https://raw.githubusercontent.com/naman14/Timber/master/graphics/Screenshot_2015-08-29-22-44-26.png) -![](https://raw.githubusercontent.com/naman14/Timber/master/graphics/Screenshot_2015-08-31-11-50-50.png) -![](https://raw.githubusercontent.com/naman14/Timber/master/graphics/Screenshot_2015-08-31-11-52-50.png) -## Contribute -### Translations +Get it on F-Droid -If there isn't any translations in your language go to the [res folder](https://github.com/naman14/Timber/blob/master/app/src/main/res/) and create a file named "values-XX/strings.xml" where XX is the target language's code. Copy and paste the content of the [English base file](https://github.com/naman14/Timber/blob/master/app/src/main/res/values/strings.xml) and remove all strings with "translatable=false" attribute. Translate all the keys and make a pull request. +## Screenshots -If there is a translation in your language go to its folder in [res folder](https://github.com/naman14/Timber/blob/master/app/src/main/res/) and edit the strings.xml file. You'd like to update the translation with new keys by copying them from the [English base file](https://github.com/naman14/Timber/blob/master/app/src/main/res/values/strings.xml). Edit the file and make a pull request. + + + + + + + + + + + + +## Features +- Material design +- Browse Songs, Albums, Artists +- Create and edit playlists +- 6 different now playing styles +- Homescreen widgets +- Browse device folders +- Dark theme and UI customisability +- Gestures for track switching +- LastFM scrobble +- Android Wear and Android Auto support +- Playing queue in notification (Xposed) +- Lyrics support +- Chromecast support ## Changelog @@ -34,10 +50,6 @@ Changelog is available [here](https://github.com/naman14/Timber/blob/master/Chan * [CircularSeekBar](https://github.com/devadvance/circularseekbar) * [Nammu](https://github.com/tajchert/Nammu) -#Donate -Paypal donation email- -namandwivedi14@gmail.com - ## License diff --git a/app/build.gradle b/app/build.gradle index 20602bd86..2963874c7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,30 +1,37 @@ apply plugin: 'com.android.application' + android { - compileSdkVersion 24 - buildToolsVersion '24.0.3' + compileSdkVersion rootProject.compileSdkVersion defaultConfig { - applicationId "naman14.timber" - minSdkVersion 16 - targetSdkVersion 24 - versionCode 16 - versionName "0.3b" + applicationId "it.fossoft.timberfoss" + minSdkVersion rootProject.minSdkVersion + targetSdkVersion rootProject.targetSdkVersion + versionCode 22 + versionName "1.8" //renderscript support mode is not supported for 21+ with gradle version 2.0 - renderscriptTargetApi 20 - renderscriptSupportModeEnabled true + renderscriptTargetApi 16 + renderscriptSupportModeEnabled false + multiDexEnabled true } + buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } debug { versionNameSuffix "-debug" minifyEnabled false + applicationIdSuffix '.dev' + manifestPlaceholders = [ appNameSuffix:" Dev"] + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + lintOptions { disable 'MissingTranslation' disable 'ExtraTranslation' @@ -37,29 +44,35 @@ repositories { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:24.2.1' - compile 'com.android.support:recyclerview-v7:24.2.1' - compile 'com.android.support:cardview-v7:24.2.1' - compile 'com.android.support:palette-v7:24.2.1' - compile 'com.android.support:design:24.2.1' - compile 'com.android.support:percent:24.2.1' - - compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.4' - compile 'net.steamcrafted:materialiconlib:1.0.3' - compile 'com.squareup.retrofit:retrofit:1.9.0' - compile 'com.squareup.okhttp:okhttp-urlconnection:2.3.0' - compile 'com.squareup.okhttp:okhttp:2.3.0' - compile 'com.google.code.gson:gson:2.3' - compile 'de.Maxr1998:track-selector-lib:1.1' - - compile 'com.afollestad.material-dialogs:core:0.9.0.2' - compile 'com.afollestad.material-dialogs:commons:0.9.0.2' - - compile('com.github.naman14:app-theme-engine:0.5.2@aar') { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "androidx.appcompat:appcompat:1.4.1" + implementation "com.google.android.material:material:1.6.0" + implementation "androidx.cardview:cardview:1.0.0" + implementation "androidx.recyclerview:recyclerview:1.2.1" + implementation "androidx.palette:palette:1.0.0" + implementation "androidx.percentlayout:percentlayout:1.0.0" + implementation 'androidx.multidex:multidex:2.0.1' + + implementation "androidx.mediarouter:mediarouter:1.3.0" + + + implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.4' + implementation 'net.steamcrafted:materialiconlib:1.1.4' + implementation 'com.squareup.retrofit:retrofit:1.9.0' + implementation 'com.squareup.okhttp:okhttp-urlconnection:2.3.0' + implementation 'com.squareup.okhttp:okhttp:2.3.0' + implementation 'com.google.code.gson:gson:2.8.6' + implementation 'de.Maxr1998:track-selector-lib:1.2' + + implementation 'com.afollestad.material-dialogs:core:0.9.0.2' + implementation 'com.afollestad.material-dialogs:commons:0.9.0.2' + + implementation('com.github.naman14:app-theme-engine:0.5.2@aar') { transitive = true } - compile 'com.anjlab.android.iab.v3:library:1.0.+' - + implementation 'com.anjlab.android.iab.v3:library:1.0.+' + implementation 'org.nanohttpd:nanohttpd:2.3.1' } + +apply from: '../mock.gradle' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0e114bdd5..8379bbb2c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,14 +18,14 @@ android:name=".TimberApp" android:allowBackup="false" android:icon="@mipmap/ic_launcher" - android:label="@string/app_name" + android:label="Timber" android:theme="@style/AppTheme.FullScreen.Light" - tools:replace="android:allowBackup"> + tools:replace="android:allowBackup, android:label"> + android:screenOrientation="portrait" + android:exported="true"> @@ -84,9 +84,11 @@ android:name=".activities.NowPlayingActivity" android:theme="@style/AppTheme.FullScreen.Light" /> - + + - + @@ -98,20 +100,18 @@ android:label="@string/app_name" android:process=":main" /> - - + --> + - - - + @@ -121,7 +121,8 @@ android:resource="@xml/widget_standard" /> - + @@ -131,7 +132,8 @@ android:resource="@xml/widget_white" /> - + diff --git a/app/src/main/aidl/com/naman14/timber/ITimberService.aidl b/app/src/main/aidl/com/naman14/timber/ITimberService.aidl index d545adcbe..10236c828 100644 --- a/app/src/main/aidl/com/naman14/timber/ITimberService.aidl +++ b/app/src/main/aidl/com/naman14/timber/ITimberService.aidl @@ -48,6 +48,5 @@ interface ITimberService int getRepeatMode(); int getMediaMountedCount(); int getAudioSessionId(); - void setLockscreenAlbumArt(boolean enabled); } diff --git a/app/src/main/assets/materialdesignicons-webfont.ttf b/app/src/main/assets/materialdesignicons-webfont.ttf index 8bb426d3b..69404e3d9 100644 Binary files a/app/src/main/assets/materialdesignicons-webfont.ttf and b/app/src/main/assets/materialdesignicons-webfont.ttf differ diff --git a/app/src/main/java/com/naman14/timber/MusicPlayer.java b/app/src/main/java/com/naman14/timber/MusicPlayer.java index 9c8eea716..feb0ce270 100644 --- a/app/src/main/java/com/naman14/timber/MusicPlayer.java +++ b/app/src/main/java/com/naman14/timber/MusicPlayer.java @@ -99,16 +99,7 @@ public static void next() { } public static void initPlaybackServiceWithSettings(final Context context) { - setShowAlbumArtOnLockscreen(true); - } - public static void setShowAlbumArtOnLockscreen(final boolean enabled) { - try { - if (mService != null) { - mService.setLockscreenAlbumArt(enabled); - } - } catch (final RemoteException ignored) { - } } public static void asyncNext(final Context context) { @@ -515,27 +506,19 @@ public static void playNext(Context context, final long[] list, final long sourc public static void shuffleAll(final Context context) { Cursor cursor = SongLoader.makeSongCursor(context, null, null); - final long[] mTrackList = SongLoader.getSongListForCursor(cursor); - final int position = 0; - if (mTrackList.length == 0 || mService == null) { + final long[] trackList = SongLoader.getSongListForCursor(cursor); + if (trackList.length == 0 || mService == null) { return; } try { mService.setShuffleMode(MusicService.SHUFFLE_NORMAL); - final long mCurrentId = mService.getAudioId(); - final int mCurrentQueuePosition = getQueuePosition(); - if (position != -1 && mCurrentQueuePosition == position - && mCurrentId == mTrackList[position]) { - final long[] mPlaylist = getQueue(); - if (Arrays.equals(mTrackList, mPlaylist)) { + if (getQueuePosition() == 0 && mService.getAudioId() == trackList[0] && Arrays.equals(trackList, getQueue())) { mService.play(); return; - } } - mService.open(mTrackList, -1, -1, IdType.NA.mId); + mService.open(trackList, -1, -1, IdType.NA.mId); mService.play(); cursor.close(); - cursor = null; } catch (final RemoteException ignored) { } } @@ -624,6 +607,8 @@ public static void seek(final long position) { try { mService.seek(position); } catch (final RemoteException ignored) { + } catch (IllegalStateException ignored) { + } } } diff --git a/app/src/main/java/com/naman14/timber/MusicService.java b/app/src/main/java/com/naman14/timber/MusicService.java index 48426eac3..398c9670f 100644 --- a/app/src/main/java/com/naman14/timber/MusicService.java +++ b/app/src/main/java/com/naman14/timber/MusicService.java @@ -17,8 +17,11 @@ import android.Manifest; import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.app.AlarmManager; import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; @@ -35,7 +38,10 @@ import android.graphics.Color; import android.media.AudioManager; import android.media.AudioManager.OnAudioFocusChangeListener; +import android.media.MediaMetadataEditor; +import android.media.MediaMetadataRetriever; import android.media.MediaPlayer; +import android.media.RemoteControlClient; import android.media.audiofx.AudioEffect; import android.net.Uri; import android.os.Build; @@ -52,12 +58,13 @@ import android.provider.MediaStore; import android.provider.MediaStore.Audio.AlbumColumns; import android.provider.MediaStore.Audio.AudioColumns; -import android.support.v4.app.NotificationManagerCompat; +import androidx.core.app.NotificationManagerCompat; import android.support.v4.media.MediaMetadataCompat; import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.PlaybackStateCompat; -import android.support.v7.app.NotificationCompat; -import android.support.v7.graphics.Palette; + +import androidx.media.app.NotificationCompat; +import androidx.palette.graphics.Palette; import android.text.TextUtils; import android.util.Log; @@ -120,6 +127,8 @@ public class MusicService extends Service { public static final String CMDPREVIOUS = "previous"; public static final String CMDNEXT = "next"; public static final String CMDNOTIF = "buttonId"; + public static final String UPDATE_PREFERENCES = "updatepreferences"; + public static final String CHANNEL_ID = "timber_channel_01"; public static final int NEXT = 2; public static final int LAST = 3; public static final int SHUFFLE_NONE = 0; @@ -188,6 +197,8 @@ public class MusicService extends Service { private boolean mPausedByTransientLossOfFocus = false; private MediaSessionCompat mSession; + @SuppressWarnings("deprecation") + private RemoteControlClient mRemoteControlClient; private ComponentName mMediaButtonReceiverComponent; @@ -223,6 +234,7 @@ public void onAudioFocusChange(final int focusChange) { private BroadcastReceiver mUnmountReceiver = null; private MusicPlaybackState mPlaybackStateStore; private boolean mShowAlbumArtOnLockscreen; + private boolean mActivateXTrackSelector; private SongPlayCount mSongPlayCount; private RecentStore mRecentStore; private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @@ -277,6 +289,7 @@ public void onCreate() { super.onCreate(); mNotificationManager = NotificationManagerCompat.from(this); + createNotificationChannel(); // gets a pointer to the playback state store mPlaybackStateStore = MusicPlaybackState.getInstance(this); @@ -299,6 +312,8 @@ public void onCreate() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) setUpMediaSession(); + else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) + setUpRemoteControlClient(); mPreferences = getSharedPreferences("Service", 0); mCardId = getCardId(); @@ -319,6 +334,8 @@ public void onCreate() { filter.addAction(PREVIOUS_FORCE_ACTION); filter.addAction(REPEAT_ACTION); filter.addAction(SHUFFLE_ACTION); + filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY); + filter.addAction(Intent.ACTION_SCREEN_ON); // Attach the broadcast listener registerReceiver(mIntentReceiver, filter); @@ -345,6 +362,35 @@ public void onCreate() { reloadQueueAfterPermissionCheck(); notifyChange(QUEUE_CHANGED); notifyChange(META_CHANGED); + //Try to push LastFMCache + if (LastfmUserSession.getSession(this) != null) { + LastFmClient.getInstance(this).Scrobble(null); + } + PreferencesUtility pref = PreferencesUtility.getInstance(this); + mShowAlbumArtOnLockscreen = pref.getSetAlbumartLockscreen(); + mActivateXTrackSelector = pref.getXPosedTrackselectorEnabled(); + } + + @SuppressWarnings("deprecation") + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) + private void setUpRemoteControlClient() { + //Legacy for ICS + if (mRemoteControlClient == null) { + Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); + mediaButtonIntent.setComponent(mMediaButtonReceiverComponent); + PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, 0); + + // create and register the remote control client + mRemoteControlClient = new RemoteControlClient(mediaPendingIntent); + mAudioManager.registerRemoteControlClient(mRemoteControlClient); + } + + mRemoteControlClient.setTransportControlFlags( + RemoteControlClient.FLAG_KEY_MEDIA_PLAY | + RemoteControlClient.FLAG_KEY_MEDIA_PAUSE | + RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS | + RemoteControlClient.FLAG_KEY_MEDIA_NEXT | + RemoteControlClient.FLAG_KEY_MEDIA_STOP); } private void setUpMediaSession() { @@ -384,13 +430,18 @@ public void onStop() { releaseServiceUiAndStop(); } }); - mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); + mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS + | MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS); } @Override public void onDestroy() { if (D) Log.d(TAG, "Destroying service"); super.onDestroy(); + //Try to push LastFMCache + if (LastfmUserSession.getSession(this).isLogedin()) { + LastFmClient.getInstance(this).Scrobble(null); + } // Remove any sound effects final Intent audioEffectsIntent = new Intent( AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); @@ -454,9 +505,11 @@ public int onStartCommand(final Intent intent, final int flags, final int startI } void scrobble() { - if (LastfmUserSession.getSession(this) != null) { + if (LastfmUserSession.getSession(this).isLogedin()) { Log.d("Scrobble", "to LastFM"); - LastFmClient.getInstance(this).Scrobble(new ScrobbleQuery(getArtistName(), getTrackName(), (System.currentTimeMillis() - duration()) / 1000)); + String trackname = getTrackName(); + if (trackname != null) + LastFmClient.getInstance(this).Scrobble(new ScrobbleQuery(getArtistName(), trackname, System.currentTimeMillis() / 1000)); } } @@ -516,9 +569,30 @@ private void handleCommandIntent(Intent intent) { cycleRepeat(); } else if (SHUFFLE_ACTION.equals(action)) { cycleShuffle(); + } else if (UPDATE_PREFERENCES.equals(action)) { + onPreferencesUpdate(intent.getExtras()); + } + else if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(action)) { + if (PreferencesUtility.getInstance(getApplicationContext()).pauseEnabledOnDetach()) { + pause(); + } } } + private void onPreferencesUpdate(Bundle extras) { + mShowAlbumArtOnLockscreen = extras.getBoolean("lockscreen", mShowAlbumArtOnLockscreen); + mActivateXTrackSelector = extras.getBoolean("xtrack",mActivateXTrackSelector); + LastfmUserSession session = LastfmUserSession.getSession(this); + session.mToken = extras.getString("lf_token", session.mToken); + session.mUsername = extras.getString("lf_user", session.mUsername); + if ("logout".equals(session.mToken)) { + session.mToken = null; + session.mUsername = null; + } + notifyChange(META_CHANGED); + + } + private void updateNotification() { final int newNotifyMode; if (isPlaying()) { @@ -634,6 +708,12 @@ private void cancelShutdown() { private void stop(final boolean goToIdle) { if (D) Log.d(TAG, "Stopping playback, goToIdle = " + goToIdle); + long duration = this.duration(); + long position = this.position(); + if (duration > 30000 && (position >= duration / 2) || position > 240000) { + scrobble(); + } + if (mPlayer.isInitialized()) { mPlayer.stop(); } @@ -1028,6 +1108,8 @@ private void notifyChange(final String what) { // Update the lockscreen controls if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) updateMediaSession(what); + else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) + updateRemoteControlClient(what); if (what.equals(POSITION_CHANGED)) { return; @@ -1037,6 +1119,7 @@ private void notifyChange(final String what) { intent.putExtra("id", getAudioId()); intent.putExtra("artist", getArtistName()); intent.putExtra("album", getAlbumName()); + intent.putExtra("albumid", getAlbumId()); intent.putExtra("track", getTrackName()); intent.putExtra("playing", isPlaying()); @@ -1072,6 +1155,42 @@ && getShuffleMode() != SHUFFLE_NONE) { } + @SuppressWarnings("deprecation") + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) + private void updateRemoteControlClient(final String what) { + //Legacy for ICS + if (mRemoteControlClient != null) { + int playState = mIsSupposedToBePlaying + ? RemoteControlClient.PLAYSTATE_PLAYING + : RemoteControlClient.PLAYSTATE_PAUSED; + if (what.equals(META_CHANGED) || what.equals(QUEUE_CHANGED)) { + Bitmap albumArt = null; + if (mShowAlbumArtOnLockscreen) { + albumArt = ImageLoader.getInstance().loadImageSync(TimberUtils.getAlbumArtUri(getAlbumId()).toString()); + if (albumArt != null) { + + Bitmap.Config config = albumArt.getConfig(); + if (config == null) { + config = Bitmap.Config.ARGB_8888; + } + albumArt = albumArt.copy(config, false); + } + } + + RemoteControlClient.MetadataEditor editor = mRemoteControlClient.editMetadata(true); + editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, getAlbumName()); + editor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, getArtistName()); + editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, getTrackName()); + editor.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, duration()); + editor.putBitmap(MediaMetadataEditor.BITMAP_KEY_ARTWORK, albumArt); + editor.apply(); + + } + mRemoteControlClient.setPlaybackState(playState); + } + } + + private void updateMediaSession(final String what) { int playState = mIsSupposedToBePlaying ? PlaybackStateCompat.STATE_PLAYING @@ -1086,14 +1205,17 @@ private void updateMediaSession(final String what) { .build()); } } else if (what.equals(META_CHANGED) || what.equals(QUEUE_CHANGED)) { - Bitmap albumArt = ImageLoader.getInstance().loadImageSync(TimberUtils.getAlbumArtUri(getAlbumId()).toString()); - if (albumArt != null) { - - Bitmap.Config config = albumArt.getConfig(); - if (config == null) { - config = Bitmap.Config.ARGB_8888; + Bitmap albumArt = null; + if (mShowAlbumArtOnLockscreen) { + albumArt = ImageLoader.getInstance().loadImageSync(TimberUtils.getAlbumArtUri(getAlbumId()).toString()); + if (albumArt != null) { + + Bitmap.Config config = albumArt.getConfig(); + if (config == null) { + config = Bitmap.Config.ARGB_8888; + } + albumArt = albumArt.copy(config, false); } - albumArt = albumArt.copy(config, false); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mSession.setMetadata(new MediaMetadataCompat.Builder() @@ -1105,8 +1227,7 @@ private void updateMediaSession(final String what) { .putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, getQueuePosition() + 1) .putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, getQueue().length) .putString(MediaMetadataCompat.METADATA_KEY_GENRE, getGenreName()) - .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, - mShowAlbumArtOnLockscreen ? albumArt : null) + .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt) .build()); mSession.setPlaybackState(new PlaybackStateCompat.Builder() @@ -1118,6 +1239,16 @@ private void updateMediaSession(final String what) { } } + private void createNotificationChannel() { + if (TimberUtils.isOreo()) { + CharSequence name = "Timber"; + int importance = NotificationManager.IMPORTANCE_LOW; + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance); + manager.createNotificationChannel(mChannel); + } + } + private Notification buildNotification() { final String albumName = getAlbumName(); final String artistName = getArtistName(); @@ -1141,7 +1272,7 @@ private Notification buildNotification() { mNotificationPostTime = System.currentTimeMillis(); } - android.support.v4.app.NotificationCompat.Builder builder = new NotificationCompat.Builder(this) + androidx.core.app.NotificationCompat.Builder builder = new androidx.core.app.NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) .setLargeIcon(artwork) .setContentIntent(clickIntent) @@ -1160,6 +1291,7 @@ private Notification buildNotification() { if (TimberUtils.isJellyBeanMR1()) { builder.setShowWhen(false); } + if (TimberUtils.isLollipop()) { builder.setVisibility(Notification.VISIBILITY_PUBLIC); NotificationCompat.MediaStyle style = new NotificationCompat.MediaStyle() @@ -1167,11 +1299,17 @@ private Notification buildNotification() { .setShowActionsInCompactView(0, 1, 2, 3); builder.setStyle(style); } - if (artwork != null && TimberUtils.isLollipop()) + if (artwork != null && TimberUtils.isLollipop()) { builder.setColor(Palette.from(artwork).generate().getVibrantColor(Color.parseColor("#403f4d"))); + } + + if (TimberUtils.isOreo()) { + builder.setColorized(true); + } + Notification n = builder.build(); - if (PreferencesUtility.getInstance(this).getXPosedTrackselectorEnabled()) { + if (mActivateXTrackSelector) { addXTrackSelector(n); } @@ -1193,8 +1331,7 @@ private void addXTrackSelector(Notification n) { ArrayList list = new ArrayList<>(); do { TrackItem t = new TrackItem() - .setArt(ImageLoader.getInstance() - .loadImageSync(TimberUtils.getAlbumArtUri(c.getLong(c.getColumnIndexOrThrow(AudioColumns.ALBUM_ID))).toString())) + .setArt(TimberUtils.getAlbumArtUri(c.getLong(c.getColumnIndexOrThrow(AudioColumns.ALBUM_ID)))) .setTitle(c.getString(c.getColumnIndexOrThrow(AudioColumns.TITLE))) .setArtist(c.getString(c.getColumnIndexOrThrow(AudioColumns.ARTIST))) .setDuration(TimberUtils.makeShortTimeString(this, c.getInt(c.getColumnIndexOrThrow(AudioColumns.DURATION)) / 1000)); @@ -1964,8 +2101,6 @@ public void setAndRecordPlayPos(int nextPos) { public void prev(boolean forcePrevious) { synchronized (this) { - - boolean goPrevious = getRepeatMode() != REPEAT_CURRENT && (position() < REWIND_INSTEAD_PREVIOUS_THRESHOLD || forcePrevious); @@ -2100,11 +2235,6 @@ public void playlistChanged() { notifyChange(PLAYLIST_CHANGED); } - public void setLockscreenAlbumArt(boolean enabled) { - mShowAlbumArtOnLockscreen = enabled; - notifyChange(META_CHANGED); - } - public interface TrackErrorExtra { String TRACK_NAME = "trackname"; @@ -2172,7 +2302,6 @@ public void handleMessage(final Message msg) { service.updateNotification(); break; case TRACK_ENDED: - mService.get().scrobble(); if (service.mRepeatMode == REPEAT_CURRENT) { service.seek(0); service.play(); @@ -2704,12 +2833,6 @@ public int getAudioSessionId() throws RemoteException { return mService.get().getAudioSessionId(); } - - @Override - public void setLockscreenAlbumArt(boolean enabled) { - mService.get().setLockscreenAlbumArt(enabled); - } - } private class MediaStoreObserver extends ContentObserver implements Runnable { @@ -2737,5 +2860,4 @@ public void run() { refresh(); } } - } diff --git a/app/src/main/java/com/naman14/timber/TimberApp.java b/app/src/main/java/com/naman14/timber/TimberApp.java index 5016e9c9f..a0151c241 100644 --- a/app/src/main/java/com/naman14/timber/TimberApp.java +++ b/app/src/main/java/com/naman14/timber/TimberApp.java @@ -14,17 +14,22 @@ package com.naman14.timber; -import android.app.Application; +import androidx.multidex.MultiDexApplication; import com.afollestad.appthemeengine.ATE; import com.naman14.timber.permissions.Nammu; +import com.naman14.timber.utils.PreferencesUtility; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; +import com.nostra13.universalimageloader.core.download.BaseImageDownloader; import com.nostra13.universalimageloader.utils.L; -public class TimberApp extends Application { +import java.io.IOException; +import java.io.InputStream; +public class TimberApp extends MultiDexApplication { + private static TimberApp mInstance; public static synchronized TimberApp getInstance() { @@ -36,7 +41,17 @@ public void onCreate() { super.onCreate(); mInstance = this; - ImageLoaderConfiguration localImageLoaderConfiguration = new ImageLoaderConfiguration.Builder(this).build(); + ImageLoaderConfiguration localImageLoaderConfiguration = new ImageLoaderConfiguration.Builder(this).imageDownloader(new BaseImageDownloader(this) { + PreferencesUtility prefs = PreferencesUtility.getInstance(TimberApp.this); + + @Override + protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException { + if (prefs.loadArtistAndAlbumImages()) + return super.getStreamFromNetwork(imageUri, extra); + throw new IOException(); + } + }).build(); + ImageLoader.getInstance().init(localImageLoaderConfiguration); L.writeLogs(false); L.disableLogging(); diff --git a/app/src/main/java/com/naman14/timber/WearBrowserService.java b/app/src/main/java/com/naman14/timber/WearBrowserService.java index db31e1523..949d703a9 100644 --- a/app/src/main/java/com/naman14/timber/WearBrowserService.java +++ b/app/src/main/java/com/naman14/timber/WearBrowserService.java @@ -24,7 +24,7 @@ import android.os.AsyncTask; import android.os.Bundle; import android.service.media.MediaBrowserService; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import com.naman14.timber.dataloaders.AlbumLoader; import com.naman14.timber.dataloaders.AlbumSongLoader; diff --git a/app/src/main/java/com/naman14/timber/activities/BaseActivity.java b/app/src/main/java/com/naman14/timber/activities/BaseActivity.java index c113c164e..d74727e70 100644 --- a/app/src/main/java/com/naman14/timber/activities/BaseActivity.java +++ b/app/src/main/java/com/naman14/timber/activities/BaseActivity.java @@ -22,13 +22,12 @@ import android.content.IntentFilter; import android.content.ServiceConnection; import android.media.AudioManager; -import android.media.session.MediaSessionManager; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; -import android.support.annotation.Nullable; -import android.support.v4.app.FragmentManager; +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentManager; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -36,10 +35,12 @@ import com.afollestad.appthemeengine.ATE; import com.afollestad.appthemeengine.ATEActivity; + import com.naman14.timber.ITimberService; import com.naman14.timber.MusicPlayer; import com.naman14.timber.MusicService; import com.naman14.timber.R; +import com.naman14.timber.cast.WebServer; import com.naman14.timber.listeners.MusicStateListener; import com.naman14.timber.slidinguppanel.SlidingUpPanelLayout; import com.naman14.timber.subfragments.QuickControlsFragment; @@ -47,6 +48,7 @@ import com.naman14.timber.utils.NavigationUtils; import com.naman14.timber.utils.TimberUtils; +import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -58,6 +60,10 @@ public class BaseActivity extends ATEActivity implements ServiceConnection, Musi private MusicPlayer.ServiceToken mToken; private PlaybackStatus mPlaybackStatus; + private WebServer castServer; + + + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -92,24 +98,31 @@ protected void onStart() { @Override protected void onStop() { super.onStop(); - - } @Override public void onResume() { - super.onResume(); + //For Android 8.0+: service may get destroyed if in background too long + if(mService == null){ + mToken = MusicPlayer.bindToService(this, this); + } onMetaChanged(); + super.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); } @Override public void onServiceConnected(final ComponentName name, final IBinder service) { mService = ITimberService.Stub.asInterface(service); - onMetaChanged(); } + @Override public void onServiceDisconnected(final ComponentName name) { mService = null; @@ -180,6 +193,10 @@ public void removeMusicStateListenerListener(final MusicStateListener status) { @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); + + getMenuInflater().inflate(R.menu.menu_cast, menu); + + if (!TimberUtils.hasEffectsPanel(BaseActivity.this)) { menu.removeItem(R.id.action_equalizer); } @@ -229,19 +246,22 @@ public void setPanelSlideListeners(SlidingUpPanelLayout panelLayout) { @Override public void onPanelSlide(View panel, float slideOffset) { View nowPlayingCard = QuickControlsFragment.topContainer; - nowPlayingCard.setAlpha(1 - slideOffset); + if (nowPlayingCard != null) + nowPlayingCard.setAlpha(1 - slideOffset); } @Override public void onPanelCollapsed(View panel) { View nowPlayingCard = QuickControlsFragment.topContainer; - nowPlayingCard.setAlpha(1); + if (nowPlayingCard != null) + nowPlayingCard.setAlpha(1); } @Override public void onPanelExpanded(View panel) { View nowPlayingCard = QuickControlsFragment.topContainer; - nowPlayingCard.setAlpha(0); + if (nowPlayingCard != null) + nowPlayingCard.setAlpha(0); } @Override @@ -300,12 +320,6 @@ protected String doInBackground(String... params) { @Override protected void onPostExecute(String result) { -// QuickControlsFragment.topContainer.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// NavigationUtils.navigateToNowplaying(BaseActivity.this, false); -// } -// }); } @Override @@ -313,4 +327,26 @@ protected void onPreExecute() { } } + public void showCastMiniController() { + //implement by overriding in activities + } + + public void hideCastMiniController() { + //implement by overriding in activities + } + + private void startCastServer() { + castServer = new WebServer(this); + try { + castServer.start(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void stopCastServer() { + if (castServer != null) { + castServer.stop(); + } + } } diff --git a/app/src/main/java/com/naman14/timber/activities/BaseThemedActivity.java b/app/src/main/java/com/naman14/timber/activities/BaseThemedActivity.java index f46d09c9f..9454bd11f 100644 --- a/app/src/main/java/com/naman14/timber/activities/BaseThemedActivity.java +++ b/app/src/main/java/com/naman14/timber/activities/BaseThemedActivity.java @@ -2,7 +2,7 @@ import android.media.AudioManager; import android.os.Bundle; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import com.afollestad.appthemeengine.ATEActivity; import com.naman14.timber.utils.Helpers; diff --git a/app/src/main/java/com/naman14/timber/activities/DonateActivity.java b/app/src/main/java/com/naman14/timber/activities/DonateActivity.java index 737c0f411..997ab6d3b 100644 --- a/app/src/main/java/com/naman14/timber/activities/DonateActivity.java +++ b/app/src/main/java/com/naman14/timber/activities/DonateActivity.java @@ -3,7 +3,7 @@ import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; -import android.support.v7.widget.Toolbar; +import androidx.appcompat.widget.Toolbar; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -16,6 +16,7 @@ import com.anjlab.android.iab.v3.SkuDetails; import com.anjlab.android.iab.v3.TransactionDetails; import com.naman14.timber.R; +import com.naman14.timber.utils.PreferencesUtility; import java.util.ArrayList; import java.util.Collections; @@ -42,6 +43,8 @@ public class DonateActivity extends BaseThemedActivity implements BillingProcess private ProgressBar progressBar; private TextView status; + private String action = "support"; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -51,11 +54,12 @@ protected void onCreate(Bundle savedInstanceState) { setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setTitle("Donate"); + getSupportActionBar().setTitle("Support development"); + action = getIntent().getAction(); + productListView = (LinearLayout) findViewById(R.id.product_list); progressBar = (ProgressBar) findViewById(R.id.progressBar); status = (TextView) findViewById(R.id.donation_status); - bp = new BillingProcessor(this, "PLAY_LICENSE_KEY", this); } @@ -64,7 +68,8 @@ protected void onCreate(Bundle savedInstanceState) { public void onBillingInitialized() { readyToPurchase = true; checkStatus(); - getProducts(); + if (!(action != null && action.equals("restore"))) + getProducts(); } @Override @@ -73,7 +78,7 @@ public void onProductPurchased(String productId, TransactionDetails details) { runOnUiThread(new Runnable() { @Override public void run() { - Toast.makeText(DonateActivity.this, "Thanks for your donation!", Toast.LENGTH_SHORT).show(); + Toast.makeText(DonateActivity.this, "Thanks for your support!", Toast.LENGTH_SHORT).show(); } }); } @@ -119,7 +124,19 @@ protected Boolean doInBackground(Void... voids) { protected void onPostExecute(Boolean b) { super.onPostExecute(b); if (b) { - status.setText("Thanks for your donation!"); + PreferencesUtility.getInstance(DonateActivity.this).setFullUnlocked(true); + status.setText("Thanks for your support!"); + if (action!=null && action.equals("restore")) { + status.setText("Your purchases has been restored. Thanks for your support"); + progressBar.setVisibility(View.GONE); + } + if (getSupportActionBar() != null) + getSupportActionBar().setTitle("Support development"); + } else { + if (action!=null && action.equals("restore")) { + status.setText("No previous purchase found"); + getProducts(); + } } } }.execute(); diff --git a/app/src/main/java/com/naman14/timber/activities/MainActivity.java b/app/src/main/java/com/naman14/timber/activities/MainActivity.java index 94ee213d0..463f5deb1 100644 --- a/app/src/main/java/com/naman14/timber/activities/MainActivity.java +++ b/app/src/main/java/com/naman14/timber/activities/MainActivity.java @@ -16,25 +16,28 @@ import android.Manifest; import android.content.Intent; -import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; -import android.support.design.widget.NavigationView; -import android.support.design.widget.Snackbar; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.support.v4.view.GravityCompat; -import android.support.v4.widget.DrawerLayout; +import com.google.android.material.navigation.NavigationView; +import com.google.android.material.snackbar.Snackbar; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; +import androidx.core.view.GravityCompat; +import androidx.drawerlayout.widget.DrawerLayout; +import android.view.Gravity; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import com.afollestad.appthemeengine.customizers.ATEActivityThemeCustomizer; import com.anjlab.android.iab.v3.BillingProcessor; + import com.naman14.timber.MusicPlayer; import com.naman14.timber.R; import com.naman14.timber.fragments.AlbumDetailFragment; @@ -46,6 +49,7 @@ import com.naman14.timber.permissions.Nammu; import com.naman14.timber.permissions.PermissionCallback; import com.naman14.timber.slidinguppanel.SlidingUpPanelLayout; +import com.naman14.timber.subfragments.LyricsFragment; import com.naman14.timber.utils.Constants; import com.naman14.timber.utils.Helpers; import com.naman14.timber.utils.NavigationUtils; @@ -58,17 +62,18 @@ public class MainActivity extends BaseActivity implements ATEActivityThemeCustomizer { + private SlidingUpPanelLayout panelLayout; + private NavigationView navigationView; + private TextView songtitle, songartist; + private ImageView albumart; + private String action; + private Map navigationMap = new HashMap(); + private Handler navDrawerRunnable = new Handler(); + private Runnable runnable; + private DrawerLayout mDrawerLayout; + private boolean isDarkTheme; - private static MainActivity sMainActivity; - SlidingUpPanelLayout panelLayout; - NavigationView navigationView; - TextView songtitle, songartist; - ImageView albumart; - String action; - Map navigationMap = new HashMap(); - Handler navDrawerRunnable = new Handler(); - Runnable runnable; - Runnable navigateLibrary = new Runnable() { + private Runnable navigateLibrary = new Runnable() { public void run() { navigationView.getMenu().findItem(R.id.nav_library).setChecked(true); Fragment fragment = new MainFragment(); @@ -77,24 +82,8 @@ public void run() { } }; - Runnable navigateNowplaying = new Runnable() { - public void run() { - navigateLibrary.run(); - startActivity(new Intent(MainActivity.this, NowPlayingActivity.class)); - } - }; - final PermissionCallback permissionReadstorageCallback = new PermissionCallback() { - @Override - public void permissionGranted() { - loadEverything(); - } - @Override - public void permissionRefused() { - finish(); - } - }; - Runnable navigatePlaylist = new Runnable() { + private Runnable navigatePlaylist = new Runnable() { public void run() { navigationView.getMenu().findItem(R.id.nav_playlists).setChecked(true); Fragment fragment = new PlaylistFragment(); @@ -104,7 +93,8 @@ public void run() { } }; - Runnable navigateFolder = new Runnable() { + + private Runnable navigateFolder = new Runnable() { public void run() { navigationView.getMenu().findItem(R.id.nav_folders).setChecked(true); Fragment fragment = new FoldersFragment(); @@ -114,7 +104,8 @@ public void run() { } }; - Runnable navigateQueue = new Runnable() { + + private Runnable navigateQueue = new Runnable() { public void run() { navigationView.getMenu().findItem(R.id.nav_queue).setChecked(true); Fragment fragment = new QueueFragment(); @@ -124,7 +115,8 @@ public void run() { } }; - Runnable navigateAlbum = new Runnable() { + + private Runnable navigateAlbum = new Runnable() { public void run() { long albumID = getIntent().getExtras().getLong(Constants.ALBUM_ID); Fragment fragment = AlbumDetailFragment.newInstance(albumID, false, null); @@ -133,7 +125,8 @@ public void run() { .replace(R.id.fragment_container, fragment).commit(); } }; - Runnable navigateArtist = new Runnable() { + + private Runnable navigateArtist = new Runnable() { public void run() { long artistID = getIntent().getExtras().getLong(Constants.ARTIST_ID); Fragment fragment = ArtistDetailFragment.newInstance(artistID, false, null); @@ -142,17 +135,39 @@ public void run() { .replace(R.id.fragment_container, fragment).commit(); } }; - private DrawerLayout mDrawerLayout; - private boolean isDarkTheme; - public static MainActivity getInstance() { - return sMainActivity; - } + private Runnable navigateLyrics = new Runnable() { + public void run() { + Fragment fragment = new LyricsFragment(); + FragmentManager fragmentManager = getSupportFragmentManager(); + fragmentManager.beginTransaction() + .replace(R.id.fragment_container, fragment).commit(); + } + }; + + private Runnable navigateNowplaying = new Runnable() { + public void run() { + navigateLibrary.run(); + startActivity(new Intent(MainActivity.this, NowPlayingActivity.class)); + } + }; + + private final PermissionCallback permissionReadstorageCallback = new PermissionCallback() { + @Override + public void permissionGranted() { + loadEverything(); + } + + @Override + public void permissionRefused() { + finish(); + } + }; + @Override public void onCreate(Bundle savedInstanceState) { - sMainActivity = this; action = getIntent().getAction(); isDarkTheme = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("dark_theme", false); @@ -166,6 +181,7 @@ public void onCreate(Bundle savedInstanceState) { navigationMap.put(Constants.NAVIGATE_NOWPLAYING, navigateNowplaying); navigationMap.put(Constants.NAVIGATE_ALBUM, navigateAlbum); navigationMap.put(Constants.NAVIGATE_ARTIST, navigateArtist); + navigationMap.put(Constants.NAVIGATE_LYRICS, navigateLyrics); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); panelLayout = (SlidingUpPanelLayout) findViewById(R.id.sliding_layout); @@ -190,6 +206,7 @@ public void run() { if (TimberUtils.isMarshmallow()) { checkPermissionAndThenLoad(); + //checkWritePermissions(); } else { loadEverything(); } @@ -209,6 +226,11 @@ public void run() { }, 350); } + if (!panelLayout.isPanelHidden() && MusicPlayer.getTrackName() == null ) { + panelLayout.hidePanel(); + } + + } private void loadEverything() { @@ -224,7 +246,7 @@ private void loadEverything() { private void checkPermissionAndThenLoad() { //check for permission - if (Nammu.checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE)) { + if (Nammu.checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE) && Nammu.checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { loadEverything(); } else { if (Nammu.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) { @@ -233,18 +255,20 @@ private void checkPermissionAndThenLoad() { .setAction("OK", new View.OnClickListener() { @Override public void onClick(View view) { - Nammu.askForPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE, permissionReadstorageCallback); + Nammu.askForPermission(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, permissionReadstorageCallback); } }).show(); } else { - Nammu.askForPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE, permissionReadstorageCallback); + Nammu.askForPermission(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, permissionReadstorageCallback); } } } + @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); + return true; } @@ -297,7 +321,6 @@ private void setupNavigationIcons(NavigationView navigationView) { navigationView.getMenu().findItem(R.id.nav_folders).setIcon(R.drawable.ic_folder_open_black_24dp); navigationView.getMenu().findItem(R.id.nav_nowplaying).setIcon(R.drawable.bookmark_music); navigationView.getMenu().findItem(R.id.nav_settings).setIcon(R.drawable.settings); - navigationView.getMenu().findItem(R.id.nav_help).setIcon(R.drawable.help_circle); navigationView.getMenu().findItem(R.id.nav_about).setIcon(R.drawable.information); navigationView.getMenu().findItem(R.id.nav_donate).setIcon(R.drawable.payment_black); } else { @@ -307,7 +330,6 @@ private void setupNavigationIcons(NavigationView navigationView) { navigationView.getMenu().findItem(R.id.nav_folders).setIcon(R.drawable.ic_folder_open_white_24dp); navigationView.getMenu().findItem(R.id.nav_nowplaying).setIcon(R.drawable.bookmark_music_white); navigationView.getMenu().findItem(R.id.nav_settings).setIcon(R.drawable.settings_white); - navigationView.getMenu().findItem(R.id.nav_help).setIcon(R.drawable.help_circle_white); navigationView.getMenu().findItem(R.id.nav_about).setIcon(R.drawable.information_white); navigationView.getMenu().findItem(R.id.nav_donate).setIcon(R.drawable.payment_white); } @@ -348,12 +370,6 @@ private void updatePosition(final MenuItem menuItem) { case R.id.nav_settings: NavigationUtils.navigateToSettings(MainActivity.this); break; - case R.id.nav_help: - Intent intent = new Intent(Intent.ACTION_VIEW); - Uri data = Uri.parse("mailto:namandwivedi14@gmail.com"); - intent.setData(data); - startActivity(intent); - break; case R.id.nav_about: mDrawerLayout.closeDrawers(); Handler handler = new Handler(); @@ -402,12 +418,10 @@ public void setDetailsToHeader() { public void onMetaChanged() { super.onMetaChanged(); setDetailsToHeader(); - } - @Override - public void onResume() { - super.onResume(); - sMainActivity = this; + if (panelLayout.isPanelHidden() && MusicPlayer.getTrackName() != null) { + panelLayout.showPanel(); + } } @Override @@ -442,6 +456,23 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); getSupportFragmentManager().findFragmentById(R.id.fragment_container).onActivityResult(requestCode, resultCode, data); } + + + @Override + public void showCastMiniController() { + findViewById(R.id.castMiniController).setVisibility(View.VISIBLE); + findViewById(R.id.quickcontrols_container).setVisibility(View.GONE); + panelLayout.hidePanel(); + } + + @Override + public void hideCastMiniController() { + + findViewById(R.id.castMiniController).setVisibility(View.GONE); + findViewById(R.id.quickcontrols_container).setVisibility(View.VISIBLE); + + panelLayout.showPanel(); + } } diff --git a/app/src/main/java/com/naman14/timber/activities/NowPlayingActivity.java b/app/src/main/java/com/naman14/timber/activities/NowPlayingActivity.java index 89a6abb32..46cd4bf2c 100644 --- a/app/src/main/java/com/naman14/timber/activities/NowPlayingActivity.java +++ b/app/src/main/java/com/naman14/timber/activities/NowPlayingActivity.java @@ -5,12 +5,13 @@ import android.graphics.Color; import android.os.Bundle; import android.preference.PreferenceManager; -import android.support.annotation.StyleRes; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; +import androidx.annotation.StyleRes; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; import com.afollestad.appthemeengine.Config; import com.afollestad.appthemeengine.customizers.ATEActivityThemeCustomizer; +import com.afollestad.appthemeengine.customizers.ATEStatusBarCustomizer; import com.afollestad.appthemeengine.customizers.ATEToolbarCustomizer; import com.naman14.timber.R; import com.naman14.timber.utils.Constants; @@ -20,7 +21,7 @@ /** * Created by naman on 01/01/16. */ -public class NowPlayingActivity extends BaseActivity implements ATEActivityThemeCustomizer, ATEToolbarCustomizer { +public class NowPlayingActivity extends BaseActivity implements ATEActivityThemeCustomizer, ATEToolbarCustomizer, ATEStatusBarCustomizer { @Override public void onCreate(Bundle savedInstanceState) { @@ -48,11 +49,21 @@ public int getLightToolbarMode() { return Config.LIGHT_TOOLBAR_AUTO; } + @Override + public int getLightStatusBarMode() { + return Config.LIGHT_STATUS_BAR_OFF; + } + @Override public int getToolbarColor() { return Color.TRANSPARENT; } + @Override + public int getStatusBarColor() { + return Color.TRANSPARENT; + } + @Override public void onResume() { super.onResume(); diff --git a/app/src/main/java/com/naman14/timber/activities/PlaylistDetailActivity.java b/app/src/main/java/com/naman14/timber/activities/PlaylistDetailActivity.java index d94c822d0..a02ea6f02 100644 --- a/app/src/main/java/com/naman14/timber/activities/PlaylistDetailActivity.java +++ b/app/src/main/java/com/naman14/timber/activities/PlaylistDetailActivity.java @@ -23,12 +23,12 @@ import android.os.Handler; import android.preference.PreferenceManager; import android.provider.MediaStore; -import android.support.annotation.NonNull; -import android.support.annotation.StyleRes; -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.NonNull; +import androidx.annotation.StyleRes; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.appcompat.widget.Toolbar; import android.transition.Transition; import android.util.Log; import android.view.Menu; @@ -52,7 +52,6 @@ import com.naman14.timber.listeners.SimplelTransitionListener; import com.naman14.timber.models.Song; import com.naman14.timber.utils.Constants; -import com.naman14.timber.utils.PreferencesUtility; import com.naman14.timber.utils.TimberUtils; import com.naman14.timber.widgets.DividerItemDecoration; import com.naman14.timber.widgets.DragSortRecycler; @@ -64,41 +63,43 @@ public class PlaylistDetailActivity extends BaseActivity implements ATEActivityThemeCustomizer, ATEToolbarCustomizer { - String action; - long playlistID; - HashMap playlistsMap = new HashMap<>(); - Runnable playlistLastAdded = new Runnable() { + private String action; + private long playlistID; + private HashMap playlistsMap = new HashMap<>(); + + private AppCompatActivity mContext = PlaylistDetailActivity.this; + private SongsListAdapter mAdapter; + private RecyclerView recyclerView; + private ImageView blurFrame; + private TextView playlistname; + private View foreground; + private boolean animate; + + private Runnable playlistLastAdded = new Runnable() { public void run() { new loadLastAdded().execute(""); } }; - Runnable playlistRecents = new Runnable() { + private Runnable playlistRecents = new Runnable() { @Override public void run() { new loadRecentlyPlayed().execute(""); } }; - Runnable playlistToptracks = new Runnable() { + private Runnable playlistToptracks = new Runnable() { @Override public void run() { new loadTopTracks().execute(""); } }; - Runnable playlistUsercreated = new Runnable() { + private Runnable playlistUsercreated = new Runnable() { @Override public void run() { new loadUserCreatedPlaylist().execute(""); } }; - private AppCompatActivity mContext = PlaylistDetailActivity.this; - private SongsListAdapter mAdapter; - private RecyclerView recyclerView; - private ImageView blurFrame; - private TextView playlistname; - private View foreground; - private boolean animate; @TargetApi(21) @Override @@ -130,12 +131,22 @@ public void onCreate(Bundle savedInstanceState) { setAlbumart(); animate = getIntent().getBooleanExtra(Constants.ACTIVITY_TRANSITION, false); - if (animate && TimberUtils.isLollipop() && PreferencesUtility.getInstance(this).getAnimations()) { - getWindow().getEnterTransition().addListener(new EnterTransitionListener()); + if (animate && TimberUtils.isLollipop()) { + if(savedInstanceState != null && savedInstanceState.containsKey("ROTATION_RECREATION")){ + setUpSongs(); + } + else{ + getWindow().getEnterTransition().addListener(new EnterTransitionListener()); + } } else { setUpSongs(); } + } + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString("ROTATION_RECREATION", "Rotacion"); } private void setAlbumart() { @@ -184,7 +195,7 @@ private void loadBitmap(String uri) { private void setRecyclerViewAapter() { recyclerView.setAdapter(mAdapter); - if (animate && TimberUtils.isLollipop() && PreferencesUtility.getInstance(mContext).getAnimations()) { + if (animate && TimberUtils.isLollipop()) { Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override @@ -378,6 +389,13 @@ private void clearAutoPlaylists() { finish(); } + @Override + public void onMetaChanged() { + super.onMetaChanged(); + if (mAdapter != null) + mAdapter.notifyDataSetChanged(); + } + @Override public int getToolbarColor() { return Color.TRANSPARENT; diff --git a/app/src/main/java/com/naman14/timber/activities/SearchActivity.java b/app/src/main/java/com/naman14/timber/activities/SearchActivity.java index d5c3652f4..2c114bc6e 100644 --- a/app/src/main/java/com/naman14/timber/activities/SearchActivity.java +++ b/app/src/main/java/com/naman14/timber/activities/SearchActivity.java @@ -17,12 +17,13 @@ import android.content.Context; import android.os.AsyncTask; import android.os.Bundle; -import android.support.annotation.Nullable; -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.Nullable; +import androidx.core.view.MenuItemCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.appcompat.widget.SearchView; +import androidx.appcompat.widget.Toolbar; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; @@ -45,7 +46,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; -public class SearchActivity extends BaseThemedActivity implements SearchView.OnQueryTextListener, View.OnTouchListener { +public class SearchActivity extends BaseActivity implements SearchView.OnQueryTextListener, View.OnTouchListener { private final Executor mSearchExecutor = Executors.newSingleThreadExecutor(); @Nullable @@ -60,6 +61,8 @@ public class SearchActivity extends BaseThemedActivity implements SearchView.OnQ private List searchResults = Collections.emptyList(); + Bundle bundle; + @Override public void onCreate(Bundle savedInstanceState) { @@ -76,6 +79,18 @@ public void onCreate(Bundle savedInstanceState) { recyclerView.setLayoutManager(new LinearLayoutManager(this)); adapter = new SearchAdapter(this); recyclerView.setAdapter(adapter); + + if(savedInstanceState != null && savedInstanceState.containsKey("QUERY_STRING")){ + bundle = savedInstanceState; + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + if (queryString != null){ + outState.putString("QUERY_STRING", queryString); + } } @@ -106,9 +121,23 @@ public boolean onMenuItemActionCollapse(MenuItem item) { }); menu.findItem(R.id.menu_search).expandActionView(); + + if(bundle != null && bundle.containsKey("QUERY_STRING")){ + mSearchView.setQuery(bundle.getString("QUERY_STRING"), true); + } + return super.onCreateOptionsMenu(menu); } + + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + MenuItem item = menu.findItem(R.id.action_search); + item.setVisible(false); + return true; + } + @Override public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { @@ -146,6 +175,7 @@ public boolean onQueryTextChange(final String newText) { adapter.notifyDataSetChanged(); } else { mSearchTask = new SearchTask().executeOnExecutor(mSearchExecutor, queryString); + Log.d("AAAABBBBBB", "TaskCanelled? " + (mSearchTask.isCancelled())); } return true; @@ -186,8 +216,8 @@ protected ArrayList doInBackground(String... params) { results.add(getString(R.string.songs)); results.addAll(songList); } - - if (isCancelled()) { + boolean canceled = isCancelled(); + if (canceled) { return null; } List albumList = AlbumLoader.getAlbums(SearchActivity.this, params[0], 7); @@ -196,7 +226,8 @@ protected ArrayList doInBackground(String... params) { results.addAll(albumList); } - if (isCancelled()) { + canceled = isCancelled(); + if (canceled) { return null; } List artistList = ArtistLoader.getArtists(SearchActivity.this, params[0], 7); diff --git a/app/src/main/java/com/naman14/timber/activities/SettingsActivity.java b/app/src/main/java/com/naman14/timber/activities/SettingsActivity.java index 47f5a0761..5c1be7f96 100644 --- a/app/src/main/java/com/naman14/timber/activities/SettingsActivity.java +++ b/app/src/main/java/com/naman14/timber/activities/SettingsActivity.java @@ -17,12 +17,12 @@ import android.os.Bundle; import android.preference.PreferenceFragment; import android.preference.PreferenceManager; -import android.support.annotation.ColorInt; -import android.support.annotation.NonNull; -import android.support.annotation.StyleRes; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v7.widget.Toolbar; +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.StyleRes; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.appcompat.widget.Toolbar; import android.view.MenuItem; import com.afollestad.appthemeengine.ATE; @@ -37,7 +37,7 @@ public class SettingsActivity extends BaseThemedActivity implements ColorChooserDialog.ColorCallback, ATEActivityThemeCustomizer { - String action; + private String action; @Override public void onCreate(Bundle savedInstanceState) { diff --git a/app/src/main/java/com/naman14/timber/adapters/AlbumAdapter.java b/app/src/main/java/com/naman14/timber/adapters/AlbumAdapter.java index 5060712b5..e9486254a 100644 --- a/app/src/main/java/com/naman14/timber/adapters/AlbumAdapter.java +++ b/app/src/main/java/com/naman14/timber/adapters/AlbumAdapter.java @@ -16,8 +16,8 @@ import android.app.Activity; import android.graphics.Bitmap; -import android.support.v7.graphics.Palette; -import android.support.v7.widget.RecyclerView; +import androidx.palette.graphics.Palette; +import androidx.recyclerview.widget.RecyclerView; import android.util.Pair; import android.view.LayoutInflater; import android.view.View; @@ -75,7 +75,7 @@ public void onBindViewHolder(final ItemHolder itemHolder, int i) { ImageLoader.getInstance().displayImage(TimberUtils.getAlbumArtUri(localItem.id).toString(), itemHolder.albumArt, new DisplayImageOptions.Builder().cacheInMemory(true) - .showImageOnFail(R.drawable.ic_empty_music2) + .showImageOnLoading(R.drawable.ic_empty_music2) .resetViewBeforeLoading(true) .displayer(new FadeInBitmapDisplayer(400)) .build(), new SimpleImageLoadingListener() { diff --git a/app/src/main/java/com/naman14/timber/adapters/AlbumSongsAdapter.java b/app/src/main/java/com/naman14/timber/adapters/AlbumSongsAdapter.java index 58e0d614a..12d9b30ec 100644 --- a/app/src/main/java/com/naman14/timber/adapters/AlbumSongsAdapter.java +++ b/app/src/main/java/com/naman14/timber/adapters/AlbumSongsAdapter.java @@ -16,8 +16,8 @@ import android.app.Activity; import android.os.Handler; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.RecyclerView; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -35,7 +35,7 @@ import java.util.List; -public class AlbumSongsAdapter extends RecyclerView.Adapter { +public class AlbumSongsAdapter extends BaseSongAdapter { private List arraylist; private Activity mContext; @@ -109,6 +109,9 @@ public boolean onMenuItemClick(MenuItem item) { case R.id.popup_song_addto_playlist: AddPlaylistDialog.newInstance(arraylist.get(position)).show(((AppCompatActivity) mContext).getSupportFragmentManager(), "ADD_PLAYLIST"); break; + case R.id.popup_song_share: + TimberUtils.shareTrack(mContext, arraylist.get(position).id); + break; case R.id.popup_song_delete: long[] deleteIds = {arraylist.get(position).id}; TimberUtils.showDeleteDialog(mContext,arraylist.get(position).title, deleteIds, AlbumSongsAdapter.this, position); @@ -137,11 +140,17 @@ public long[] getSongIds() { return ret; } + @Override public void updateDataSet(List arraylist) { this.arraylist = arraylist; this.songIDs = getSongIds(); } + @Override + public void removeSongAt(int i){ + arraylist.remove(i); + } + public class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener { protected TextView title, duration, trackNumber; protected ImageView menu; @@ -161,8 +170,9 @@ public void onClick(View v) { handler.postDelayed(new Runnable() { @Override public void run() { - MusicPlayer.playAll(mContext, songIDs, getAdapterPosition(), albumID, TimberUtils.IdType.Album, false); - NavigationUtils.navigateToNowplaying(mContext, true); + playAll(mContext, songIDs, getAdapterPosition(), albumID, + TimberUtils.IdType.Album, false, + arraylist.get(getAdapterPosition()), true); } }, 100); diff --git a/app/src/main/java/com/naman14/timber/adapters/ArtistAdapter.java b/app/src/main/java/com/naman14/timber/adapters/ArtistAdapter.java index 3a4d59b05..6706bc8a9 100644 --- a/app/src/main/java/com/naman14/timber/adapters/ArtistAdapter.java +++ b/app/src/main/java/com/naman14/timber/adapters/ArtistAdapter.java @@ -17,9 +17,9 @@ import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Color; -import android.support.annotation.ColorInt; -import android.support.v7.graphics.Palette; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.ColorInt; +import androidx.palette.graphics.Palette; +import androidx.recyclerview.widget.RecyclerView; import android.util.Pair; import android.view.LayoutInflater; import android.view.View; @@ -94,7 +94,7 @@ public void artistInfoSucess(LastfmArtist artist) { ImageLoader.getInstance().displayImage(artist.mArtwork.get(2).mUrl, itemHolder.artistImage, new DisplayImageOptions.Builder().cacheInMemory(true) .cacheOnDisk(true) - .showImageOnFail(R.drawable.ic_empty_music2) + .showImageOnLoading(R.drawable.ic_empty_music2) .resetViewBeforeLoading(true) .displayer(new FadeInBitmapDisplayer(400)) .build(), new SimpleImageLoadingListener() { @@ -136,7 +136,7 @@ public void onLoadingFailed(String imageUri, View view, FailReason failReason) { ImageLoader.getInstance().displayImage(artist.mArtwork.get(1).mUrl, itemHolder.artistImage, new DisplayImageOptions.Builder().cacheInMemory(true) .cacheOnDisk(true) - .showImageOnFail(R.drawable.ic_empty_music2) + .showImageOnLoading(R.drawable.ic_empty_music2) .resetViewBeforeLoading(true) .displayer(new FadeInBitmapDisplayer(400)) .build()); @@ -155,6 +155,11 @@ public void artistInfoFailed() { } + @Override + public long getItemId(int position) { + return arraylist.get(position).id; + } + @Override public int getItemCount() { return (null != arraylist ? arraylist.size() : 0); diff --git a/app/src/main/java/com/naman14/timber/adapters/ArtistAlbumAdapter.java b/app/src/main/java/com/naman14/timber/adapters/ArtistAlbumAdapter.java index 0cfccf2ef..e656e47c4 100644 --- a/app/src/main/java/com/naman14/timber/adapters/ArtistAlbumAdapter.java +++ b/app/src/main/java/com/naman14/timber/adapters/ArtistAlbumAdapter.java @@ -15,8 +15,8 @@ package com.naman14.timber.adapters; import android.app.Activity; -import android.support.v7.widget.CardView; -import android.support.v7.widget.RecyclerView; +import androidx.cardview.widget.CardView; +import androidx.recyclerview.widget.RecyclerView; import android.util.Pair; import android.view.LayoutInflater; import android.view.View; @@ -26,10 +26,9 @@ import com.naman14.timber.R; import com.naman14.timber.models.Album; +import com.naman14.timber.utils.ImageUtils; import com.naman14.timber.utils.NavigationUtils; import com.naman14.timber.utils.TimberUtils; -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; import java.util.List; @@ -60,11 +59,7 @@ public void onBindViewHolder(ItemHolder itemHolder, int i) { String songCount = TimberUtils.makeLabel(mContext, R.plurals.Nsongs, localItem.songCount); itemHolder.details.setText(songCount); - ImageLoader.getInstance().displayImage(TimberUtils.getAlbumArtUri(localItem.id).toString(), itemHolder.albumArt, - new DisplayImageOptions.Builder().cacheInMemory(true) - .showImageOnFail(R.drawable.ic_empty_music2) - .resetViewBeforeLoading(true) - .build()); + ImageUtils.loadAlbumArtIntoView(localItem.id, itemHolder.albumArt); if (TimberUtils.isLollipop()) itemHolder.albumArt.setTransitionName("transition_album_art" + i); diff --git a/app/src/main/java/com/naman14/timber/adapters/ArtistSongAdapter.java b/app/src/main/java/com/naman14/timber/adapters/ArtistSongAdapter.java index 44f4634f9..b80c64792 100644 --- a/app/src/main/java/com/naman14/timber/adapters/ArtistSongAdapter.java +++ b/app/src/main/java/com/naman14/timber/adapters/ArtistSongAdapter.java @@ -17,9 +17,9 @@ import android.app.Activity; import android.graphics.Rect; import android.os.Handler; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -41,7 +41,7 @@ import java.util.ArrayList; import java.util.List; -public class ArtistSongAdapter extends RecyclerView.Adapter { +public class ArtistSongAdapter extends BaseSongAdapter { private List arraylist; private Activity mContext; @@ -79,7 +79,9 @@ public void onBindViewHolder(ItemHolder itemHolder, int i) { itemHolder.title.setText(localItem.title); itemHolder.album.setText(localItem.albumName); - ImageLoader.getInstance().displayImage(TimberUtils.getAlbumArtUri(localItem.albumId).toString(), itemHolder.albumArt, new DisplayImageOptions.Builder().cacheInMemory(true).showImageOnFail(R.drawable.ic_empty_music2).resetViewBeforeLoading(true).build()); + ImageLoader.getInstance().displayImage(TimberUtils.getAlbumArtUri(localItem.albumId).toString(), + itemHolder.albumArt, new DisplayImageOptions.Builder() + .cacheInMemory(true).showImageOnLoading(R.drawable.ic_empty_music2).resetViewBeforeLoading(true).build()); setOnPopupMenuListener(itemHolder, i - 1); } @@ -110,7 +112,7 @@ public void onClick(View v) { public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.popup_song_play: - MusicPlayer.playAll(mContext, songIDs, position, -1, TimberUtils.IdType.NA, false); + MusicPlayer.playAll(mContext, songIDs, position + 1, -1, TimberUtils.IdType.NA, false); break; case R.id.popup_song_play_next: long[] ids = new long[1]; @@ -131,9 +133,12 @@ public boolean onMenuItemClick(MenuItem item) { case R.id.popup_song_addto_playlist: AddPlaylistDialog.newInstance(arraylist.get(position + 1)).show(((AppCompatActivity) mContext).getSupportFragmentManager(), "ADD_PLAYLIST"); break; + case R.id.popup_song_share: + TimberUtils.shareTrack(mContext, arraylist.get(position + 1).id); + break; case R.id.popup_song_delete: - long[] deleteIds = {arraylist.get(position).id}; - TimberUtils.showDeleteDialog(mContext,arraylist.get(position).title, deleteIds, ArtistSongAdapter.this, position); + long[] deleteIds = {arraylist.get(position + 1).id}; + TimberUtils.showDeleteDialog(mContext,arraylist.get(position + 1).title, deleteIds, ArtistSongAdapter.this, position + 1); break; } return false; @@ -169,7 +174,7 @@ private void clearExtraSpacingBetweenCards(RecyclerView albumsRecyclerview) { public long[] getSongIds() { List actualArraylist = new ArrayList(arraylist); - actualArraylist.remove(0); + //actualArraylist.remove(0); long[] ret = new long[actualArraylist.size()]; for (int i = 0; i < actualArraylist.size(); i++) { ret[i] = actualArraylist.get(i).id; @@ -177,6 +182,18 @@ public long[] getSongIds() { return ret; } + @Override + public void removeSongAt(int i){ + arraylist.remove(i); + updateDataSet(arraylist); + } + + @Override + public void updateDataSet(List arraylist) { + this.arraylist = arraylist; + this.songIDs = getSongIds(); + } + @Override public int getItemViewType(int position) { int viewType; @@ -211,8 +228,9 @@ public void onClick(View v) { handler.postDelayed(new Runnable() { @Override public void run() { - MusicPlayer.playAll(mContext, songIDs, getAdapterPosition() - 1, artistID, TimberUtils.IdType.Artist, false); - NavigationUtils.navigateToNowplaying(mContext, true); + playAll(mContext, songIDs, getAdapterPosition(), artistID, + TimberUtils.IdType.Artist, false, + arraylist.get(getAdapterPosition()), true); } }, 100); diff --git a/app/src/main/java/com/naman14/timber/adapters/BaseQueueAdapter.java b/app/src/main/java/com/naman14/timber/adapters/BaseQueueAdapter.java index 612c5f414..9048ea650 100644 --- a/app/src/main/java/com/naman14/timber/adapters/BaseQueueAdapter.java +++ b/app/src/main/java/com/naman14/timber/adapters/BaseQueueAdapter.java @@ -15,8 +15,8 @@ package com.naman14.timber.adapters; import android.os.Handler; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.RecyclerView; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -72,12 +72,16 @@ public void onBindViewHolder(ItemHolder itemHolder, int i) { if (MusicPlayer.isPlaying()) { itemHolder.visualizer.setColor(Config.accentColor(mContext, ateKey)); itemHolder.visualizer.setVisibility(View.VISIBLE); + } else { + itemHolder.visualizer.setVisibility(View.GONE); } } else { itemHolder.title.setTextColor(Config.textColorPrimary(mContext, ateKey)); itemHolder.visualizer.setVisibility(View.GONE); } - ImageLoader.getInstance().displayImage(TimberUtils.getAlbumArtUri(localItem.albumId).toString(), itemHolder.albumArt, new DisplayImageOptions.Builder().cacheInMemory(true).showImageOnFail(R.drawable.ic_empty_music2).resetViewBeforeLoading(true).build()); + ImageLoader.getInstance().displayImage(TimberUtils.getAlbumArtUri(localItem.albumId).toString(), + itemHolder.albumArt, new DisplayImageOptions.Builder().cacheInMemory(true) + .showImageOnLoading(R.drawable.ic_empty_music2).resetViewBeforeLoading(true).build()); setOnPopupMenuListener(itemHolder, i); } @@ -119,6 +123,9 @@ public boolean onMenuItemClick(MenuItem item) { case R.id.popup_song_addto_playlist: AddPlaylistDialog.newInstance(arraylist.get(position)).show(mContext.getSupportFragmentManager(), "ADD_PLAYLIST"); break; + case R.id.popup_song_share: + TimberUtils.shareTrack(mContext, arraylist.get(position).id); + break; case R.id.popup_song_delete: long[] deleteIds = {arraylist.get(position).id}; TimberUtils.showDeleteDialog(mContext,arraylist.get(position).title, deleteIds, BaseQueueAdapter.this, position); @@ -142,6 +149,10 @@ public long[] getSongIds() { return ret; } + public void removeSongAt(int i){ + arraylist.remove(i); + } + public class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener { protected TextView title, artist; protected ImageView albumArt, popupMenu; diff --git a/app/src/main/java/com/naman14/timber/adapters/BaseSongAdapter.java b/app/src/main/java/com/naman14/timber/adapters/BaseSongAdapter.java new file mode 100644 index 000000000..205090bc0 --- /dev/null +++ b/app/src/main/java/com/naman14/timber/adapters/BaseSongAdapter.java @@ -0,0 +1,72 @@ +package com.naman14.timber.adapters; + +import android.app.Activity; + +import androidx.recyclerview.widget.RecyclerView; + +import android.view.View; +import android.view.ViewGroup; + +import com.naman14.timber.MusicPlayer; +import com.naman14.timber.activities.BaseActivity; +import com.naman14.timber.models.Song; +import com.naman14.timber.utils.NavigationUtils; +import com.naman14.timber.utils.TimberUtils; + +import java.util.List; + +/** + * Created by naman on 7/12/17. + */ + +public class BaseSongAdapter extends RecyclerView.Adapter { + + @Override + public V onCreateViewHolder(ViewGroup parent, int viewType) { + return null; + } + + @Override + public void onBindViewHolder(V holder, int position) { + + } + + @Override + public int getItemCount() { + return 0; + } + + @Override + public int getItemViewType(int position) { + return super.getItemViewType(position); + } + + public class ItemHolder extends RecyclerView.ViewHolder { + + public ItemHolder(View view) { + super(view); + } + + } + + public void playAll(final Activity context, final long[] list, int position, + final long sourceId, final TimberUtils.IdType sourceType, + final boolean forceShuffle, final Song currentSong, boolean navigateNowPlaying) { + + if (context instanceof BaseActivity) { + MusicPlayer.playAll(context, list, position, -1, TimberUtils.IdType.NA, false); + + } else { + MusicPlayer.playAll(context, list, position, -1, TimberUtils.IdType.NA, false); + } + + if (navigateNowPlaying) { + NavigationUtils.navigateToNowplaying(context, true); + } + + + } + public void removeSongAt(int i){} + public void updateDataSet(List arraylist) {} + +} diff --git a/app/src/main/java/com/naman14/timber/adapters/FolderAdapter.java b/app/src/main/java/com/naman14/timber/adapters/FolderAdapter.java index 9447db839..4195347b4 100644 --- a/app/src/main/java/com/naman14/timber/adapters/FolderAdapter.java +++ b/app/src/main/java/com/naman14/timber/adapters/FolderAdapter.java @@ -8,16 +8,15 @@ import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Handler; -import android.support.annotation.NonNull; -import android.support.v4.content.ContextCompat; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; -import com.naman14.timber.MusicPlayer; import com.naman14.timber.R; import com.naman14.timber.dataloaders.FolderLoader; import com.naman14.timber.dataloaders.SongLoader; @@ -36,7 +35,7 @@ * Created by nv95 on 10.11.16. */ -public class FolderAdapter extends RecyclerView.Adapter implements BubbleTextGetter { +public class FolderAdapter extends BaseSongAdapter implements BubbleTextGetter { @NonNull private List mFileSet; @@ -131,23 +130,23 @@ public boolean goUpAsync() { } File parent = mRoot.getParentFile(); if (parent != null && parent.canRead()) { - updateDataSetAsync(parent); - return true; + return updateDataSetAsync(parent); } else { return false; } } - public void updateDataSetAsync(File newRoot) { + public boolean updateDataSetAsync(File newRoot) { if (mBusy) { - return; + return false; } if ("..".equals(newRoot.getName())) { goUpAsync(); - return; + return false; } mRoot = newRoot; new NavigateTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mRoot); + return true; } @Override @@ -218,16 +217,15 @@ public void onClick(View v) { } final File f = mFileSet.get(getAdapterPosition()); - if (f.isDirectory()) { + if (f.isDirectory() && updateDataSetAsync(f)) { albumArt.setImageDrawable(mIcons[3]); - updateDataSetAsync(f); } else if (f.isFile()) { final Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { - int current =-1; + int current = -1; long songId = SongLoader.getSongFromPath(mFileSet.get(getAdapterPosition()).getAbsolutePath(),mContext).id; int count = 0; for (Song song : mSongs) { @@ -246,7 +244,8 @@ public void run() { j++; } } - MusicPlayer.playAll(mContext, ret, current, -1, TimberUtils.IdType.NA, false); + playAll(mContext, ret, current, -1, TimberUtils.IdType.NA, + false, mSongs.get(getAdapterPosition()), false); } }, 100); diff --git a/app/src/main/java/com/naman14/timber/adapters/PlayingQueueAdapter.java b/app/src/main/java/com/naman14/timber/adapters/PlayingQueueAdapter.java index 036cf1be5..9debfd043 100644 --- a/app/src/main/java/com/naman14/timber/adapters/PlayingQueueAdapter.java +++ b/app/src/main/java/com/naman14/timber/adapters/PlayingQueueAdapter.java @@ -16,8 +16,9 @@ import android.app.Activity; import android.os.Handler; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.RecyclerView; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.RecyclerView; +import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -41,6 +42,7 @@ import java.util.List; public class PlayingQueueAdapter extends RecyclerView.Adapter { + private static final String TAG = "PlayingQueueAdapter"; public int currentlyPlayingPosition; private List arraylist; @@ -73,12 +75,16 @@ public void onBindViewHolder(ItemHolder itemHolder, int i) { if (MusicPlayer.isPlaying()) { itemHolder.visualizer.setColor(Config.accentColor(mContext, ateKey)); itemHolder.visualizer.setVisibility(View.VISIBLE); + } else { + itemHolder.visualizer.setVisibility(View.GONE); } } else { itemHolder.title.setTextColor(Config.textColorPrimary(mContext, ateKey)); itemHolder.visualizer.setVisibility(View.GONE); } - ImageLoader.getInstance().displayImage(TimberUtils.getAlbumArtUri(localItem.albumId).toString(), itemHolder.albumArt, new DisplayImageOptions.Builder().cacheInMemory(true).showImageOnFail(R.drawable.ic_empty_music2).resetViewBeforeLoading(true).build()); + ImageLoader.getInstance().displayImage(TimberUtils.getAlbumArtUri(localItem.albumId).toString(), + itemHolder.albumArt, new DisplayImageOptions.Builder().cacheInMemory(true) + .showImageOnLoading(R.drawable.ic_empty_music2).resetViewBeforeLoading(true).build()); setOnPopupMenuListener(itemHolder, i); } @@ -93,6 +99,12 @@ public void onClick(View v) { @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { + case R.id.popup_song_remove_queue: + Log.v(TAG,"Removing " + position); + MusicPlayer.removeTrackAtPosition(getSongAt(position).id, position); + removeSongAt(position); + notifyItemRemoved(position); + break; case R.id.popup_song_play: MusicPlayer.playAll(mContext, getSongIds(), position, -1, TimberUtils.IdType.NA, false); break; @@ -105,10 +117,6 @@ public boolean onMenuItemClick(MenuItem item) { case R.id.popup_song_addto_playlist: AddPlaylistDialog.newInstance(arraylist.get(position)).show(((AppCompatActivity) mContext).getSupportFragmentManager(), "ADD_PLAYLIST"); break; - case R.id.popup_song_delete: - long[] deleteIds = {arraylist.get(position).id}; - TimberUtils.showDeleteDialog(mContext,arraylist.get(position).title, deleteIds, PlayingQueueAdapter.this,position); - break; } return false; } diff --git a/app/src/main/java/com/naman14/timber/adapters/PlaylistAdapter.java b/app/src/main/java/com/naman14/timber/adapters/PlaylistAdapter.java index db0c159fc..9a094f14b 100644 --- a/app/src/main/java/com/naman14/timber/adapters/PlaylistAdapter.java +++ b/app/src/main/java/com/naman14/timber/adapters/PlaylistAdapter.java @@ -2,8 +2,8 @@ import android.app.Activity; import android.graphics.Bitmap; -import android.support.v7.graphics.Palette; -import android.support.v7.widget.RecyclerView; +import androidx.palette.graphics.Palette; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -41,6 +41,7 @@ public class PlaylistAdapter extends RecyclerView.Adapter lastAddedSongs = LastAddedLoader.getLastAddedSongs(mContext); songCountInt = lastAddedSongs.size(); + totalRuntime = 0; + for(Song song : lastAddedSongs){ + totalRuntime += song.duration / 1000; //for some reason default playlists have songs with durations 1000x larger than they should be + } if (songCountInt != 0) { firstAlbumID = lastAddedSongs.get(0).albumId; @@ -149,6 +154,10 @@ private String getAlbumArtUri(int position, long id) { TopTracksLoader recentloader = new TopTracksLoader(mContext, TopTracksLoader.QueryType.RecentSongs); List recentsongs = SongLoader.getSongsForCursor(TopTracksLoader.getCursor()); songCountInt = recentsongs.size(); + totalRuntime = 0; + for(Song song : recentsongs){ + totalRuntime += song.duration / 1000; //for some reason default playlists have songs with durations 1000x larger than they should be + } if (songCountInt != 0) { firstAlbumID = recentsongs.get(0).albumId; @@ -158,6 +167,10 @@ private String getAlbumArtUri(int position, long id) { TopTracksLoader topTracksLoader = new TopTracksLoader(mContext, TopTracksLoader.QueryType.TopTracks); List topsongs = SongLoader.getSongsForCursor(TopTracksLoader.getCursor()); songCountInt = topsongs.size(); + totalRuntime = 0; + for(Song song : topsongs){ + totalRuntime += song.duration / 1000; //for some reason default playlists have songs with durations 1000x larger than they should be + } if (songCountInt != 0) { firstAlbumID = topsongs.get(0).albumId; @@ -166,6 +179,10 @@ private String getAlbumArtUri(int position, long id) { default: List playlistsongs = PlaylistSongLoader.getSongsInPlaylist(mContext, id); songCountInt = playlistsongs.size(); + totalRuntime = 0; + for(Song song : playlistsongs){ + totalRuntime += song.duration; + } if (songCountInt != 0) { firstAlbumID = playlistsongs.get(0).albumId; @@ -176,6 +193,10 @@ private String getAlbumArtUri(int position, long id) { } else { List playlistsongs = PlaylistSongLoader.getSongsInPlaylist(mContext, id); songCountInt = playlistsongs.size(); + totalRuntime = 0; + for(Song song : playlistsongs){ + totalRuntime += song.duration; + } if (songCountInt != 0) { firstAlbumID = playlistsongs.get(0).albumId; diff --git a/app/src/main/java/com/naman14/timber/adapters/SearchAdapter.java b/app/src/main/java/com/naman14/timber/adapters/SearchAdapter.java index a5460733d..b46a33edf 100644 --- a/app/src/main/java/com/naman14/timber/adapters/SearchAdapter.java +++ b/app/src/main/java/com/naman14/timber/adapters/SearchAdapter.java @@ -16,8 +16,8 @@ import android.app.Activity; import android.os.Handler; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.RecyclerView; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -45,7 +45,7 @@ import java.util.Collections; import java.util.List; -public class SearchAdapter extends RecyclerView.Adapter { +public class SearchAdapter extends BaseSongAdapter { private Activity mContext; private List searchResults = Collections.emptyList(); @@ -188,6 +188,9 @@ public boolean onMenuItemClick(MenuItem item) { } }); menu.inflate(R.menu.popup_song); + //Hide these because they aren't implemented + menu.getMenu().findItem(R.id.popup_song_delete).setVisible(false); + menu.getMenu().findItem(R.id.popup_song_share).setVisible(false); menu.show(); } }); @@ -243,7 +246,8 @@ public void onClick(View v) { public void run() { long[] ret = new long[1]; ret[0] = ((Song) searchResults.get(getAdapterPosition())).id; - MusicPlayer.playAll(mContext, ret, 0, -1, TimberUtils.IdType.NA, false); + playAll(mContext, ret, 0, -1, TimberUtils.IdType.NA, + false, (Song) searchResults.get(getAdapterPosition()), false); } }, 100); diff --git a/app/src/main/java/com/naman14/timber/adapters/Timber4QueueAdapter.java b/app/src/main/java/com/naman14/timber/adapters/SlidingQueueAdapter.java similarity index 88% rename from app/src/main/java/com/naman14/timber/adapters/Timber4QueueAdapter.java rename to app/src/main/java/com/naman14/timber/adapters/SlidingQueueAdapter.java index 585441c7a..b2290b7ac 100644 --- a/app/src/main/java/com/naman14/timber/adapters/Timber4QueueAdapter.java +++ b/app/src/main/java/com/naman14/timber/adapters/SlidingQueueAdapter.java @@ -16,7 +16,7 @@ import android.app.Activity; import android.os.Handler; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -33,14 +33,14 @@ import java.util.List; -public class Timber4QueueAdapter extends RecyclerView.Adapter { +public class SlidingQueueAdapter extends RecyclerView.Adapter { public static int currentlyPlayingPosition; private List arraylist; private Activity mContext; private int lastPosition = -1; - public Timber4QueueAdapter(Activity context, List arraylist) { + public SlidingQueueAdapter(Activity context, List arraylist) { this.arraylist = arraylist; this.mContext = context; currentlyPlayingPosition = MusicPlayer.getQueuePosition(); @@ -48,7 +48,7 @@ public Timber4QueueAdapter(Activity context, List arraylist) { @Override public ItemHolder onCreateViewHolder(ViewGroup viewGroup, int i) { - View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_queue_timber4_bottom, null); + View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_song_sliding_queue, null); ItemHolder ml = new ItemHolder(v); return ml; } @@ -59,7 +59,9 @@ public void onBindViewHolder(ItemHolder itemHolder, int i) { // setAnimation(itemHolder.itemView, i); Song localItem = arraylist.get(i); - ImageLoader.getInstance().displayImage(TimberUtils.getAlbumArtUri(localItem.albumId).toString(), itemHolder.albumArt, new DisplayImageOptions.Builder().cacheInMemory(true).showImageOnFail(R.drawable.ic_empty_music2).resetViewBeforeLoading(true).build()); + ImageLoader.getInstance().displayImage(TimberUtils.getAlbumArtUri(localItem.albumId).toString(), + itemHolder.albumArt, new DisplayImageOptions.Builder().cacheInMemory(true) + .showImageOnLoading(R.drawable.ic_empty_music2).resetViewBeforeLoading(true).build()); } diff --git a/app/src/main/java/com/naman14/timber/adapters/SongsListAdapter.java b/app/src/main/java/com/naman14/timber/adapters/SongsListAdapter.java index fa59450fd..8e3ac1ff4 100644 --- a/app/src/main/java/com/naman14/timber/adapters/SongsListAdapter.java +++ b/app/src/main/java/com/naman14/timber/adapters/SongsListAdapter.java @@ -16,8 +16,9 @@ import android.graphics.Color; import android.os.Handler; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.RecyclerView; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.RecyclerView; + import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -35,7 +36,6 @@ import com.naman14.timber.models.Song; import com.naman14.timber.utils.Helpers; import com.naman14.timber.utils.NavigationUtils; -import com.naman14.timber.utils.PreferencesUtility; import com.naman14.timber.utils.TimberUtils; import com.naman14.timber.widgets.BubbleTextGetter; import com.naman14.timber.widgets.MusicVisualizer; @@ -44,7 +44,7 @@ import java.util.List; -public class SongsListAdapter extends RecyclerView.Adapter implements BubbleTextGetter { +public class SongsListAdapter extends BaseSongAdapter implements BubbleTextGetter { public int currentlyPlayingPosition; private List arraylist; @@ -85,23 +85,30 @@ public void onBindViewHolder(ItemHolder itemHolder, int i) { itemHolder.title.setText(localItem.title); itemHolder.artist.setText(localItem.artistName); - ImageLoader.getInstance().displayImage(TimberUtils.getAlbumArtUri(localItem.albumId).toString(), itemHolder.albumArt, new DisplayImageOptions.Builder().cacheInMemory(true).showImageOnFail(R.drawable.ic_empty_music2).resetViewBeforeLoading(true).build()); + ImageLoader.getInstance().displayImage(TimberUtils.getAlbumArtUri(localItem.albumId).toString(), + itemHolder.albumArt, new DisplayImageOptions.Builder().cacheInMemory(true) + .showImageOnLoading(R.drawable.ic_empty_music2) + .resetViewBeforeLoading(true).build()); + if (MusicPlayer.getCurrentAudioId() == localItem.id) { itemHolder.title.setTextColor(Config.accentColor(mContext, ateKey)); if (MusicPlayer.isPlaying()) { itemHolder.visualizer.setColor(Config.accentColor(mContext, ateKey)); itemHolder.visualizer.setVisibility(View.VISIBLE); + } else { + itemHolder.visualizer.setVisibility(View.GONE); } } else { - if (isPlaylist) + itemHolder.visualizer.setVisibility(View.GONE); + if (isPlaylist) { itemHolder.title.setTextColor(Color.WHITE); - else + } else { itemHolder.title.setTextColor(Config.textColorPrimary(mContext, ateKey)); - itemHolder.visualizer.setVisibility(View.GONE); + } } - if (animate && isPlaylist && PreferencesUtility.getInstance(mContext).getAnimations()) { + if (animate && isPlaylist) { if (TimberUtils.isLollipop()) setAnimation(itemHolder.itemView, i); else { @@ -163,6 +170,9 @@ public boolean onMenuItemClick(MenuItem item) { case R.id.popup_song_addto_playlist: AddPlaylistDialog.newInstance(arraylist.get(position)).show(mContext.getSupportFragmentManager(), "ADD_PLAYLIST"); break; + case R.id.popup_song_share: + TimberUtils.shareTrack(mContext, arraylist.get(position).id); + break; case R.id.popup_song_delete: long[] deleteIds = {arraylist.get(position).id}; TimberUtils.showDeleteDialog(mContext,arraylist.get(position).title, deleteIds, SongsListAdapter.this, position); @@ -208,6 +218,7 @@ private void setAnimation(View viewToAnimate, int position) { } } + @Override public void updateDataSet(List arraylist) { this.arraylist = arraylist; this.songIDs = getSongIds(); @@ -234,7 +245,9 @@ public void onClick(View v) { handler.postDelayed(new Runnable() { @Override public void run() { - MusicPlayer.playAll(mContext, songIDs, getAdapterPosition(), -1, TimberUtils.IdType.NA, false); + playAll(mContext, songIDs, getAdapterPosition(), -1, + TimberUtils.IdType.NA, false, + arraylist.get(getAdapterPosition()), false); Handler handler1 = new Handler(); handler1.postDelayed(new Runnable() { @Override @@ -259,8 +272,10 @@ public void addSongTo(int i, Song song) { arraylist.add(i, song); } + @Override public void removeSongAt(int i) { arraylist.remove(i); + updateDataSet(arraylist); } } diff --git a/app/src/main/java/com/naman14/timber/cast/WebServer.java b/app/src/main/java/com/naman14/timber/cast/WebServer.java new file mode 100644 index 000000000..0c3d0a593 --- /dev/null +++ b/app/src/main/java/com/naman14/timber/cast/WebServer.java @@ -0,0 +1,76 @@ +package com.naman14.timber.cast; + +import android.content.Context; +import android.net.Uri; + +import com.naman14.timber.utils.Constants; +import com.naman14.timber.utils.TimberUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.Map; + +import fi.iki.elonen.NanoHTTPD; + +public class WebServer extends NanoHTTPD { + + private Context context; + private Uri songUri, albumArtUri; + + public WebServer(Context context) { + super(Constants.CAST_SERVER_PORT); + this.context = context; + } + + @Override + public Response serve(String uri, Method method, + Map header, + Map parameters, + Map files) { + if (uri.contains("albumart")) { + //serve the picture + + String albumId = parameters.get("id"); + this.albumArtUri = TimberUtils.getAlbumArtUri(Long.parseLong(albumId)); + + if (albumArtUri != null) { + String mediasend = "image/jpg"; + InputStream fisAlbumArt = null; + try { + fisAlbumArt = context.getContentResolver().openInputStream(albumArtUri); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + Response.Status st = Response.Status.OK; + + //serve the song + return newChunkedResponse(st, mediasend, fisAlbumArt); + } + + } else if (uri.contains("song")) { + + String songId = parameters.get("id"); + this.songUri = TimberUtils.getSongUri(context, Long.parseLong(songId)); + + if (songUri != null) { + String mediasend = "audio/mp3"; + FileInputStream fisSong = null; + File song = new File(songUri.getPath()); + try { + fisSong = new FileInputStream(song); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + Response.Status st = Response.Status.OK; + + //serve the song + return newFixedLengthResponse(st, mediasend, fisSong, song.length()); + } + + } + return newFixedLengthResponse("Error"); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/naman14/timber/dataloaders/ArtistAlbumLoader.java b/app/src/main/java/com/naman14/timber/dataloaders/ArtistAlbumLoader.java index fb026b705..e62d61577 100644 --- a/app/src/main/java/com/naman14/timber/dataloaders/ArtistAlbumLoader.java +++ b/app/src/main/java/com/naman14/timber/dataloaders/ArtistAlbumLoader.java @@ -19,40 +19,26 @@ import android.provider.MediaStore; import com.naman14.timber.models.Album; +import com.naman14.timber.models.Song; import java.util.ArrayList; +import java.util.List; public class ArtistAlbumLoader { public static ArrayList getAlbumsForArtist(Context context, long artistID) { - ArrayList albumList = new ArrayList(); - Cursor cursor = makeAlbumForArtistCursor(context, artistID); - - if (cursor != null) { - if (cursor.moveToFirst()) - do { - - Album album = new Album(cursor.getLong(0), cursor.getString(1), cursor.getString(2), artistID, cursor.getInt(3), cursor.getInt(4)); - albumList.add(album); - } - while (cursor.moveToNext()); - - } - if (cursor != null) - cursor.close(); - return albumList; - } - - - public static Cursor makeAlbumForArtistCursor(Context context, long artistID) { - if (artistID == -1) return null; - Cursor cursor = context.getContentResolver().query(MediaStore.Audio.Artists.Albums.getContentUri("external", artistID), new String[]{"_id", "album", "artist", "numsongs", "minyear"}, null, null, MediaStore.Audio.Albums.DEFAULT_SORT_ORDER); - - return cursor; + List allAlbums = AlbumLoader.getAllAlbums(context); + ArrayList artistAlbums = new ArrayList<>(); + for (Album album: allAlbums) { + if (album.artistId == artistID) { + artistAlbums.add(album); + } + } + return artistAlbums; } } diff --git a/app/src/main/java/com/naman14/timber/dataloaders/FolderLoader.java b/app/src/main/java/com/naman14/timber/dataloaders/FolderLoader.java index 03797ab63..6a81ce7a4 100644 --- a/app/src/main/java/com/naman14/timber/dataloaders/FolderLoader.java +++ b/app/src/main/java/com/naman14/timber/dataloaders/FolderLoader.java @@ -28,22 +28,24 @@ public class FolderLoader { public static List getMediaFiles(File dir, final boolean acceptDirs) { ArrayList list = new ArrayList<>(); list.add(new File(dir, "..")); - List files = Arrays.asList(dir.listFiles(new FileFilter() { + if (dir.isDirectory()) { + List files = Arrays.asList(dir.listFiles(new FileFilter() { - @Override - public boolean accept(File file) { - if (file.isFile()) { - String name = file.getName(); - return !".nomedia".equals(name) && checkFileExt(name); - } else if (file.isDirectory()){ - return acceptDirs && checkDir(file); - } else - return false; - } - })); - Collections.sort(files, new FilenameComparator()); - Collections.sort(files, new DirFirstComparator()); - list.addAll(files); + @Override + public boolean accept(File file) { + if (file.isFile()) { + String name = file.getName(); + return !".nomedia".equals(name) && checkFileExt(name); + } else if (file.isDirectory()) { + return acceptDirs && checkDir(file); + } else + return false; + } + })); + Collections.sort(files, new FilenameComparator()); + Collections.sort(files, new DirFirstComparator()); + list.addAll(files); + } return list; } diff --git a/app/src/main/java/com/naman14/timber/dataloaders/QueueLoader.java b/app/src/main/java/com/naman14/timber/dataloaders/QueueLoader.java index ea287f9e5..601339f23 100644 --- a/app/src/main/java/com/naman14/timber/dataloaders/QueueLoader.java +++ b/app/src/main/java/com/naman14/timber/dataloaders/QueueLoader.java @@ -63,4 +63,6 @@ public static List getQueueSongs(Context context) { return mSongList; } + + } diff --git a/app/src/main/java/com/naman14/timber/dataloaders/SongLoader.java b/app/src/main/java/com/naman14/timber/dataloaders/SongLoader.java index 38a84877a..8737d06a3 100644 --- a/app/src/main/java/com/naman14/timber/dataloaders/SongLoader.java +++ b/app/src/main/java/com/naman14/timber/dataloaders/SongLoader.java @@ -17,6 +17,7 @@ import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; +import android.media.MediaMetadataRetriever; import android.net.Uri; import android.provider.BaseColumns; import android.provider.MediaStore; @@ -151,5 +152,19 @@ private static Cursor makeSongCursor(Context context, String selection, String[] } + public static Song songFromFile(String filePath) { + MediaMetadataRetriever mmr = new MediaMetadataRetriever(); + mmr.setDataSource(filePath); + return new Song( + -1, + -1, + -1, + mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE), + mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST), + mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM), + Integer.parseInt(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)), + 0 + ); + } } diff --git a/app/src/main/java/com/naman14/timber/dialogs/AddPlaylistDialog.java b/app/src/main/java/com/naman14/timber/dialogs/AddPlaylistDialog.java index fdb6d67b4..a402c5aac 100644 --- a/app/src/main/java/com/naman14/timber/dialogs/AddPlaylistDialog.java +++ b/app/src/main/java/com/naman14/timber/dialogs/AddPlaylistDialog.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.view.View; import com.afollestad.materialdialogs.MaterialDialog; diff --git a/app/src/main/java/com/naman14/timber/dialogs/CreatePlaylistDialog.java b/app/src/main/java/com/naman14/timber/dialogs/CreatePlaylistDialog.java index 0993c5ab6..ee122b93e 100644 --- a/app/src/main/java/com/naman14/timber/dialogs/CreatePlaylistDialog.java +++ b/app/src/main/java/com/naman14/timber/dialogs/CreatePlaylistDialog.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.widget.Toast; import com.afollestad.materialdialogs.MaterialDialog; diff --git a/app/src/main/java/com/naman14/timber/dialogs/LastFmLoginDialog.java b/app/src/main/java/com/naman14/timber/dialogs/LastFmLoginDialog.java index 5ad4e0c1b..659f62226 100644 --- a/app/src/main/java/com/naman14/timber/dialogs/LastFmLoginDialog.java +++ b/app/src/main/java/com/naman14/timber/dialogs/LastFmLoginDialog.java @@ -4,7 +4,7 @@ import android.app.DialogFragment; import android.app.ProgressDialog; import android.os.Bundle; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.widget.EditText; import android.widget.Toast; @@ -26,8 +26,8 @@ public class LastFmLoginDialog extends DialogFragment { public Dialog onCreateDialog(Bundle savedInstanceState) { return new MaterialDialog.Builder(getActivity()). positiveText("Login"). - negativeText("Cancel"). - title("Login to LastFM"). + negativeText(getString(R.string.cancel)). + title(getString(R.string.lastfm_login)). customView(R.layout.dialog_lastfm_login, false). onPositive(new MaterialDialog.SingleButtonCallback() { @Override @@ -51,7 +51,7 @@ public void userSuccess() { @Override public void userInfoFailed() { progressDialog.dismiss(); - Toast.makeText(getTargetFragment().getActivity(), "Failed to Login", Toast.LENGTH_SHORT).show(); + Toast.makeText(getTargetFragment().getActivity(), getString(R.string.lastfm_login_failture), Toast.LENGTH_SHORT).show(); } }); } diff --git a/app/src/main/java/com/naman14/timber/dialogs/StorageSelectDialog.java b/app/src/main/java/com/naman14/timber/dialogs/StorageSelectDialog.java new file mode 100644 index 000000000..b88cd707e --- /dev/null +++ b/app/src/main/java/com/naman14/timber/dialogs/StorageSelectDialog.java @@ -0,0 +1,72 @@ +package com.naman14.timber.dialogs; + +import android.content.Context; +import android.content.DialogInterface; +import android.os.Environment; +import androidx.appcompat.app.AlertDialog; + +import com.naman14.timber.R; + +import java.io.File; +import java.io.FileFilter; + +/** + * Created by nv95 on 06.12.16. + */ + +public class StorageSelectDialog implements DialogInterface.OnClickListener { + + private final AlertDialog mDialog; + private final File[] mStorages; + private OnDirSelectListener mDirSelectListener; + + public StorageSelectDialog(final Context context) { + mStorages = getAvailableStorages(context); + String[] names = new String[mStorages.length]; + for (int i=0;i songList = ArtistSongLoader.getSongsForArtist(getActivity(), artistID); + mAdapter = new ArtistSongAdapter(getActivity(), songList, artistID); collapsingToolbarLayout.setTitle(artist.name); @@ -188,6 +202,32 @@ public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { }); } + @Override + public void onActivityCreated(final Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.artist_detail, menu); + if (getActivity() != null) + ATE.applyMenu(getActivity(), "dark_theme", menu); + } + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.popup_song_addto_queue: + MusicPlayer.addToQueue(getContext(), mAdapter.getSongIds(), -1, TimberUtils.IdType.NA); + break; + case R.id.popup_song_addto_playlist: + AddPlaylistDialog.newInstance(mAdapter.getSongIds()).show(getActivity().getSupportFragmentManager(), "ADD_PLAYLIST"); + break; + } + return super.onOptionsItemSelected(item); + } + @Override public void onResume() { super.onResume(); diff --git a/app/src/main/java/com/naman14/timber/fragments/ArtistFragment.java b/app/src/main/java/com/naman14/timber/fragments/ArtistFragment.java index 13044f507..f2a138e87 100644 --- a/app/src/main/java/com/naman14/timber/fragments/ArtistFragment.java +++ b/app/src/main/java/com/naman14/timber/fragments/ArtistFragment.java @@ -17,9 +17,9 @@ import android.graphics.Rect; import android.os.AsyncTask; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v7.widget.GridLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -33,6 +33,7 @@ import com.naman14.timber.models.Artist; import com.naman14.timber.utils.PreferencesUtility; import com.naman14.timber.utils.SortOrder; +import com.naman14.timber.widgets.BaseRecyclerView; import com.naman14.timber.widgets.DividerItemDecoration; import com.naman14.timber.widgets.FastScroller; @@ -41,8 +42,7 @@ public class ArtistFragment extends Fragment { private ArtistAdapter mAdapter; - private RecyclerView recyclerView; - private FastScroller fastScroller; + private BaseRecyclerView recyclerView; private GridLayoutManager layoutManager; private RecyclerView.ItemDecoration itemDecoration; private PreferencesUtility mPreferences; @@ -60,8 +60,10 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa View rootView = inflater.inflate( R.layout.fragment_recyclerview, container, false); - recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview); - fastScroller = (FastScroller) rootView.findViewById(R.id.fastscroller); + recyclerView = rootView.findViewById(R.id.recyclerview); + FastScroller fastScroller = rootView.findViewById(R.id.fastscroller); + fastScroller.setRecyclerView(recyclerView); + recyclerView.setEmptyView(getActivity(), rootView.findViewById(R.id.list_empty), "No media found"); setLayoutManager(); @@ -73,11 +75,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa private void setLayoutManager() { if (isGrid) { layoutManager = new GridLayoutManager(getActivity(), 2); - fastScroller.setVisibility(View.GONE); } else { layoutManager = new GridLayoutManager(getActivity(), 1); - fastScroller.setVisibility(View.VISIBLE); - fastScroller.setRecyclerView(recyclerView); } recyclerView.setLayoutManager(layoutManager); } @@ -174,7 +173,10 @@ protected String doInBackground(String... params) { @Override protected void onPostExecute(String result) { - recyclerView.setAdapter(mAdapter); + if (mAdapter != null) { + mAdapter.setHasStableIds(true); + recyclerView.setAdapter(mAdapter); + } if (getActivity() != null) { setItemDecoration(); } diff --git a/app/src/main/java/com/naman14/timber/fragments/ArtistMusicFragment.java b/app/src/main/java/com/naman14/timber/fragments/ArtistMusicFragment.java index db6eaeb08..7a073385c 100644 --- a/app/src/main/java/com/naman14/timber/fragments/ArtistMusicFragment.java +++ b/app/src/main/java/com/naman14/timber/fragments/ArtistMusicFragment.java @@ -15,9 +15,9 @@ package com.naman14.timber.fragments; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -34,8 +34,8 @@ public class ArtistMusicFragment extends Fragment { public static RecyclerView songsRecyclerview; - long artistID = -1; - ArtistSongAdapter mSongAdapter; + private long artistID = -1; + private ArtistSongAdapter mSongAdapter; public static ArtistMusicFragment newInstance(long id) { ArtistMusicFragment fragment = new ArtistMusicFragment(); diff --git a/app/src/main/java/com/naman14/timber/fragments/FoldersFragment.java b/app/src/main/java/com/naman14/timber/fragments/FoldersFragment.java index bffae0df0..214a52b4f 100644 --- a/app/src/main/java/com/naman14/timber/fragments/FoldersFragment.java +++ b/app/src/main/java/com/naman14/timber/fragments/FoldersFragment.java @@ -5,13 +5,16 @@ import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; -import android.support.v4.app.Fragment; -import android.support.v7.app.ActionBar; -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.fragment.app.Fragment; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.appcompat.widget.Toolbar; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ProgressBar; @@ -19,6 +22,7 @@ import com.afollestad.appthemeengine.ATE; import com.naman14.timber.R; import com.naman14.timber.adapters.FolderAdapter; +import com.naman14.timber.dialogs.StorageSelectDialog; import com.naman14.timber.utils.PreferencesUtility; import com.naman14.timber.widgets.DividerItemDecoration; import com.naman14.timber.widgets.FastScroller; @@ -29,7 +33,7 @@ * Created by nv95 on 10.11.16. */ -public class FoldersFragment extends Fragment { +public class FoldersFragment extends Fragment implements StorageSelectDialog.OnDirSelectListener { private FolderAdapter mAdapter; private RecyclerView recyclerView; @@ -82,7 +86,23 @@ private void setItemDecoration() { @Override public void onActivityCreated(final Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - //setHasOptionsMenu(true); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.menu_folders, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.action_storages) { + new StorageSelectDialog(getActivity()) + .setDirSelectListener(this) + .show(); + } + return super.onOptionsItemSelected(item); } public void updateTheme() { @@ -93,6 +113,11 @@ public void updateTheme() { } } + @Override + public void onDirSelected(File dir) { + mAdapter.updateDataSetAsync(dir); + } + private class loadFolders extends AsyncTask { @Override diff --git a/app/src/main/java/com/naman14/timber/fragments/MainFragment.java b/app/src/main/java/com/naman14/timber/fragments/MainFragment.java index 6f9fd3373..bc3fce0f2 100644 --- a/app/src/main/java/com/naman14/timber/fragments/MainFragment.java +++ b/app/src/main/java/com/naman14/timber/fragments/MainFragment.java @@ -16,14 +16,14 @@ import android.os.Bundle; import android.preference.PreferenceManager; -import android.support.design.widget.TabLayout; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; +import com.google.android.material.tabs.TabLayout; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.ViewPager; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -40,8 +40,8 @@ public class MainFragment extends Fragment { - PreferencesUtility mPreferences; - ViewPager viewPager; + private PreferencesUtility mPreferences; + private ViewPager viewPager; @Override public void onCreate(final Bundle savedInstanceState) { diff --git a/app/src/main/java/com/naman14/timber/fragments/PlaylistFragment.java b/app/src/main/java/com/naman14/timber/fragments/PlaylistFragment.java index 82746cde3..74071f83f 100644 --- a/app/src/main/java/com/naman14/timber/fragments/PlaylistFragment.java +++ b/app/src/main/java/com/naman14/timber/fragments/PlaylistFragment.java @@ -20,14 +20,14 @@ import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentStatePagerAdapter; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.GridLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.Toolbar; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.appcompat.widget.Toolbar; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -52,10 +52,10 @@ public class PlaylistFragment extends Fragment { - int playlistcount; - FragmentStatePagerAdapter adapter; - MultiViewPager pager; - RecyclerView recyclerView; + private int playlistcount; + private FragmentStatePagerAdapter adapter; + private MultiViewPager pager; + private RecyclerView recyclerView; private GridLayoutManager layoutManager; private RecyclerView.ItemDecoration itemDecoration; diff --git a/app/src/main/java/com/naman14/timber/fragments/QueueFragment.java b/app/src/main/java/com/naman14/timber/fragments/QueueFragment.java index c0882c53f..6e5d9f33a 100644 --- a/app/src/main/java/com/naman14/timber/fragments/QueueFragment.java +++ b/app/src/main/java/com/naman14/timber/fragments/QueueFragment.java @@ -17,12 +17,11 @@ import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; -import android.support.v4.app.Fragment; -import android.support.v7.app.ActionBar; -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.fragment.app.Fragment; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.appcompat.widget.Toolbar; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -36,19 +35,20 @@ import com.naman14.timber.dataloaders.QueueLoader; import com.naman14.timber.listeners.MusicStateListener; import com.naman14.timber.models.Song; +import com.naman14.timber.widgets.BaseRecyclerView; import com.naman14.timber.widgets.DragSortRecycler; public class QueueFragment extends Fragment implements MusicStateListener { private PlayingQueueAdapter mAdapter; - private RecyclerView recyclerView; + private BaseRecyclerView recyclerView; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate( R.layout.fragment_queue, container, false); - Toolbar toolbar = (Toolbar) rootView.findViewById(R.id.toolbar); + Toolbar toolbar = rootView.findViewById(R.id.toolbar); ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); final ActionBar ab = ((AppCompatActivity) getActivity()).getSupportActionBar(); @@ -56,9 +56,10 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa ab.setDisplayHomeAsUpEnabled(true); ab.setTitle(R.string.playing_queue); - recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview); + recyclerView = rootView.findViewById(R.id.recyclerview); recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); recyclerView.setItemAnimator(null); + recyclerView.setEmptyView(getActivity(), rootView.findViewById(R.id.list_empty), "No songs in queue"); new loadQueueSongs().execute(""); ((BaseActivity) getActivity()).setMusicStateListenerListener(this); diff --git a/app/src/main/java/com/naman14/timber/fragments/SettingsFragment.java b/app/src/main/java/com/naman14/timber/fragments/SettingsFragment.java index 7373d4f06..49bcb4d60 100644 --- a/app/src/main/java/com/naman14/timber/fragments/SettingsFragment.java +++ b/app/src/main/java/com/naman14/timber/fragments/SettingsFragment.java @@ -14,6 +14,7 @@ package com.naman14.timber.fragments; +import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Color; import android.os.Bundle; @@ -29,9 +30,10 @@ import com.afollestad.appthemeengine.prefs.ATEColorPreference; import com.afollestad.materialdialogs.color.ColorChooserDialog; import com.naman14.timber.R; +import com.naman14.timber.activities.DonateActivity; import com.naman14.timber.activities.SettingsActivity; -import com.naman14.timber.dialogs.LastFmLoginDialog; -import com.naman14.timber.lastfmapi.LastFmClient; +import com.naman14.timber.dialogs.LastFmLoginDialog; +import com.naman14.timber.lastfmapi.LastFmClient; import com.naman14.timber.utils.Constants; import com.naman14.timber.utils.NavigationUtils; import com.naman14.timber.utils.PreferencesUtility; @@ -39,19 +41,24 @@ public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String NOW_PLAYING_SELECTOR = "now_playing_selector"; - private static final String LASTFM_LOGIN = "lastfm_login"; + private static final String LASTFM_LOGIN = "lastfm_login"; + + private static final String LOCKSCREEN = "show_albumart_lockscreen"; + private static final String XPOSED = "toggle_xposed_trackselector"; + private static final String KEY_ABOUT = "preference_about"; private static final String KEY_SOURCE = "preference_source"; private static final String KEY_THEME = "theme_preference"; private static final String TOGGLE_ANIMATIONS = "toggle_animations"; private static final String TOGGLE_SYSTEM_ANIMATIONS = "toggle_system_animations"; private static final String KEY_START_PAGE = "start_page_preference"; - private boolean lastFMlogedin; - Preference nowPlayingSelector; - Preference lastFMlogin; - SwitchPreference toggleAnimations; - ListPreference themePreference, startPagePreference; - PreferencesUtility mPreferences; + private boolean lastFMlogedin; + + private Preference nowPlayingSelector, lastFMlogin, lockscreen, xposed; + + private SwitchPreference toggleAnimations; + private ListPreference themePreference, startPagePreference; + private PreferencesUtility mPreferences; private String mAteKey; @Override @@ -62,30 +69,18 @@ public void onCreate(Bundle savedInstanceState) { mPreferences = PreferencesUtility.getInstance(getActivity()); + lockscreen = findPreference(LOCKSCREEN); nowPlayingSelector = findPreference(NOW_PLAYING_SELECTOR); - lastFMlogin = findPreference(LASTFM_LOGIN); - updateLastFM(); + + xposed = findPreference(XPOSED); + + lastFMlogin = findPreference(LASTFM_LOGIN); + updateLastFM(); // themePreference = (ListPreference) findPreference(KEY_THEME); startPagePreference = (ListPreference) findPreference(KEY_START_PAGE); nowPlayingSelector.setIntent(NavigationUtils.getNavigateToStyleSelectorIntent(getActivity(), Constants.SETTINGS_STYLE_SELECTOR_NOWPLAYING)); - lastFMlogin.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - if (lastFMlogedin) { - LastFmClient.getInstance(getActivity()).logout(); - updateLastFM(); - } else { - LastFmLoginDialog lastFmLoginDialog = new LastFmLoginDialog(); - lastFmLoginDialog.setTargetFragment(SettingsFragment.this, 0); - lastFmLoginDialog.show(getFragmentManager(), LastFmLoginDialog.FRAGMENT_NAME); - - } - return true; - } - }); - - PreferencesUtility.getInstance(getActivity()).setOnSharedPreferenceChangeListener(this); + setPreferenceClickListeners(); } @@ -130,6 +125,53 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { return true; } }); + + Intent restoreIntent = new Intent(getActivity(), DonateActivity.class); + restoreIntent.putExtra("title", "Restoring purchases.."); + restoreIntent.setAction("restore"); + + findPreference("support_development").setIntent(new Intent(getActivity(), DonateActivity.class)); + findPreference("restore_purchases").setIntent(restoreIntent); + + lockscreen.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + Bundle extras = new Bundle(); + extras.putBoolean("lockscreen",(boolean)newValue); + mPreferences.updateService(extras); + return true; + } + }); + + xposed.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + Bundle extras = new Bundle(); + extras.putBoolean("xtrack",(boolean)newValue); + mPreferences.updateService(extras); + return true; + } + }); + + lastFMlogin.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + if (lastFMlogedin) { + LastFmClient.getInstance(getActivity()).logout(); + Bundle extras = new Bundle(); + extras.putString("lf_token","logout"); + extras.putString("lf_user",null); + mPreferences.updateService(extras); + updateLastFM(); + } else { + LastFmLoginDialog lastFmLoginDialog = new LastFmLoginDialog(); + lastFmLoginDialog.show(getChildFragmentManager(), LastFmLoginDialog.FRAGMENT_NAME); + + } + return true; + } + }); + } @Override @@ -209,16 +251,16 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { } - public void updateLastFM() { - String username = LastFmClient.getInstance(getActivity()).getUsername(); - if (username != null) { - lastFMlogedin = true; - lastFMlogin.setTitle("Logout"); - lastFMlogin.setSummary("Logged in as " + username); - } else { - lastFMlogedin = false; - lastFMlogin.setTitle("Login"); - lastFMlogin.setSummary("Login to LastFM to scrobble"); - } - } + public void updateLastFM() { + String username = LastFmClient.getInstance(getActivity()).getUsername(); + if (username != null) { + lastFMlogedin = true; + lastFMlogin.setTitle("Logout"); + lastFMlogin.setSummary(String.format(getString(R.string.lastfm_loged_in),username)); + } else { + lastFMlogedin = false; + lastFMlogin.setTitle("Login"); + lastFMlogin.setSummary(getString(R.string.lastfm_pref)); + } + } } diff --git a/app/src/main/java/com/naman14/timber/fragments/SimilarArtistFragment.java b/app/src/main/java/com/naman14/timber/fragments/SimilarArtistFragment.java index e22c32e12..836b39df8 100644 --- a/app/src/main/java/com/naman14/timber/fragments/SimilarArtistFragment.java +++ b/app/src/main/java/com/naman14/timber/fragments/SimilarArtistFragment.java @@ -15,7 +15,7 @@ package com.naman14.timber.fragments; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -31,7 +31,7 @@ public class SimilarArtistFragment extends Fragment { - long artistID = -1; + private long artistID = -1; public static SimilarArtistFragment newInstance(long id) { SimilarArtistFragment fragment = new SimilarArtistFragment(); diff --git a/app/src/main/java/com/naman14/timber/fragments/SongsFragment.java b/app/src/main/java/com/naman14/timber/fragments/SongsFragment.java index 541eb244f..ecccb2040 100644 --- a/app/src/main/java/com/naman14/timber/fragments/SongsFragment.java +++ b/app/src/main/java/com/naman14/timber/fragments/SongsFragment.java @@ -16,10 +16,10 @@ import android.os.AsyncTask; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.fragment.app.Fragment; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; + import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -35,6 +35,7 @@ import com.naman14.timber.models.Song; import com.naman14.timber.utils.PreferencesUtility; import com.naman14.timber.utils.SortOrder; +import com.naman14.timber.widgets.BaseRecyclerView; import com.naman14.timber.widgets.DividerItemDecoration; import com.naman14.timber.widgets.FastScroller; @@ -43,7 +44,7 @@ public class SongsFragment extends Fragment implements MusicStateListener { private SongsListAdapter mAdapter; - private RecyclerView recyclerView; + private BaseRecyclerView recyclerView; private PreferencesUtility mPreferences; @Override @@ -57,9 +58,10 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa View rootView = inflater.inflate( R.layout.fragment_recyclerview, container, false); - recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview); + recyclerView = rootView.findViewById(R.id.recyclerview); recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); - FastScroller fastScroller = (FastScroller) rootView.findViewById(R.id.fastscroller); + recyclerView.setEmptyView(getActivity(), rootView.findViewById(R.id.list_empty), "No media found"); + FastScroller fastScroller = rootView.findViewById(R.id.fastscroller); fastScroller.setRecyclerView(recyclerView); new loadSongs().execute(""); diff --git a/app/src/main/java/com/naman14/timber/helpers/MediaButtonIntentReceiver.java b/app/src/main/java/com/naman14/timber/helpers/MediaButtonIntentReceiver.java index 63bf009c6..92d66d21a 100644 --- a/app/src/main/java/com/naman14/timber/helpers/MediaButtonIntentReceiver.java +++ b/app/src/main/java/com/naman14/timber/helpers/MediaButtonIntentReceiver.java @@ -18,7 +18,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.WakeLock; -import android.support.v4.content.WakefulBroadcastReceiver; +import androidx.legacy.content.WakefulBroadcastReceiver; import android.util.Log; import android.view.KeyEvent; diff --git a/app/src/main/java/com/naman14/timber/lastfmapi/LastFmClient.java b/app/src/main/java/com/naman14/timber/lastfmapi/LastFmClient.java index f5b38e9db..045e41891 100644 --- a/app/src/main/java/com/naman14/timber/lastfmapi/LastFmClient.java +++ b/app/src/main/java/com/naman14/timber/lastfmapi/LastFmClient.java @@ -16,8 +16,10 @@ import android.content.Context; import android.content.SharedPreferences; +import android.os.Bundle; import android.util.Log; +import com.naman14.timber.lastfmapi.callbacks.AlbumInfoListener; import com.naman14.timber.lastfmapi.callbacks.ArtistInfoListener; import com.naman14.timber.lastfmapi.callbacks.UserListener; import com.naman14.timber.lastfmapi.models.AlbumInfo; @@ -29,10 +31,14 @@ import com.naman14.timber.lastfmapi.models.ScrobbleQuery; import com.naman14.timber.lastfmapi.models.UserLoginInfo; import com.naman14.timber.lastfmapi.models.UserLoginQuery; +import com.naman14.timber.utils.PreferencesUtility; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.HashSet; +import java.util.Map; +import java.util.TreeMap; import retrofit.Callback; import retrofit.RetrofitError; @@ -49,10 +55,16 @@ public class LastFmClient { public static final String BASE_API_URL = "http://ws.audioscrobbler.com/2.0"; public static final String BASE_SECURE_API_URL = "https://ws.audioscrobbler.com/2.0"; + public static final String PREFERENCES_NAME = "Lastfm"; + static final String PREFERENCE_CACHE_NAME = "Cache"; + private static LastFmClient sInstance; private LastFmRestService mRestService; private LastFmUserRestService mUserRestService; + private HashSet queries; + private boolean isUploading = false; + private Context context; private LastfmUserSession mUserSession; @@ -73,9 +85,8 @@ public static LastFmClient getInstance(Context context) { } private static String generateMD5(String in) { - byte[] bytesOfMessage = new byte[0]; try { - bytesOfMessage = in.getBytes("UTF-8"); + byte[] bytesOfMessage = in.getBytes("UTF-8"); MessageDigest md = MessageDigest.getInstance("MD5"); byte[] digest = md.digest(bytesOfMessage); String out = ""; @@ -90,16 +101,16 @@ private static String generateMD5(String in) { } - public void getAlbumInfo(AlbumQuery albumQuery) { + public void getAlbumInfo(AlbumQuery albumQuery, final AlbumInfoListener listener) { mRestService.getAlbumInfo(albumQuery.mArtist, albumQuery.mALbum, new Callback() { @Override public void success(AlbumInfo albumInfo, Response response) { - + listener.albumInfoSuccess(albumInfo.mAlbum); } @Override public void failure(RetrofitError error) { - + listener.albumInfoFailed(); error.printStackTrace(); } }); @@ -125,6 +136,10 @@ public void getUserLoginInfo(UserLoginQuery userLoginQuery, final UserListener l @Override public void success(UserLoginInfo userLoginInfo, Response response) { Log.d("Logedin", userLoginInfo.mSession.mToken + " " + userLoginInfo.mSession.mUsername); + Bundle extras = new Bundle(); + extras.putString("lf_token",userLoginInfo.mSession.mToken); + extras.putString("lf_user",userLoginInfo.mSession.mUsername); + PreferencesUtility.getInstance(context).updateService(extras); mUserSession = userLoginInfo.mSession; mUserSession.update(context); listener.userSuccess(); @@ -137,28 +152,123 @@ public void failure(RetrofitError error) { }); } - public void Scrobble(ScrobbleQuery scrobbleQuery) { - try { - mUserRestService.getScrobbleInfo(ScrobbleQuery.Method, API_KEY, generateMD5(scrobbleQuery.getSignature(mUserSession.mToken)), mUserSession.mToken, scrobbleQuery.mArtist, scrobbleQuery.mTrack, scrobbleQuery.mTimestamp, new Callback() { + public void Scrobble(final ScrobbleQuery scrobbleQuery) { + if (mUserSession.isLogedin()) + new ScrobbleUploader(scrobbleQuery); + } + + private class ScrobbleUploader { + boolean cachedirty = false; + ScrobbleQuery newquery; + SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE); + + ScrobbleUploader(ScrobbleQuery query) { + if (queries == null) { + queries = new HashSet<>(); + queries.addAll(preferences.getStringSet(PREFERENCE_CACHE_NAME, new HashSet())); + } + if (query != null) { + synchronized (sLock) { + if (isUploading) { + cachedirty = true; + queries.add(query.toString()); + save(); + return; + } + } + newquery = query; + } + upload(); + } + + void upload() { + synchronized (sLock) { + isUploading = true; + } + int size = queries.size(); + if (size == 0 && newquery == null) return; + //Max 50 Scrobbles per Request (restriction by LastFM) + if (size > 50) size = 50; + if (newquery != null && size > 49) size = 49; + final String currentqueries[] = new String[size]; + int n = 0; + for (String t : queries) { + currentqueries[n++] = t; + if (n >= size) break; + } + + TreeMap fields = new TreeMap<>(); + fields.put("method", ScrobbleQuery.Method); + fields.put("api_key", API_KEY); + fields.put("sk", mUserSession.mToken); + + int i = 0; + for (String squery : currentqueries) { + ScrobbleQuery query = new ScrobbleQuery(squery); + fields.put("artist[" + i + ']', query.mArtist); + fields.put("track[" + i + ']', query.mTrack); + fields.put("timestamp[" + i + ']', Long.toString(query.mTimestamp)); + i++; + } + if (newquery != null) { + fields.put("artist[" + i + ']', newquery.mArtist); + fields.put("track[" + i + ']', newquery.mTrack); + fields.put("timestamp[" + i + ']', Long.toString(newquery.mTimestamp)); + } + String sig = ""; + for (Map.Entry ent : fields.entrySet()) { + sig += ent.getKey() + ent.getValue(); + } + sig += API_SECRET; + mUserRestService.getScrobbleInfo(generateMD5(sig), JSON, fields, new Callback() { @Override public void success(ScrobbleInfo scrobbleInfo, Response response) { + synchronized (sLock) { + isUploading = false; + cachedirty = true; + if (newquery != null) newquery = null; + for (String squery : currentqueries) { + queries.remove(squery); + } + if (queries.size() > 0) + upload(); + else + save(); + + } } @Override public void failure(RetrofitError error) { + synchronized (sLock) { + isUploading = false; + //Max 500 scrobbles in Cache + if (newquery != null && queries.size() <= 500) + queries.add(newquery.toString()); + if (cachedirty) + save(); + } } }); - } catch (Exception e) { - e.printStackTrace(); + + } + + void save() { + if (!cachedirty) return; + SharedPreferences.Editor editor = preferences.edit(); + editor.putStringSet(PREFERENCE_CACHE_NAME, queries); + editor.apply(); + } + } public void logout() { this.mUserSession.mToken = null; this.mUserSession.mUsername = null; - SharedPreferences preferences = context.getSharedPreferences("Lastfm", Context.MODE_PRIVATE); + SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); editor.clear(); editor.apply(); diff --git a/app/src/main/java/com/naman14/timber/lastfmapi/LastFmUserRestService.java b/app/src/main/java/com/naman14/timber/lastfmapi/LastFmUserRestService.java index 2975c63ba..ae067a24d 100644 --- a/app/src/main/java/com/naman14/timber/lastfmapi/LastFmUserRestService.java +++ b/app/src/main/java/com/naman14/timber/lastfmapi/LastFmUserRestService.java @@ -3,8 +3,11 @@ import com.naman14.timber.lastfmapi.models.ScrobbleInfo; import com.naman14.timber.lastfmapi.models.UserLoginInfo; +import java.util.Map; + import retrofit.Callback; import retrofit.http.Field; +import retrofit.http.FieldMap; import retrofit.http.FormUrlEncoded; import retrofit.http.POST; @@ -21,6 +24,6 @@ public interface LastFmUserRestService { @POST(BASE) @FormUrlEncoded - void getScrobbleInfo(@Field("method") String method, @Field("api_key") String apikey, @Field("api_sig") String apisig, @Field("sk") String token,@Field("artist") String artist, @Field("track") String track, @Field("timestamp") long timestamp, Callback callback); + void getScrobbleInfo(@Field("api_sig") String apisig, @Field("format") String format, @FieldMap Map fields, Callback callback); } diff --git a/app/src/main/java/com/naman14/timber/lastfmapi/RestServiceFactory.java b/app/src/main/java/com/naman14/timber/lastfmapi/RestServiceFactory.java index 5a932327e..63f02f452 100644 --- a/app/src/main/java/com/naman14/timber/lastfmapi/RestServiceFactory.java +++ b/app/src/main/java/com/naman14/timber/lastfmapi/RestServiceFactory.java @@ -16,17 +16,12 @@ import android.content.Context; +import com.naman14.timber.utils.PreferencesUtility; import com.squareup.okhttp.Cache; -import com.squareup.okhttp.Interceptor; import com.squareup.okhttp.OkHttpClient; -import com.squareup.okhttp.Response; -import java.io.IOException; -import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; -import okio.BufferedSink; -import okio.DeflaterSink; import retrofit.RequestInterceptor; import retrofit.RestAdapter; import retrofit.client.OkClient; @@ -35,7 +30,7 @@ public class RestServiceFactory { private static final String TAG_OK_HTTP = "OkHttp"; private static final long CACHE_SIZE = 1024 * 1024; - public static T createStatic(final Context context, String baseUrl, Class clazz) { + public static T createStatic(final Context context, String baseUrl, Class clazz) { final OkHttpClient okHttpClient = new OkHttpClient(); okHttpClient.setCache(new Cache(context.getApplicationContext().getCacheDir(), @@ -43,10 +38,15 @@ public static T createStatic(final Context context, String baseUrl, Class okHttpClient.setConnectTimeout(40, TimeUnit.SECONDS); RequestInterceptor interceptor = new RequestInterceptor() { + PreferencesUtility prefs = PreferencesUtility.getInstance(context); + @Override public void intercept(RequestFacade request) { //7-days cache - request.addHeader("Cache-Control", String.format("max-age=%d,max-stale=%d", Integer.valueOf(60 * 60 * 24 * 7), Integer.valueOf(31536000))); + request.addHeader("Cache-Control", + String.format("max-age=%d,%smax-stale=%d", + Integer.valueOf(60 * 60 * 24 * 7), + prefs.loadArtistAndAlbumImages() ? "" : "only-if-cached,", Integer.valueOf(31536000))); request.addHeader("Connection", "keep-alive"); } }; @@ -62,16 +62,16 @@ public void intercept(RequestFacade request) { } - public static T create(final Context context, String baseUrl, Class clazz) { - - RestAdapter.Builder builder = new RestAdapter.Builder() - .setEndpoint(baseUrl); - - return builder - .build() - .create(clazz); - - } - + public static T create(final Context context, String baseUrl, Class clazz) { + + RestAdapter.Builder builder = new RestAdapter.Builder() + .setEndpoint(baseUrl); + + return builder + .build() + .create(clazz); + + } + } diff --git a/app/src/main/java/com/naman14/timber/lastfmapi/callbacks/AlbuminfoListener.java b/app/src/main/java/com/naman14/timber/lastfmapi/callbacks/AlbumInfoListener.java similarity index 89% rename from app/src/main/java/com/naman14/timber/lastfmapi/callbacks/AlbuminfoListener.java rename to app/src/main/java/com/naman14/timber/lastfmapi/callbacks/AlbumInfoListener.java index 1c7e7ba3c..a68ebbad2 100644 --- a/app/src/main/java/com/naman14/timber/lastfmapi/callbacks/AlbuminfoListener.java +++ b/app/src/main/java/com/naman14/timber/lastfmapi/callbacks/AlbumInfoListener.java @@ -16,9 +16,9 @@ import com.naman14.timber.lastfmapi.models.LastfmAlbum; -public interface AlbuminfoListener { +public interface AlbumInfoListener { - void albumInfoSucess(LastfmAlbum album); + void albumInfoSuccess(LastfmAlbum album); void albumInfoFailed(); diff --git a/app/src/main/java/com/naman14/timber/lastfmapi/models/LastfmAlbum.java b/app/src/main/java/com/naman14/timber/lastfmapi/models/LastfmAlbum.java index 051d9bc72..402ba4818 100644 --- a/app/src/main/java/com/naman14/timber/lastfmapi/models/LastfmAlbum.java +++ b/app/src/main/java/com/naman14/timber/lastfmapi/models/LastfmAlbum.java @@ -14,5 +14,15 @@ package com.naman14.timber.lastfmapi.models; +import com.google.gson.annotations.SerializedName; + +import java.util.List; + public class LastfmAlbum { + private static final String IMAGE = "image"; + + @SerializedName(IMAGE) + public List mArtwork; + + // Only needed fields have been defined. See https://www.last.fm/api/show/album.getInfo } diff --git a/app/src/main/java/com/naman14/timber/lastfmapi/models/LastfmUserSession.java b/app/src/main/java/com/naman14/timber/lastfmapi/models/LastfmUserSession.java index 969405af9..b703f8dbb 100644 --- a/app/src/main/java/com/naman14/timber/lastfmapi/models/LastfmUserSession.java +++ b/app/src/main/java/com/naman14/timber/lastfmapi/models/LastfmUserSession.java @@ -4,6 +4,7 @@ import android.content.SharedPreferences; import com.google.gson.annotations.SerializedName; +import com.naman14.timber.lastfmapi.LastFmClient; /** * Created by christoph on 17.07.16. @@ -11,20 +12,24 @@ public class LastfmUserSession { private static final String USERNAME = "name"; private static final String TOKEN = "key"; + private static LastfmUserSession session; - private static final String PREFERENCES_NAME = "Lastfm"; public static LastfmUserSession getSession(Context context) { - SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE); - LastfmUserSession session = new LastfmUserSession(); + if (session != null) return session; + SharedPreferences preferences = context.getSharedPreferences(LastFmClient.PREFERENCES_NAME, Context.MODE_PRIVATE); + session = new LastfmUserSession(); session.mToken = preferences.getString(TOKEN, null); session.mUsername = preferences.getString(USERNAME, null); - if (session.mToken == null || session.mUsername == null) return null; return session; } + public boolean isLogedin(){ + return session.mToken != null && session.mUsername != null; + } + public void update(Context context) { - SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE); + SharedPreferences preferences = context.getSharedPreferences(LastFmClient.PREFERENCES_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); if (this.mToken == null || this.mUsername == null) { editor.clear(); diff --git a/app/src/main/java/com/naman14/timber/lastfmapi/models/ScrobbleQuery.java b/app/src/main/java/com/naman14/timber/lastfmapi/models/ScrobbleQuery.java index 2d4ff9636..a9351f5bb 100644 --- a/app/src/main/java/com/naman14/timber/lastfmapi/models/ScrobbleQuery.java +++ b/app/src/main/java/com/naman14/timber/lastfmapi/models/ScrobbleQuery.java @@ -3,6 +3,10 @@ import com.google.gson.annotations.SerializedName; import com.naman14.timber.lastfmapi.LastFmClient; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; + /** * Created by christoph on 17.07.16. */ @@ -22,13 +26,27 @@ public class ScrobbleQuery { public static final String Method = "track.scrobble"; + public ScrobbleQuery(String in) { + String[] arr = in.split(","); + try { + this.mArtist = URLDecoder.decode(arr[0],"UTF-8"); + this.mTrack = URLDecoder.decode(arr[1],"UTF-8"); + this.mTimestamp = Long.parseLong(arr[2],16); + } catch (UnsupportedEncodingException ignored) { } + } + public ScrobbleQuery(String artist, String track, long timestamp) { this.mArtist = artist; this.mTrack = track; this.mTimestamp = timestamp; } - public String getSignature(String token) { - return "api_key" + LastFmClient.API_KEY + ARTIST_NAME + this.mArtist + "method" + Method + "sk" + token + TIMESTAMP_NAME + this.mTimestamp + TRACK_NAME + this.mTrack + LastFmClient.API_SECRET; + @Override + public String toString(){ + try { + return URLEncoder.encode(mArtist,"UTF-8")+','+URLEncoder.encode(mTrack,"UTF-8")+','+Long.toHexString(mTimestamp); + } catch (UnsupportedEncodingException ignored) { + return ""; + } } } diff --git a/app/src/main/java/com/naman14/timber/nowplaying/BaseNowplayingFragment.java b/app/src/main/java/com/naman14/timber/nowplaying/BaseNowplayingFragment.java index 1a3f6071d..a7b496987 100644 --- a/app/src/main/java/com/naman14/timber/nowplaying/BaseNowplayingFragment.java +++ b/app/src/main/java/com/naman14/timber/nowplaying/BaseNowplayingFragment.java @@ -21,30 +21,38 @@ import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; -import android.support.annotation.Nullable; -import android.support.design.widget.FloatingActionButton; -import android.support.v4.app.Fragment; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.ActionBar; -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.Nullable; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import androidx.fragment.app.Fragment; +import androidx.core.content.ContextCompat; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.appcompat.widget.Toolbar; +import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.widget.ImageView; import android.widget.SeekBar; import android.widget.TextView; +import android.widget.Toast; import com.afollestad.appthemeengine.ATE; import com.afollestad.appthemeengine.Config; import com.naman14.timber.MusicPlayer; +import com.naman14.timber.MusicService; import com.naman14.timber.R; import com.naman14.timber.activities.BaseActivity; import com.naman14.timber.adapters.BaseQueueAdapter; +import com.naman14.timber.adapters.SlidingQueueAdapter; import com.naman14.timber.dataloaders.QueueLoader; import com.naman14.timber.listeners.MusicStateListener; import com.naman14.timber.timely.TimelyView; import com.naman14.timber.utils.Helpers; +import com.naman14.timber.utils.NavigationUtils; import com.naman14.timber.utils.PreferencesUtility; import com.naman14.timber.utils.SlideTrackSwitcher; import com.naman14.timber.utils.TimberUtils; @@ -64,67 +72,77 @@ public class BaseNowplayingFragment extends Fragment implements MusicStateListener { - ImageView albumart; - ImageView shuffle; - ImageView repeat; - MaterialIconView previous, next; - PlayPauseButton mPlayPause; - PlayPauseDrawable playPauseDrawable = new PlayPauseDrawable(); - FloatingActionButton playPauseFloating; - View playPauseWrapper; + private MaterialIconView previous, next; + private PlayPauseButton mPlayPause; + private PlayPauseDrawable playPauseDrawable = new PlayPauseDrawable(); + private FloatingActionButton playPauseFloating; + private View playPauseWrapper; + + private String ateKey; + private int overflowcounter = 0; + private TextView songtitle, songalbum, songartist, songduration, elapsedtime; + private SeekBar mProgress; + boolean fragmentPaused = false; + + private CircularSeekBar mCircularProgress; + private BaseQueueAdapter mAdapter; + private SlidingQueueAdapter slidingQueueAdapter; + + private TimelyView timelyView11, timelyView12, timelyView13, timelyView14, timelyView15; + private TextView hourColon; + private int[] timeArr = new int[]{0, 0, 0, 0, 0}; + private Handler mElapsedTimeHandler; + private boolean duetoplaypause = false; - String ateKey; - int accentColor; + public ImageView albumart, shuffle, repeat; + public int accentColor; + public RecyclerView recyclerView; - TextView songtitle, songalbum, songartist, songduration, elapsedtime; - SeekBar mProgress; //seekbar public Runnable mUpdateProgress = new Runnable() { @Override public void run() { + long position = MusicPlayer.position(); if (mProgress != null) { - long position = MusicPlayer.position(); mProgress.setProgress((int) position); if (elapsedtime != null && getActivity() != null) elapsedtime.setText(TimberUtils.makeShortTimeString(getActivity(), position / 1000)); } - - if (MusicPlayer.isPlaying()) { - mProgress.postDelayed(mUpdateProgress, 50); + overflowcounter--; + int delay = 250; //not sure why this delay was so high before + if (overflowcounter < 0 && !fragmentPaused) { + overflowcounter++; + mProgress.postDelayed(mUpdateProgress, delay); //delay } - } }; - CircularSeekBar mCircularProgress; + //circular seekbar public Runnable mUpdateCircularProgress = new Runnable() { @Override public void run() { - + long position = MusicPlayer.position(); if (mCircularProgress != null) { - long position = MusicPlayer.position(); mCircularProgress.setProgress((int) position); if (elapsedtime != null && getActivity() != null) elapsedtime.setText(TimberUtils.makeShortTimeString(getActivity(), position / 1000)); } - + overflowcounter--; if (MusicPlayer.isPlaying()) { - mCircularProgress.postDelayed(mUpdateCircularProgress, 50); + int delay = (int) (1500 - (position % 1000)); + if (overflowcounter < 0 && !fragmentPaused) { + overflowcounter++; + mCircularProgress.postDelayed(mUpdateCircularProgress, delay); + } } } }; - RecyclerView recyclerView; - BaseQueueAdapter mAdapter; - TimelyView timelyView11, timelyView12, timelyView13, timelyView14, timelyView15; - TextView hourColon; - int[] timeArr = new int[]{0, 0, 0, 0, 0}; - Handler mElapsedTimeHandler; public Runnable mUpdateElapsedTime = new Runnable() { @Override public void run() { @@ -157,7 +175,7 @@ public void run() { } }; - private boolean duetoplaypause = false; + private final View.OnClickListener mButtonListener = new View.OnClickListener() { @Override public void onClick(View v) { @@ -182,21 +200,27 @@ public void run() { } }; + private final View.OnClickListener mFLoatingButtonListener = new View.OnClickListener() { @Override public void onClick(View v) { duetoplaypause = true; - playPauseDrawable.transformToPlay(true); - playPauseDrawable.transformToPause(true); - Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() { - MusicPlayer.playOrPause(); - if (recyclerView != null && recyclerView.getAdapter() != null) - recyclerView.getAdapter().notifyDataSetChanged(); - } - }, 250); + if(MusicPlayer.getCurrentTrack() == null) { + Toast.makeText(getContext(), getString(R.string.now_playing_no_track_selected), Toast.LENGTH_SHORT).show(); + } else { + playPauseDrawable.transformToPlay(true); + playPauseDrawable.transformToPause(true); + Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + MusicPlayer.playOrPause(); + if (recyclerView != null && recyclerView.getAdapter() != null) + recyclerView.getAdapter().notifyDataSetChanged(); + } + }, 250); + } + } @@ -209,6 +233,51 @@ public void onCreate(@Nullable Bundle savedInstanceState) { accentColor = Config.accentColor(getActivity(), ateKey); } + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.now_playing, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_go_to_album: + NavigationUtils.goToAlbum(getContext(), MusicPlayer.getCurrentAlbumId()); + break; + case R.id.menu_go_to_artist: + NavigationUtils.goToArtist(getContext(), MusicPlayer.getCurrentArtistId()); + break; + case R.id.action_lyrics: + NavigationUtils.goToLyrics(getContext()); + break; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onPause() { + super.onPause(); + fragmentPaused = true; + } + + @Override + public void onResume() { + super.onResume(); + fragmentPaused = false; + if (mProgress != null) + mProgress.postDelayed(mUpdateProgress, 10); + + if (mCircularProgress != null) + mCircularProgress.postDelayed(mUpdateCircularProgress, 10); + } + public void setSongDetails(View view) { albumart = (ImageView) view.findViewById(R.id.album_art); @@ -238,6 +307,10 @@ public void setSongDetails(View view) { recyclerView = (RecyclerView) view.findViewById(R.id.queue_recyclerview); + + songtitle.setSelected(true); + + Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar); if (toolbar != null) { ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); @@ -384,14 +457,19 @@ public void onClick(View view) { public void updateRepeatState() { if (repeat != null && getActivity() != null) { MaterialDrawableBuilder builder = MaterialDrawableBuilder.with(getActivity()) - .setIcon(MaterialDrawableBuilder.IconValue.REPEAT) .setSizeDp(30); - if (getActivity() != null) { - if (MusicPlayer.getRepeatMode() == 0) { + if (MusicPlayer.getRepeatMode() == MusicService.REPEAT_NONE) { + builder.setIcon(MaterialDrawableBuilder.IconValue.REPEAT); builder.setColor(Config.textColorPrimary(getActivity(), ateKey)); - } else builder.setColor(Config.accentColor(getActivity(), ateKey)); - } + } else if (MusicPlayer.getRepeatMode() == MusicService.REPEAT_CURRENT) { + builder.setIcon(MaterialDrawableBuilder.IconValue.REPEAT_ONCE); + builder.setColor(Config.accentColor(getActivity(), ateKey)); + } else if (MusicPlayer.getRepeatMode() == MusicService.REPEAT_ALL) { + builder.setColor(Config.accentColor(getActivity(), ateKey)); + builder.setIcon(MaterialDrawableBuilder.IconValue.REPEAT); + } + repeat.setImageDrawable(builder.build()); repeat.setOnClickListener(new View.OnClickListener() { @@ -467,6 +545,31 @@ public void onLoadingFailed(String imageUri, View view, FailReason failReason) { }); } + if (songtitle != null && MusicPlayer.getTrackName() != null) { + songtitle.setText(MusicPlayer.getTrackName()); + if(MusicPlayer.getTrackName().length() <= 23){ + songtitle.setTextSize(25); + } + else if(MusicPlayer.getTrackName().length() >= 30){ + songtitle.setTextSize(18); + } + else{ + songtitle.setTextSize(18 + (MusicPlayer.getTrackName().length() - 24)); + } + Log.v("BaseNowPlayingFrag", "Title Text Size: " + songtitle.getTextSize()); + } + if (songartist != null) { + songartist.setText(MusicPlayer.getArtistName()); + songartist.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + NavigationUtils.goToArtist(getContext(), MusicPlayer.getCurrentArtistId()); + } + }); + } + if (songalbum != null) + songalbum.setText(MusicPlayer.getAlbumName()); + } duetoplaypause = false; @@ -476,16 +579,6 @@ public void onLoadingFailed(String imageUri, View view, FailReason failReason) { if (playPauseFloating != null) updatePlayPauseFloatingButton(); - - if (songtitle != null) - songtitle.setText(MusicPlayer.getTrackName()); - - if (songalbum != null) - songalbum.setText(MusicPlayer.getAlbumName()); - - if (songartist != null) - songartist.setText(MusicPlayer.getArtistName()); - if (songduration != null && getActivity() != null) songduration.setText(TimberUtils.makeShortTimeString(getActivity(), MusicPlayer.duration() / 1000)); diff --git a/app/src/main/java/com/naman14/timber/nowplaying/Timber2.java b/app/src/main/java/com/naman14/timber/nowplaying/Timber2.java index 6ccd900db..76c75ebcf 100644 --- a/app/src/main/java/com/naman14/timber/nowplaying/Timber2.java +++ b/app/src/main/java/com/naman14/timber/nowplaying/Timber2.java @@ -25,7 +25,9 @@ import android.view.ViewGroup; import android.widget.ImageView; +import com.afollestad.appthemeengine.Config; import com.naman14.timber.MusicPlayer; +import com.naman14.timber.MusicService; import com.naman14.timber.R; import com.naman14.timber.utils.ImageUtils; @@ -76,13 +78,23 @@ public void onClick(View view) { public void updateRepeatState() { if (repeat != null && getActivity() != null) { MaterialDrawableBuilder builder = MaterialDrawableBuilder.with(getActivity()) - .setIcon(MaterialDrawableBuilder.IconValue.REPEAT) .setSizeDp(30); if (MusicPlayer.getRepeatMode() == 0) { builder.setColor(Color.WHITE); } else builder.setColor(accentColor); + if (MusicPlayer.getRepeatMode() == MusicService.REPEAT_NONE) { + builder.setIcon(MaterialDrawableBuilder.IconValue.REPEAT); + builder.setColor(Color.WHITE); + } else if (MusicPlayer.getRepeatMode() == MusicService.REPEAT_CURRENT) { + builder.setIcon(MaterialDrawableBuilder.IconValue.REPEAT_ONCE); + builder.setColor(accentColor); + } else if (MusicPlayer.getRepeatMode() == MusicService.REPEAT_ALL) { + builder.setColor(accentColor); + builder.setIcon(MaterialDrawableBuilder.IconValue.REPEAT); + } + repeat.setImageDrawable(builder.build()); repeat.setOnClickListener(new View.OnClickListener() { @Override diff --git a/app/src/main/java/com/naman14/timber/nowplaying/Timber4.java b/app/src/main/java/com/naman14/timber/nowplaying/Timber4.java index ec15bd7e8..bfc100fb0 100644 --- a/app/src/main/java/com/naman14/timber/nowplaying/Timber4.java +++ b/app/src/main/java/com/naman14/timber/nowplaying/Timber4.java @@ -20,16 +20,17 @@ import android.graphics.drawable.TransitionDrawable; import android.os.AsyncTask; import android.os.Bundle; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import com.naman14.timber.MusicPlayer; +import com.naman14.timber.MusicService; import com.naman14.timber.R; -import com.naman14.timber.adapters.Timber4QueueAdapter; +import com.naman14.timber.adapters.SlidingQueueAdapter; import com.naman14.timber.dataloaders.QueueLoader; import com.naman14.timber.utils.ImageUtils; @@ -39,7 +40,7 @@ public class Timber4 extends BaseNowplayingFragment { ImageView mBlurredArt; RecyclerView horizontalRecyclerview; - Timber4QueueAdapter horizontalAdapter; + SlidingQueueAdapter horizontalAdapter; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -85,13 +86,22 @@ public void onClick(View view) { public void updateRepeatState() { if (repeat != null && getActivity() != null) { MaterialDrawableBuilder builder = MaterialDrawableBuilder.with(getActivity()) - .setIcon(MaterialDrawableBuilder.IconValue.REPEAT) .setSizeDp(30); if (MusicPlayer.getRepeatMode() == 0) { builder.setColor(Color.WHITE); } else builder.setColor(accentColor); + if (MusicPlayer.getRepeatMode() == MusicService.REPEAT_NONE) { + builder.setIcon(MaterialDrawableBuilder.IconValue.REPEAT); + builder.setColor(Color.WHITE); + } else if (MusicPlayer.getRepeatMode() == MusicService.REPEAT_CURRENT) { + builder.setIcon(MaterialDrawableBuilder.IconValue.REPEAT_ONCE); + builder.setColor(accentColor); + } else if (MusicPlayer.getRepeatMode() == MusicService.REPEAT_ALL) { + builder.setColor(accentColor); + builder.setIcon(MaterialDrawableBuilder.IconValue.REPEAT); + } repeat.setImageDrawable(builder.build()); repeat.setOnClickListener(new View.OnClickListener() { @Override @@ -112,7 +122,7 @@ public void doAlbumArtStuff(Bitmap loadedImage) { private void setupHorizontalQueue() { horizontalRecyclerview.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false)); - horizontalAdapter = new Timber4QueueAdapter(getActivity(), QueueLoader.getQueueSongs(getActivity())); + horizontalAdapter = new SlidingQueueAdapter(getActivity(), QueueLoader.getQueueSongs(getActivity())); horizontalRecyclerview.setAdapter(horizontalAdapter); horizontalRecyclerview.scrollToPosition(MusicPlayer.getQueuePosition() - 3); } diff --git a/app/src/main/java/com/naman14/timber/nowplaying/Timber5.java b/app/src/main/java/com/naman14/timber/nowplaying/Timber5.java new file mode 100644 index 000000000..43d91edfa --- /dev/null +++ b/app/src/main/java/com/naman14/timber/nowplaying/Timber5.java @@ -0,0 +1,158 @@ +package com.naman14.timber.nowplaying; + +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.TransitionDrawable; +import android.os.AsyncTask; +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import com.naman14.timber.MusicPlayer; +import com.naman14.timber.MusicService; +import com.naman14.timber.R; +import com.naman14.timber.adapters.SlidingQueueAdapter; +import com.naman14.timber.dataloaders.QueueLoader; +import com.naman14.timber.utils.ImageUtils; + +import net.steamcrafted.materialiconlib.MaterialDrawableBuilder; + +/** + * Created by naman on 22/02/17. + */ + +public class Timber5 extends BaseNowplayingFragment { + + ImageView mBlurredArt; + RecyclerView recyclerView; + SlidingQueueAdapter adapter; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View rootView = inflater.inflate( + R.layout.fragment_timber5, container, false); + + setMusicStateListener(); + setSongDetails(rootView); + + mBlurredArt = (ImageView) rootView.findViewById(R.id.album_art_blurred); + recyclerView = (RecyclerView) rootView.findViewById(R.id.queue_recyclerview_horizontal) ; + initGestures(mBlurredArt); + setupSlidingQueue(); + + return rootView; + } + + @Override + public void updateShuffleState() { + if (shuffle != null && getActivity() != null) { + MaterialDrawableBuilder builder = MaterialDrawableBuilder.with(getActivity()) + .setIcon(MaterialDrawableBuilder.IconValue.SHUFFLE) + .setSizeDp(30); + + if (MusicPlayer.getShuffleMode() == 0) { + builder.setColor(Color.WHITE); + } else builder.setColor(accentColor); + + shuffle.setImageDrawable(builder.build()); + shuffle.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + MusicPlayer.cycleShuffle(); + updateShuffleState(); + updateRepeatState(); + } + }); + } + } + + @Override + public void updateRepeatState() { + if (repeat != null && getActivity() != null) { + MaterialDrawableBuilder builder = MaterialDrawableBuilder.with(getActivity()) + .setSizeDp(30); + + if (MusicPlayer.getRepeatMode() == 0) { + builder.setColor(Color.WHITE); + } else builder.setColor(accentColor); + + if (MusicPlayer.getRepeatMode() == MusicService.REPEAT_NONE) { + builder.setIcon(MaterialDrawableBuilder.IconValue.REPEAT); + builder.setColor(Color.WHITE); + } else if (MusicPlayer.getRepeatMode() == MusicService.REPEAT_CURRENT) { + builder.setIcon(MaterialDrawableBuilder.IconValue.REPEAT_ONCE); + builder.setColor(accentColor); + } else if (MusicPlayer.getRepeatMode() == MusicService.REPEAT_ALL) { + builder.setColor(accentColor); + builder.setIcon(MaterialDrawableBuilder.IconValue.REPEAT); + } + + repeat.setImageDrawable(builder.build()); + repeat.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + MusicPlayer.cycleRepeat(); + updateRepeatState(); + updateShuffleState(); + } + }); + } + } + + @Override + public void doAlbumArtStuff(Bitmap loadedImage) { + setBlurredAlbumArt blurredAlbumArt = new setBlurredAlbumArt(); + blurredAlbumArt.execute(loadedImage); + } + + private void setupSlidingQueue() { + recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false)); + adapter = new SlidingQueueAdapter((AppCompatActivity) getActivity(), QueueLoader.getQueueSongs(getActivity())); + recyclerView.setAdapter(adapter); + recyclerView.scrollToPosition(MusicPlayer.getQueuePosition() - 3); + } + + + private class setBlurredAlbumArt extends AsyncTask { + + @Override + protected Drawable doInBackground(Bitmap... loadedImage) { + Drawable drawable = null; + try { + drawable = ImageUtils.createBlurredImageFromBitmap(loadedImage[0], getActivity(), 12); + } catch (Exception e) { + e.printStackTrace(); + } + return drawable; + } + + @Override + protected void onPostExecute(Drawable result) { + if (result != null) { + if (mBlurredArt.getDrawable() != null) { + final TransitionDrawable td = + new TransitionDrawable(new Drawable[]{ + mBlurredArt.getDrawable(), + result + }); + mBlurredArt.setImageDrawable(td); + td.startTransition(200); + + } else { + mBlurredArt.setImageDrawable(result); + } + } + } + + @Override + protected void onPreExecute() { + } + } + +} diff --git a/app/src/main/java/com/naman14/timber/nowplaying/Timber6.java b/app/src/main/java/com/naman14/timber/nowplaying/Timber6.java new file mode 100644 index 000000000..d4556b886 --- /dev/null +++ b/app/src/main/java/com/naman14/timber/nowplaying/Timber6.java @@ -0,0 +1,124 @@ +package com.naman14.timber.nowplaying; + +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.SeekBar; +import android.widget.TextView; + +import com.naman14.timber.MusicPlayer; +import com.naman14.timber.MusicService; +import com.naman14.timber.R; +import com.naman14.timber.dataloaders.SongLoader; +import com.naman14.timber.models.Song; +import com.naman14.timber.utils.TimberUtils; +import com.naman14.timber.widgets.CircleImageView; + +import net.steamcrafted.materialiconlib.MaterialDrawableBuilder; + +/** + * Created by naman on 22/02/17. + */ + +public class Timber6 extends BaseNowplayingFragment { + + TextView nextSong; + CircleImageView nextArt; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View rootView = inflater.inflate( + R.layout.fragment_timber6, container, false); + + setMusicStateListener(); + setSongDetails(rootView); + + initGestures(rootView.findViewById(R.id.album_art)); + + ((SeekBar) rootView.findViewById(R.id.song_progress)).getProgressDrawable().setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.MULTIPLY)); + ((SeekBar) rootView.findViewById(R.id.song_progress)).getThumb().setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP)); + + nextSong = (TextView) rootView.findViewById(R.id.title_next); + nextArt = (CircleImageView) rootView.findViewById(R.id.album_art_next); + + rootView.findViewById(R.id.nextView).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + MusicPlayer.next(); + } + }); + + return rootView; + } + + @Override + public void updateShuffleState() { + if (shuffle != null && getActivity() != null) { + MaterialDrawableBuilder builder = MaterialDrawableBuilder.with(getActivity()) + .setIcon(MaterialDrawableBuilder.IconValue.SHUFFLE) + .setSizeDp(30); + + if (MusicPlayer.getShuffleMode() == 0) { + builder.setColor(Color.WHITE); + } else builder.setColor(accentColor); + + shuffle.setImageDrawable(builder.build()); + shuffle.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + MusicPlayer.cycleShuffle(); + updateShuffleState(); + updateRepeatState(); + } + }); + } + } + + @Override + public void updateRepeatState() { + if (repeat != null && getActivity() != null) { + MaterialDrawableBuilder builder = MaterialDrawableBuilder.with(getActivity()) + .setSizeDp(30); + + if (MusicPlayer.getRepeatMode() == 0) { + builder.setColor(Color.WHITE); + } else builder.setColor(accentColor); + + if (MusicPlayer.getRepeatMode() == MusicService.REPEAT_NONE) { + builder.setIcon(MaterialDrawableBuilder.IconValue.REPEAT); + builder.setColor(Color.WHITE); + } else if (MusicPlayer.getRepeatMode() == MusicService.REPEAT_CURRENT) { + builder.setIcon(MaterialDrawableBuilder.IconValue.REPEAT_ONCE); + builder.setColor(accentColor); + } else if (MusicPlayer.getRepeatMode() == MusicService.REPEAT_ALL) { + builder.setColor(accentColor); + builder.setIcon(MaterialDrawableBuilder.IconValue.REPEAT); + } + + repeat.setImageDrawable(builder.build()); + repeat.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + MusicPlayer.cycleRepeat(); + updateRepeatState(); + updateShuffleState(); + } + }); + } + } + + @Override + public void onMetaChanged() { + super.onMetaChanged(); + if (getActivity() != null) { + long nextId = MusicPlayer.getNextAudioId(); + Song next = SongLoader.getSongForID(getActivity(), nextId); + nextSong.setText(next.title); + nextArt.setImageURI(TimberUtils.getAlbumArtUri(next.albumId)); + } + } +} diff --git a/app/src/main/java/com/naman14/timber/permissions/Nammu.java b/app/src/main/java/com/naman14/timber/permissions/Nammu.java index 689fc119f..f039c44f4 100644 --- a/app/src/main/java/com/naman14/timber/permissions/Nammu.java +++ b/app/src/main/java/com/naman14/timber/permissions/Nammu.java @@ -25,6 +25,7 @@ package com.naman14.timber.permissions; import android.Manifest; +import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; @@ -39,6 +40,7 @@ /** * Created by Michal Tajchert on 2015-06-04. */ +@TargetApi(Build.VERSION_CODES.M) public class Nammu { private static final String TAG = Nammu.class.getSimpleName(); private static final String KEY_PREV_PERMISSIONS = "previous_permissions"; diff --git a/app/src/main/java/com/naman14/timber/slidinguppanel/SlidingUpPanelLayout.java b/app/src/main/java/com/naman14/timber/slidinguppanel/SlidingUpPanelLayout.java index e6244edaf..14ab1e792 100644 --- a/app/src/main/java/com/naman14/timber/slidinguppanel/SlidingUpPanelLayout.java +++ b/app/src/main/java/com/naman14/timber/slidinguppanel/SlidingUpPanelLayout.java @@ -10,9 +10,9 @@ import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; -import android.support.v4.content.ContextCompat; -import android.support.v4.view.MotionEventCompat; -import android.support.v4.view.ViewCompat; +import androidx.core.content.ContextCompat; +import androidx.core.view.MotionEventCompat; +import androidx.core.view.ViewCompat; import android.util.AttributeSet; import android.view.Gravity; import android.view.MotionEvent; @@ -945,7 +945,8 @@ private void onPanelDragged(int newTop) { @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { boolean result; - final int save = canvas.save(Canvas.CLIP_SAVE_FLAG); + + final int save = canvas.save(); if (isSlidingEnabled() && mMainView == child) { // Clip against the slider; no sense drawing what will immediately be covered, diff --git a/app/src/main/java/com/naman14/timber/slidinguppanel/ViewDragHelper.java b/app/src/main/java/com/naman14/timber/slidinguppanel/ViewDragHelper.java index 518946b15..ea4d26363 100644 --- a/app/src/main/java/com/naman14/timber/slidinguppanel/ViewDragHelper.java +++ b/app/src/main/java/com/naman14/timber/slidinguppanel/ViewDragHelper.java @@ -18,10 +18,10 @@ package com.naman14.timber.slidinguppanel; import android.content.Context; -import android.support.v4.view.MotionEventCompat; -import android.support.v4.view.VelocityTrackerCompat; -import android.support.v4.view.ViewCompat; -import android.support.v4.widget.ScrollerCompat; +import androidx.core.view.MotionEventCompat; +import androidx.core.view.VelocityTrackerCompat; +import androidx.core.view.ViewCompat; +import androidx.core.widget.ScrollerCompat; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; diff --git a/app/src/main/java/com/naman14/timber/subfragments/ArtistTagFragment.java b/app/src/main/java/com/naman14/timber/subfragments/ArtistTagFragment.java index 670fd1328..990811c50 100644 --- a/app/src/main/java/com/naman14/timber/subfragments/ArtistTagFragment.java +++ b/app/src/main/java/com/naman14/timber/subfragments/ArtistTagFragment.java @@ -15,8 +15,8 @@ package com.naman14.timber.subfragments; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/com/naman14/timber/subfragments/LyricsFragment.java b/app/src/main/java/com/naman14/timber/subfragments/LyricsFragment.java new file mode 100644 index 000000000..69d1decb9 --- /dev/null +++ b/app/src/main/java/com/naman14/timber/subfragments/LyricsFragment.java @@ -0,0 +1,126 @@ +package com.naman14.timber.subfragments; + +import android.content.CursorLoader; +import android.database.Cursor; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import android.provider.MediaStore; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.naman14.timber.MusicPlayer; +import com.naman14.timber.R; +import com.naman14.timber.utils.LyricsExtractor; +import com.naman14.timber.utils.LyricsLoader; + +import java.io.File; + +import retrofit.Callback; +import retrofit.RetrofitError; +import retrofit.client.Response; + +/** + * Created by christoph on 10.12.16. + */ + +public class LyricsFragment extends Fragment { + + private String lyrics = null; + private Toolbar toolbar; + private View rootView; + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + rootView = inflater.inflate(R.layout.fragment_lyrics,container,false); + + toolbar = (Toolbar) rootView.findViewById(R.id.toolbar); + setupToolbar(); + + loadLyrics(); + + return rootView; + } + + private void loadLyrics() { + + final View lyricsView = rootView.findViewById(R.id.lyrics); + final TextView poweredbyTextView = (TextView) lyricsView.findViewById(R.id.lyrics_makeitpersonal); + poweredbyTextView.setVisibility(View.GONE); + final TextView lyricsTextView = (TextView) lyricsView.findViewById(R.id.lyrics_text); + lyricsTextView.setText(getString(R.string.lyrics_loading)); + String filename = getRealPathFromURI(Uri.parse(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + MusicPlayer.getCurrentAudioId())); + if (filename != null && lyrics == null) { + lyrics = LyricsExtractor.getLyrics(new File(filename)); + } + + if (lyrics != null) { + lyricsTextView.setText(lyrics); + } else { + String artist = MusicPlayer.getArtistName(); + if (artist != null) { + int i = artist.lastIndexOf(" feat"); + if (i != -1) { + artist = artist.substring(0, i); + } + + LyricsLoader.getInstance(this.getContext()).getLyrics(artist, MusicPlayer.getTrackName(), new Callback() { + @Override + public void success(String s, Response response) { + lyrics = s; + if (s.equals("Sorry, We don't have lyrics for this song yet.\n")) { + lyricsTextView.setText(R.string.no_lyrics); + } else { + lyricsTextView.setText(s); + poweredbyTextView.setVisibility(View.VISIBLE); + } + } + + @Override + public void failure(RetrofitError error) { + lyricsTextView.setText(R.string.no_lyrics); + } + }); + + } else { + lyricsTextView.setText(R.string.no_lyrics); + } + } + } + + private void setupToolbar() { + + ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); + + final ActionBar ab = ((AppCompatActivity) getActivity()).getSupportActionBar(); + ab.setDisplayHomeAsUpEnabled(true); + if (MusicPlayer.getTrackName() != null) { + ab.setTitle(MusicPlayer.getTrackName()); + } + } + + @Override + public void onResume() { + super.onResume(); + toolbar.setBackgroundColor(Color.TRANSPARENT); + } + + private String getRealPathFromURI(Uri contentUri) { + String[] proj = {MediaStore.Audio.Media.DATA}; + CursorLoader loader = new CursorLoader(this.getContext(), contentUri, proj, null, null, null); + Cursor cursor = loader.loadInBackground(); + int column_index = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA); + cursor.moveToFirst(); + String result = cursor.getString(column_index); + cursor.close(); + return result; + } +} diff --git a/app/src/main/java/com/naman14/timber/subfragments/PlaylistPagerFragment.java b/app/src/main/java/com/naman14/timber/subfragments/PlaylistPagerFragment.java index 4601a618b..73401e04c 100644 --- a/app/src/main/java/com/naman14/timber/subfragments/PlaylistPagerFragment.java +++ b/app/src/main/java/com/naman14/timber/subfragments/PlaylistPagerFragment.java @@ -18,8 +18,8 @@ import android.graphics.Bitmap; import android.os.AsyncTask; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; import android.util.Pair; import android.view.LayoutInflater; import android.view.View; @@ -50,12 +50,12 @@ public class PlaylistPagerFragment extends Fragment { private static final String ARG_PAGE_NUMBER = "pageNumber"; - int[] foregroundColors = {R.color.pink_transparent, R.color.green_transparent, R.color.blue_transparent, R.color.red_transparent, R.color.purple_transparent}; - private int pageNumber, songCountInt; + private int[] foregroundColors = {R.color.pink_transparent, R.color.green_transparent, R.color.blue_transparent, R.color.red_transparent, R.color.purple_transparent}; + private int pageNumber, songCountInt, totalRuntime; private int foregroundColor; private long firstAlbumID = -1; private Playlist playlist; - private TextView playlistame, songcount, playlistnumber, playlisttype; + private TextView playlistame, songcount, playlistnumber, playlisttype, runtime; private ImageView playlistImage; private View foreground; private Context mContext; @@ -83,6 +83,7 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, playlistame = (TextView) rootView.findViewById(R.id.name); playlistnumber = (TextView) rootView.findViewById(R.id.number); songcount = (TextView) rootView.findViewById(R.id.songcount); + runtime = (TextView) rootView.findViewById(R.id.runtime); playlisttype = (TextView) rootView.findViewById(R.id.playlisttype); playlistImage = (ImageView) rootView.findViewById(R.id.playlist_image); foreground = rootView.findViewById(R.id.foreground); @@ -161,7 +162,9 @@ protected String doInBackground(String... params) { case 0: List lastAddedSongs = LastAddedLoader.getLastAddedSongs(getActivity()); songCountInt = lastAddedSongs.size(); - + for(Song song : lastAddedSongs) { + totalRuntime += song.duration / 1000; //for some reason default playlists have songs with durations 1000x larger than they should be + } if (songCountInt != 0) { firstAlbumID = lastAddedSongs.get(0).albumId; return TimberUtils.getAlbumArtUri(firstAlbumID).toString(); @@ -170,6 +173,9 @@ protected String doInBackground(String... params) { TopTracksLoader recentloader = new TopTracksLoader(getActivity(), TopTracksLoader.QueryType.RecentSongs); List recentsongs = SongLoader.getSongsForCursor(TopTracksLoader.getCursor()); songCountInt = recentsongs.size(); + for(Song song : recentsongs){ + totalRuntime += song.duration / 1000; + } if (songCountInt != 0) { firstAlbumID = recentsongs.get(0).albumId; @@ -179,7 +185,9 @@ protected String doInBackground(String... params) { TopTracksLoader topTracksLoader = new TopTracksLoader(getActivity(), TopTracksLoader.QueryType.TopTracks); List topsongs = SongLoader.getSongsForCursor(TopTracksLoader.getCursor()); songCountInt = topsongs.size(); - + for(Song song : topsongs){ + totalRuntime += song.duration / 1000; + } if (songCountInt != 0) { firstAlbumID = topsongs.get(0).albumId; return TimberUtils.getAlbumArtUri(firstAlbumID).toString(); @@ -187,7 +195,9 @@ protected String doInBackground(String... params) { default: List playlistsongs = PlaylistSongLoader.getSongsInPlaylist(getActivity(), playlist.id); songCountInt = playlistsongs.size(); - + for(Song song : playlistsongs){ + totalRuntime += song.duration; + } if (songCountInt != 0) { firstAlbumID = playlistsongs.get(0).albumId; return TimberUtils.getAlbumArtUri(firstAlbumID).toString(); @@ -197,7 +207,9 @@ protected String doInBackground(String... params) { } else { List playlistsongs = PlaylistSongLoader.getSongsInPlaylist(getActivity(), playlist.id); songCountInt = playlistsongs.size(); - + for(Song song : playlistsongs){ + totalRuntime += song.duration; + } if (songCountInt != 0) { firstAlbumID = playlistsongs.get(0).albumId; return TimberUtils.getAlbumArtUri(firstAlbumID).toString(); @@ -219,6 +231,7 @@ public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { } }); songcount.setText(" " + String.valueOf(songCountInt) + " " + mContext.getString(R.string.songs)); + runtime.setText(" " + TimberUtils.makeShortTimeString(mContext, totalRuntime)); } @Override diff --git a/app/src/main/java/com/naman14/timber/subfragments/QuickControlsFragment.java b/app/src/main/java/com/naman14/timber/subfragments/QuickControlsFragment.java index abedcbb79..9f7512335 100644 --- a/app/src/main/java/com/naman14/timber/subfragments/QuickControlsFragment.java +++ b/app/src/main/java/com/naman14/timber/subfragments/QuickControlsFragment.java @@ -21,7 +21,8 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; -import android.support.v4.app.Fragment; +import androidx.fragment.app.Fragment; + import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -56,6 +57,17 @@ public class QuickControlsFragment extends Fragment implements MusicStateListene public static View topContainer; private ProgressBar mProgress; private SeekBar mSeekBar; + private int overflowcounter = 0; + private PlayPauseButton mPlayPause, mPlayPauseExpanded; + private TextView mTitle, mTitleExpanded; + private TextView mArtist, mArtistExpanded; + private ImageView mAlbumArt, mBlurredArt; + private View rootView; + private View playPauseWrapper, playPauseWrapperExpanded; + private MaterialIconView previous, next; + private boolean duetoplaypause = false; + private boolean fragmentPaused = false; + public Runnable mUpdateProgress = new Runnable() { @Override @@ -65,20 +77,18 @@ public void run() { mProgress.setProgress((int) position); mSeekBar.setProgress((int) position); + overflowcounter--; if (MusicPlayer.isPlaying()) { - mProgress.postDelayed(mUpdateProgress, 50); + int delay = (int) (1500 - (position % 1000)); + if (overflowcounter < 0 && !fragmentPaused) { + overflowcounter++; + mProgress.postDelayed(mUpdateProgress, delay); + } } else mProgress.removeCallbacks(this); } }; - private PlayPauseButton mPlayPause, mPlayPauseExpanded; - private TextView mTitle, mTitleExpanded; - private TextView mArtist, mArtistExpanded; - private ImageView mAlbumArt, mBlurredArt; - private View rootView; - private View playPauseWrapper, playPauseWrapperExpanded; - private MaterialIconView previous, next; - private boolean duetoplaypause = false; + private final View.OnClickListener mPlayPauseListener = new View.OnClickListener() { @Override public void onClick(View v) { @@ -100,6 +110,7 @@ public void run() { } }; + private final View.OnClickListener mPlayPauseExpandedListener = new View.OnClickListener() { @Override public void onClick(View v) { @@ -203,10 +214,10 @@ public void run() { ((BaseActivity) getActivity()).setMusicStateListenerListener(this); if (PreferencesUtility.getInstance(getActivity()).isGesturesEnabled()) { - new SlideTrackSwitcher(){ + new SlideTrackSwitcher() { @Override public void onClick() { - NavigationUtils.navigateToNowplaying(getActivity(),false); + NavigationUtils.navigateToNowplaying(getActivity(), false); } }.attach(rootView.findViewById(R.id.root_view)); } @@ -215,6 +226,12 @@ public void onClick() { return rootView; } + @Override + public void onPause() { + super.onPause(); + fragmentPaused = true; + } + public void updateNowplayingCard() { mTitle.setText(MusicPlayer.getTrackName()); mArtist.setText(MusicPlayer.getArtistName()); @@ -273,6 +290,9 @@ public void onStop() { public void onResume() { super.onResume(); topContainer = rootView.findViewById(R.id.topContainer); + fragmentPaused = false; + if (mProgress != null) + mProgress.postDelayed(mUpdateProgress, 10); } diff --git a/app/src/main/java/com/naman14/timber/subfragments/StyleSelectorFragment.java b/app/src/main/java/com/naman14/timber/subfragments/StyleSelectorFragment.java index cdd0fa271..cb04593be 100644 --- a/app/src/main/java/com/naman14/timber/subfragments/StyleSelectorFragment.java +++ b/app/src/main/java/com/naman14/timber/subfragments/StyleSelectorFragment.java @@ -14,24 +14,28 @@ package com.naman14.timber.subfragments; +import android.content.Context; +import android.content.SharedPreferences; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentStatePagerAdapter; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentStatePagerAdapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.naman14.timber.R; import com.naman14.timber.utils.Constants; +import com.naman14.timber.utils.NavigationUtils; import com.naman14.timber.widgets.MultiViewPager; public class StyleSelectorFragment extends Fragment { public String ACTION = "action"; - FragmentStatePagerAdapter adapter; - MultiViewPager pager; + private FragmentStatePagerAdapter adapter; + private MultiViewPager pager; private SubStyleSelectorFragment selectorFragment; + private SharedPreferences preferences; public static StyleSelectorFragment newInstance(String what) { StyleSelectorFragment fragment = new StyleSelectorFragment(); @@ -47,6 +51,7 @@ public void onCreate(Bundle savedInstanceState) { if (getArguments() != null) { ACTION = getArguments().getString(Constants.SETTINGS_STYLE_SELECTOR_WHAT); } + preferences = getActivity().getSharedPreferences(Constants.FRAGMENT_ID, Context.MODE_PRIVATE); } @@ -64,7 +69,7 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Override public int getCount() { - return 4; + return 6; } @Override @@ -79,18 +84,22 @@ public int getItemPosition(Object object) { } }; pager.setAdapter(adapter); + scrollToCurrentStyle(); return rootView; } public void updateCurrentStyle() { - if (selectorFragment != null) + if (selectorFragment != null) { adapter.notifyDataSetChanged(); + scrollToCurrentStyle(); + } } - public void scrollToCurrentStyle(int page) { - pager.setCurrentItem(page); + public void scrollToCurrentStyle() { + String fragmentID = preferences.getString(Constants.NOWPLAYING_FRAGMENT_ID, Constants.TIMBER3); + pager.setCurrentItem(NavigationUtils.getIntForCurrentNowplaying(fragmentID)); } } diff --git a/app/src/main/java/com/naman14/timber/subfragments/SubStyleSelectorFragment.java b/app/src/main/java/com/naman14/timber/subfragments/SubStyleSelectorFragment.java index 96472babf..13a7159aa 100644 --- a/app/src/main/java/com/naman14/timber/subfragments/SubStyleSelectorFragment.java +++ b/app/src/main/java/com/naman14/timber/subfragments/SubStyleSelectorFragment.java @@ -15,10 +15,12 @@ package com.naman14.timber.subfragments; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -26,19 +28,23 @@ import android.widget.LinearLayout; import android.widget.TextView; +import com.afollestad.materialdialogs.DialogAction; +import com.afollestad.materialdialogs.MaterialDialog; import com.naman14.timber.R; +import com.naman14.timber.activities.DonateActivity; import com.naman14.timber.utils.Constants; +import com.naman14.timber.utils.NavigationUtils; import com.naman14.timber.utils.PreferencesUtility; public class SubStyleSelectorFragment extends Fragment { private static final String ARG_PAGE_NUMBER = "pageNumber"; private static final String WHAT = "what"; - SharedPreferences.Editor editor; - SharedPreferences preferences; - LinearLayout currentStyle; - View foreground; - ImageView styleImage; + private SharedPreferences.Editor editor; + private SharedPreferences preferences; + private LinearLayout currentStyle; + private View foreground; + private ImageView styleImage, imgLock; public static SubStyleSelectorFragment newInstance(int pageNumber, String what) { SubStyleSelectorFragment fragment = new SubStyleSelectorFragment(); @@ -56,12 +62,22 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, TextView styleName = (TextView) rootView.findViewById(R.id.style_name); styleName.setText(String.valueOf(getArguments().getInt(ARG_PAGE_NUMBER) + 1)); + preferences = getActivity().getSharedPreferences(Constants.FRAGMENT_ID, Context.MODE_PRIVATE); styleImage = (ImageView) rootView.findViewById(R.id.style_image); + imgLock = (ImageView) rootView.findViewById(R.id.img_lock); + styleImage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - setPreferences(); + if (getArguments().getInt(ARG_PAGE_NUMBER) >= 4) { + if (isUnlocked()) { + setPreferences(); + } else { + showPurchaseDialog(); + } + } else + setPreferences(); } }); @@ -78,6 +94,12 @@ public void onClick(View view) { case 3: styleImage.setImageResource(R.drawable.timber_4_nowplaying_x); break; + case 4: + styleImage.setImageResource(R.drawable.timber_5_nowplaying_x); + break; + case 5: + styleImage.setImageResource(R.drawable.timber_6_nowplaying_x); + break; } currentStyle = (LinearLayout) rootView.findViewById(R.id.currentStyle); @@ -88,13 +110,55 @@ public void onClick(View view) { return rootView; } + private boolean isUnlocked() { + return getActivity() != null && PreferencesUtility.getInstance(getActivity()).fullUnlocked(); + } + + @Override + public void onResume() { + super.onResume(); + updateLockedStatus(); + } + + private void updateLockedStatus() { + if (getArguments().getInt(ARG_PAGE_NUMBER) >= 4 && !isUnlocked()) { + imgLock.setVisibility(View.VISIBLE); + foreground.setVisibility(View.VISIBLE); + } + else { + imgLock.setVisibility(View.GONE); + foreground.setVisibility(View.GONE); + } + } + private void showPurchaseDialog() { + MaterialDialog dialog = new MaterialDialog.Builder(getActivity()) + .title("Purchase") + .content("This now playing style is available after a one time purchase of any amount. Support development and unlock this style?") + .positiveText("Support development") + .neutralText("Restore purchases") + .onPositive(new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + startActivity(new Intent(getActivity(), DonateActivity.class)); + dialog.dismiss(); + } + }).onNeutral(new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + Intent intent = new Intent(getActivity(), DonateActivity.class); + intent.putExtra("title", "Restoring purchases.."); + intent.setAction("restore"); + startActivity(intent); + dialog.dismiss(); + } + }) + .show(); + } + public void setCurrentStyle() { - preferences = getActivity().getSharedPreferences(Constants.FRAGMENT_ID, Context.MODE_PRIVATE); String fragmentID = preferences.getString(Constants.NOWPLAYING_FRAGMENT_ID, Constants.TIMBER3); - ((StyleSelectorFragment) getParentFragment()).scrollToCurrentStyle(getIntForCurrentNowplaying(fragmentID)); - - if (getArguments().getInt(ARG_PAGE_NUMBER) == getIntForCurrentNowplaying(fragmentID)) { + if (getArguments().getInt(ARG_PAGE_NUMBER) == NavigationUtils.getIntForCurrentNowplaying(fragmentID)) { currentStyle.setVisibility(View.VISIBLE); foreground.setVisibility(View.VISIBLE); } else { @@ -127,25 +191,14 @@ private String getStyleForPageNumber() { return Constants.TIMBER3; case 3: return Constants.TIMBER4; + case 4: + return Constants.TIMBER5; + case 5: + return Constants.TIMBER6; default: return Constants.TIMBER3; } } - private int getIntForCurrentNowplaying(String nowPlaying) { - switch (nowPlaying) { - case Constants.TIMBER1: - return 0; - case Constants.TIMBER2: - return 1; - case Constants.TIMBER3: - return 2; - case Constants.TIMBER4: - return 3; - default: - return 2; - } - - } } diff --git a/app/src/main/java/com/naman14/timber/utils/ATEUtils.java b/app/src/main/java/com/naman14/timber/utils/ATEUtils.java index 239ea7c4c..4663c6723 100644 --- a/app/src/main/java/com/naman14/timber/utils/ATEUtils.java +++ b/app/src/main/java/com/naman14/timber/utils/ATEUtils.java @@ -7,10 +7,10 @@ import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.os.Build; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.FloatingActionButton; -import android.support.v4.widget.DrawerLayout; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import androidx.drawerlayout.widget.DrawerLayout; import android.view.View; import android.view.ViewGroup; import android.view.Window; diff --git a/app/src/main/java/com/naman14/timber/utils/Constants.java b/app/src/main/java/com/naman14/timber/utils/Constants.java index 7d4280f3b..4856ceb88 100644 --- a/app/src/main/java/com/naman14/timber/utils/Constants.java +++ b/app/src/main/java/com/naman14/timber/utils/Constants.java @@ -22,6 +22,7 @@ public class Constants { public static final String NAVIGATE_ALBUM = "navigate_album"; public static final String NAVIGATE_ARTIST = "navigate_artist"; public static final String NAVIGATE_NOWPLAYING = "navigate_nowplaying"; + public static final String NAVIGATE_LYRICS = "navigate_lyrics"; public static final String NAVIGATE_PLAYLIST_RECENT = "navigate_playlist_recent"; public static final String NAVIGATE_PLAYLIST_LASTADDED = "navigate_playlist_lastadded"; @@ -43,6 +44,8 @@ public class Constants { public static final String TIMBER2 = "timber2"; public static final String TIMBER3 = "timber3"; public static final String TIMBER4 = "timber4"; + public static final String TIMBER5 = "timber5"; + public static final String TIMBER6 = "timber6"; public static final String NAVIGATE_SETTINGS = "navigate_settings"; public static final String NAVIGATE_SEARCH = "navigate_search"; @@ -64,6 +67,8 @@ public class Constants { public static final String ACTIVITY_TRANSITION = "activity_transition"; + public static final int CAST_SERVER_PORT = 8080; + } diff --git a/app/src/main/java/com/naman14/timber/utils/FabAnimationUtils.java b/app/src/main/java/com/naman14/timber/utils/FabAnimationUtils.java index 635ac0135..991b0b7ca 100644 --- a/app/src/main/java/com/naman14/timber/utils/FabAnimationUtils.java +++ b/app/src/main/java/com/naman14/timber/utils/FabAnimationUtils.java @@ -15,9 +15,9 @@ package com.naman14.timber.utils; import android.os.Build; -import android.support.v4.view.ViewCompat; -import android.support.v4.view.ViewPropertyAnimatorListener; -import android.support.v4.view.animation.FastOutSlowInInterpolator; +import androidx.core.view.ViewCompat; +import androidx.core.view.ViewPropertyAnimatorListener; +import androidx.interpolator.view.animation.FastOutSlowInInterpolator; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; diff --git a/app/src/main/java/com/naman14/timber/utils/Helpers.java b/app/src/main/java/com/naman14/timber/utils/Helpers.java index 47d726214..260b7689c 100644 --- a/app/src/main/java/com/naman14/timber/utils/Helpers.java +++ b/app/src/main/java/com/naman14/timber/utils/Helpers.java @@ -23,17 +23,20 @@ import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; + +import android.text.Html; import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; +import com.afollestad.materialdialogs.MaterialDialog; import com.naman14.timber.R; public class Helpers { @@ -57,96 +60,27 @@ public static String getATEKey(Context context) { public static class AboutDialog extends DialogFragment { - String urlgooglelus = "https://plus.google.com/u/0/+NamanDwivedi14"; - String urlcommunity = "https://plus.google.com/communities/111029425713454201429"; - String urltwitter = "https://twitter.com/naman1405"; - String urlgithub = "https://github.com/naman14"; - String urlsource = "https://github.com/naman14/Timber/issues"; - public AboutDialog() { } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - LayoutInflater layoutInflater = (LayoutInflater) getActivity().getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - LinearLayout aboutBodyView = (LinearLayout) layoutInflater.inflate(R.layout.layout_about_dialog, null); - - TextView appversion = (TextView) aboutBodyView.findViewById(R.id.app_version_name); - - TextView googleplus = (TextView) aboutBodyView.findViewById(R.id.googleplus); - TextView twitter = (TextView) aboutBodyView.findViewById(R.id.twitter); - TextView github = (TextView) aboutBodyView.findViewById(R.id.github); - TextView source = (TextView) aboutBodyView.findViewById(R.id.source); - TextView community = (TextView) aboutBodyView.findViewById(R.id.feature_request); - - TextView dismiss = (TextView) aboutBodyView.findViewById(R.id.dismiss_dialog); - dismiss.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - dismiss(); - } - }); - googleplus.setPaintFlags(googleplus.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); - twitter.setPaintFlags(twitter.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); - github.setPaintFlags(github.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); - - googleplus.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse(urlgooglelus)); - startActivity(i); - } - - }); - twitter.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse(urltwitter)); - startActivity(i); - } - - }); - github.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse(urlgithub)); - startActivity(i); - } - - }); - source.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse(urlsource)); - startActivity(i); - } - }); - community.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse(urlcommunity)); - startActivity(i); - } - }); + String appName = "Timber"; try { PackageInfo pInfo = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0); String version = pInfo.versionName; int versionCode = pInfo.versionCode; - appversion.setText("Timber " + version); + appName = "Timber " + version; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } - return new AlertDialog.Builder(getActivity()) - .setView(aboutBodyView) - .create(); + return new MaterialDialog.Builder(getActivity()) + .title(appName) + .content(Html.fromHtml(getString(R.string.about_dialog_body))) + .positiveText("Dismiss") + .build(); } } diff --git a/app/src/main/java/com/naman14/timber/utils/ImageUtils.java b/app/src/main/java/com/naman14/timber/utils/ImageUtils.java index 73b940821..4bf5605fe 100644 --- a/app/src/main/java/com/naman14/timber/utils/ImageUtils.java +++ b/app/src/main/java/com/naman14/timber/utils/ImageUtils.java @@ -19,12 +19,105 @@ import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.support.v8.renderscript.RenderScript; +import android.renderscript.Allocation; +import android.renderscript.Element; +import android.renderscript.RenderScript; +import android.renderscript.ScriptIntrinsicBlur; +import android.view.View; +import android.widget.ImageView; + +import com.naman14.timber.R; +import com.naman14.timber.dataloaders.AlbumLoader; +import com.naman14.timber.lastfmapi.LastFmClient; +import com.naman14.timber.lastfmapi.callbacks.AlbumInfoListener; +import com.naman14.timber.lastfmapi.models.AlbumQuery; +import com.naman14.timber.lastfmapi.models.LastfmAlbum; +import com.naman14.timber.models.Album; +import com.nostra13.universalimageloader.core.DisplayImageOptions; +import com.nostra13.universalimageloader.core.ImageLoader; +import com.nostra13.universalimageloader.core.assist.FailReason; +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; +import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; public class ImageUtils { + private static final DisplayImageOptions lastfmDisplayImageOptions = + new DisplayImageOptions.Builder() + .cacheInMemory(true) + .cacheOnDisk(true) + .showImageOnFail(R.drawable.ic_empty_music2) + .build(); + + private static final DisplayImageOptions diskDisplayImageOptions = + new DisplayImageOptions.Builder() + .cacheInMemory(true) + .build(); + + public static void loadAlbumArtIntoView(final long albumId, final ImageView view) { + loadAlbumArtIntoView(albumId, view, new SimpleImageLoadingListener()); + } + + public static void loadAlbumArtIntoView(final long albumId, final ImageView view, + final ImageLoadingListener listener) { + if (PreferencesUtility.getInstance(view.getContext()).alwaysLoadAlbumImagesFromLastfm()) { + loadAlbumArtFromLastfm(albumId, view, listener); + } else { + loadAlbumArtFromDiskWithLastfmFallback(albumId, view, listener); + } + } + + private static void loadAlbumArtFromDiskWithLastfmFallback(final long albumId, ImageView view, + final ImageLoadingListener listener) { + ImageLoader.getInstance() + .displayImage(TimberUtils.getAlbumArtUri(albumId).toString(), + view, + diskDisplayImageOptions, + new SimpleImageLoadingListener() { + @Override + public void onLoadingFailed(String imageUri, View view, + FailReason failReason) { + loadAlbumArtFromLastfm(albumId, (ImageView) view, listener); + listener.onLoadingFailed(imageUri, view, failReason); + } + + @Override + public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { + listener.onLoadingComplete(imageUri, view, loadedImage); + } + }); + } + + private static void loadAlbumArtFromLastfm(long albumId, final ImageView albumArt, final ImageLoadingListener listener) { + Album album = AlbumLoader.getAlbum(albumArt.getContext(), albumId); + LastFmClient.getInstance(albumArt.getContext()) + .getAlbumInfo(new AlbumQuery(album.title, album.artistName), + new AlbumInfoListener() { + @Override + public void albumInfoSuccess(final LastfmAlbum album) { + if (album != null) { + ImageLoader.getInstance() + .displayImage(album.mArtwork.get(4).mUrl, + albumArt, + lastfmDisplayImageOptions, new SimpleImageLoadingListener(){ + @Override + public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { + listener.onLoadingComplete(imageUri, view, loadedImage); + } + + @Override + public void onLoadingFailed(String imageUri, View view, FailReason failReason) { + listener.onLoadingFailed(imageUri, view, failReason); + } + }); + } + } + + @Override + public void albumInfoFailed() { } + }); + } public static Drawable createBlurredImageFromBitmap(Bitmap bitmap, Context context, int inSampleSize) { @@ -38,9 +131,9 @@ public static Drawable createBlurredImageFromBitmap(Bitmap bitmap, Context conte ByteArrayInputStream bis = new ByteArrayInputStream(imageInByte); Bitmap blurTemplate = BitmapFactory.decodeStream(bis, null, options); - final android.support.v8.renderscript.Allocation input = android.support.v8.renderscript.Allocation.createFromBitmap(rs, blurTemplate); - final android.support.v8.renderscript.Allocation output = android.support.v8.renderscript.Allocation.createTyped(rs, input.getType()); - final android.support.v8.renderscript.ScriptIntrinsicBlur script = android.support.v8.renderscript.ScriptIntrinsicBlur.create(rs, android.support.v8.renderscript.Element.U8_4(rs)); + final Allocation input = Allocation.createFromBitmap(rs, blurTemplate); + final Allocation output = Allocation.createTyped(rs, input.getType()); + final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); script.setRadius(8f); script.setInput(input); script.forEach(output); @@ -48,5 +141,4 @@ public static Drawable createBlurredImageFromBitmap(Bitmap bitmap, Context conte return new BitmapDrawable(context.getResources(), blurTemplate); } - } diff --git a/app/src/main/java/com/naman14/timber/utils/LyricsExtractor.java b/app/src/main/java/com/naman14/timber/utils/LyricsExtractor.java new file mode 100644 index 000000000..151bac513 --- /dev/null +++ b/app/src/main/java/com/naman14/timber/utils/LyricsExtractor.java @@ -0,0 +1,227 @@ +package com.naman14.timber.utils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.Arrays; + +/** + * Created by Christoph Walcher on 03.12.16. + */ + +public class LyricsExtractor { + public static String getLyrics(File file){ + String filename = file.getName(); + String fileending = filename.substring(filename.lastIndexOf('.')+1,filename.length()).toLowerCase(); + try{ + switch(fileending){ + case "mp3": + return getLyricsID3(file); + case "mp4": + case "m4a": + case "aac": + return getLyricsMP4(file); + case "ogg": + case "oga": + return getLyricsVorbis(file); + } + }catch(Exception e){} + return null; + } + + private static int readOgg(byte[] buf, InputStream in, int bytesinpage, int skip) throws IOException { + int toread = skip!=-1?skip:buf.length; + int offset = 0; + while(toread>0){ + if(bytesinpage==0){ + byte magic[] = new byte[4]; + in.read(magic); + if(!Arrays.equals(magic,new byte[]{'O','g','g','S'})){ + in.close(); + throw new IOException(); + } + byte header[] = new byte[23]; + in.read(header); + int count = header[22]& 0xFF; + while(count-->0){ + bytesinpage += in.read(); + } + } + int read = toread; + if(bytesinpage-toread<0)read = bytesinpage; + if(skip != -1) + in.skip(read); + else + in.read(buf, offset, read); + offset += read; + toread -= read; + bytesinpage -= read; + } + return bytesinpage; + } + + private static String getLyricsVorbis(File file) throws Exception{ + FileInputStream in = new FileInputStream(file); + int bytesinpage = 0; + byte buffer[] = new byte[7]; + bytesinpage = readOgg(buffer,in,bytesinpage,-1); + if(!Arrays.equals(buffer, new byte[]{1,'v','o','r','b','i','s'})){ + in.close(); + return null; + } + bytesinpage = readOgg(null,in,bytesinpage, 23); + bytesinpage = readOgg(buffer,in,bytesinpage,-1); + if(!Arrays.equals(buffer, new byte[]{3,'v','o','r','b','i','s'})){ + in.close(); + return null; + } + byte length[] = new byte[4]; + bytesinpage = readOgg(length, in, bytesinpage,-1); + bytesinpage = readOgg(null, in, bytesinpage, byteArrayToInt(length)); + bytesinpage = readOgg(length, in, bytesinpage,-1); + int count = byteArrayToIntLE(length); + while(count-->0){ + bytesinpage = readOgg(length, in, bytesinpage,-1); + int comment_len = byteArrayToIntLE(length); + byte lyrics_tag[] = new byte[]{'L','Y','R','I','C','S','='}; + if(comment_len<=lyrics_tag.length){ + bytesinpage = readOgg(null, in, bytesinpage, comment_len); + continue; + } + byte comment_probe[] = new byte[lyrics_tag.length]; + bytesinpage = readOgg(comment_probe, in, bytesinpage,-1); + if(Arrays.equals(comment_probe,lyrics_tag)){ + byte lyrics[] = new byte[comment_len - lyrics_tag.length]; + readOgg(lyrics, in, bytesinpage,-1); + in.close(); + return new String(lyrics); + }else{ + bytesinpage = readOgg(null, in, bytesinpage, comment_len - lyrics_tag.length); + } + } + in.close(); + return null; + + } + + + private static String getLyricsMP4(File file) throws Exception{ + FileInputStream in = new FileInputStream(file); + + byte head[] = new byte[4]; + in.read(head); + int len = byteArrayToInt(head); + in.read(head); + if (!Arrays.equals(head, new byte[]{'f','t','y','p'})){ + in.close(); + return null; + } + in.skip(len - 8); + final byte path[][] = new byte[][]{{'m','o','o','v'},{'u','d','t','a'},{'m','e','t','a'},{'i','l','s','t'},{(byte) '©','l','y','r'},{'d','a','t','a'}}; + int atom_size = Integer.MAX_VALUE; + outter: + for(byte[] atom: path){ + while(in.available()>0){ + byte buffer[] = new byte[4]; + in.read(buffer); + len = byteArrayToInt(buffer); + if(len==0)continue; + in.read(buffer); + if(len>atom_size){ + in.close(); + return null; + } + if (Arrays.equals(buffer, atom)){ + atom_size = len - 8; + //Found Atom search next atom + continue outter; + }else{ + //Skip Atom + in.skip(len - 8); + atom_size-=len; + } + } + in.close(); + return null; + } + in.skip(8); + byte buffer[] = new byte[atom_size-8]; + in.read(buffer); + in.close(); + return new String(buffer); + } + + + private static String getLyricsID3(File file) throws Exception{ + FileInputStream in = new FileInputStream(file); + byte buffer[] = new byte[4]; + in.read(buffer, 0, 3); + if (!Arrays.equals(buffer, new byte[] { 'I', 'D', '3', 0 })){ + in.close(); + return null; + } + + in.read(buffer, 0, 3); + boolean ext = (buffer[2] & (byte) 0b0100000) != 0; + in.read(buffer); + int len = buffer[3] & 0x7F | (buffer[2] & 0x7F) << 7 | (buffer[1] & 0x7F) << 14 | (buffer[0] & 0x7F) << 21; + if (ext) { + in.read(buffer); len-=4; + int ext_len = byteArrayToInt(buffer); + in.skip(ext_len); len -= ext_len; + + } + while (len > 0) { + byte tag_name[] = new byte[4]; + in.read(tag_name); len-=4; + if(tag_name[0]==0)break; + in.read(buffer); len -=4; + int tag_len = byteArrayToInt(buffer); + in.read(buffer,0,2); len-=2; + if(Arrays.equals(tag_name, new byte[] { 'U', 'S', 'L', 'T' })){ + byte head[] = new byte[4]; + in.read(head); len -= 4; tag_len -= 4; + while(in.read()!=0){ + len--; + tag_len--; + } + if(head[0]==1)in.read(); + byte tag_value[] = new byte[tag_len]; + in.read(tag_value); len -= tag_len; + in.close(); + Charset charset = null; + switch (head[0]){ + case 0: charset = Charset.forName("ISO-8859-1"); + break; + case 1: charset = Charset.forName("UTF-16"); + break; + case 2: charset = Charset.forName("UTF-16BE"); + break; + case 3: charset = Charset.forName("UTF-8"); + break; + default: + return null; + } + return new String(tag_value,charset); + + }else{ + in.skip(tag_len); len -= tag_len; + } + + } + in.close(); + return null; + + } + + private static int byteArrayToInt(byte[] b) { + return b[3] & 0xFF | (b[2] & 0xFF) << 8 | (b[1] & 0xFF) << 16 | (b[0] & 0xFF) << 24; + } + + private static int byteArrayToIntLE(byte[] b) { + return b[0] & 0xFF | (b[1] & 0xFF) << 8 | (b[2] & 0xFF) << 16 | (b[3] & 0xFF) << 24; + } + +} diff --git a/app/src/main/java/com/naman14/timber/utils/LyricsLoader.java b/app/src/main/java/com/naman14/timber/utils/LyricsLoader.java new file mode 100644 index 000000000..f668f3465 --- /dev/null +++ b/app/src/main/java/com/naman14/timber/utils/LyricsLoader.java @@ -0,0 +1,105 @@ +package com.naman14.timber.utils; + +import android.content.Context; + +import com.squareup.okhttp.Cache; +import com.squareup.okhttp.OkHttpClient; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.util.concurrent.TimeUnit; + +import retrofit.Callback; +import retrofit.RequestInterceptor; +import retrofit.RestAdapter; +import retrofit.client.OkClient; +import retrofit.converter.ConversionException; +import retrofit.converter.Converter; +import retrofit.http.GET; +import retrofit.http.Headers; +import retrofit.http.Query; +import retrofit.mime.TypedInput; +import retrofit.mime.TypedOutput; + +/** + * Created by Christoph Walcher on 03.12.16. + */ + +public class LyricsLoader { + private static LyricsLoader instance = null; + private static final String BASE_API_URL = "https://makeitpersonal.co"; + private static final long CACHE_SIZE = 1024 * 1024; + private LyricsRestService service; + + public static LyricsLoader getInstance(Context con) { + if(instance==null)instance = new LyricsLoader(con); + return instance; + } + + private LyricsLoader(Context con){ + final OkHttpClient okHttpClient = new OkHttpClient(); + + okHttpClient.setCache(new Cache(con.getApplicationContext().getCacheDir(), + CACHE_SIZE)); + okHttpClient.setConnectTimeout(20, TimeUnit.SECONDS); + + RequestInterceptor interceptor = new RequestInterceptor() { + @Override + public void intercept(RequestFacade request) { + //7-days cache + request.addHeader("Cache-Control", String.format("max-age=%d,max-stale=%d", Integer.valueOf(60 * 60 * 24 * 7), Integer.valueOf(31536000))); + } + }; + + RestAdapter.Builder builder = new RestAdapter.Builder() + .setEndpoint(BASE_API_URL) + .setRequestInterceptor(interceptor) + .setConverter(new Converter() { + @Override + public Object fromBody(TypedInput arg0, Type arg1) + throws ConversionException { + + try { + BufferedReader br = null; + StringBuilder sb = new StringBuilder(); + + String line; + + br = new BufferedReader(new InputStreamReader(arg0.in())); + while ((line = br.readLine()) != null) { + sb.append(line); + sb.append('\n'); + } + return sb.toString(); + + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public TypedOutput toBody(Object arg0) { + return null; + } + }) + .setClient(new OkClient(okHttpClient)); + + service = builder + .build() + .create(LyricsRestService.class); + } + + public void getLyrics(String artist, String title, Callback callback){ + service.getLyrics(artist,title,callback); + } + + private interface LyricsRestService { + @Headers("Cache-Control: public") + @GET("/lyrics") + void getLyrics(@Query("artist") String artist, @Query("title") String title, Callback callback); + } + +} diff --git a/app/src/main/java/com/naman14/timber/utils/NavigationUtils.java b/app/src/main/java/com/naman14/timber/utils/NavigationUtils.java index 05a0cb562..367403da6 100644 --- a/app/src/main/java/com/naman14/timber/utils/NavigationUtils.java +++ b/app/src/main/java/com/naman14/timber/utils/NavigationUtils.java @@ -20,11 +20,10 @@ import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentTransaction; -import android.support.v7.app.AppCompatActivity; -import android.transition.Transition; -import android.transition.TransitionInflater; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentTransaction; +import androidx.appcompat.app.AppCompatActivity; + import android.util.Pair; import android.view.View; import android.widget.Toast; @@ -41,6 +40,8 @@ import com.naman14.timber.nowplaying.Timber2; import com.naman14.timber.nowplaying.Timber3; import com.naman14.timber.nowplaying.Timber4; +import com.naman14.timber.nowplaying.Timber5; +import com.naman14.timber.nowplaying.Timber6; import java.util.ArrayList; @@ -52,16 +53,10 @@ public static void navigateToAlbum(Activity context, long albumID, Pair transitionViews) { final Intent intent = new Intent(context, PlaylistDetailActivity.class); - if (!PreferencesUtility.getInstance(context).getSystemAnimations()) { - intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); - } intent.setAction(action); intent.putExtra(Constants.PLAYLIST_ID, playlistID); intent.putExtra(Constants.PLAYLIST_FOREGROUND_COLOR, foregroundcolor); @@ -150,8 +136,8 @@ public static void navigateToPlaylistDetail(Activity context, String action, lon intent.putExtra(Constants.PLAYLIST_NAME, playlistName); intent.putExtra(Constants.ACTIVITY_TRANSITION, transitionViews != null); - if (transitionViews != null && TimberUtils.isLollipop() && PreferencesUtility.getInstance(context).getAnimations()) { - ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(MainActivity.getInstance(), transitionViews.get(0), transitionViews.get(1), transitionViews.get(2)); + if (transitionViews != null && TimberUtils.isLollipop()) { + ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(context, transitionViews.get(0), transitionViews.get(1), transitionViews.get(2)); context.startActivityForResult(intent, Constants.ACTION_DELETE_PLAYLIST, options.toBundle()); } else { context.startActivityForResult(intent, Constants.ACTION_DELETE_PLAYLIST); @@ -169,9 +155,6 @@ public static void navigateToEqualizer(Activity context) { public static Intent getNavigateToStyleSelectorIntent(Activity context, String what) { final Intent intent = new Intent(context, SettingsActivity.class); - if (!PreferencesUtility.getInstance(context).getSystemAnimations()) { - intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); - } intent.setAction(Constants.SETTINGS_STYLE_SELECTOR); intent.putExtra(Constants.SETTINGS_STYLE_SELECTOR_WHAT, what); return intent; @@ -187,10 +170,34 @@ public static Fragment getFragmentForNowplayingID(String fragmentID) { return new Timber3(); case Constants.TIMBER4: return new Timber4(); + case Constants.TIMBER5: + return new Timber5(); + case Constants.TIMBER6: + return new Timber6(); default: return new Timber1(); } } + public static int getIntForCurrentNowplaying(String nowPlaying) { + switch (nowPlaying) { + case Constants.TIMBER1: + return 0; + case Constants.TIMBER2: + return 1; + case Constants.TIMBER3: + return 2; + case Constants.TIMBER4: + return 3; + case Constants.TIMBER5: + return 4; + case Constants.TIMBER6: + return 5; + default: + return 2; + } + + } + } diff --git a/app/src/main/java/com/naman14/timber/utils/PreferencesUtility.java b/app/src/main/java/com/naman14/timber/utils/PreferencesUtility.java index 519e834c1..adaec2fd9 100644 --- a/app/src/main/java/com/naman14/timber/utils/PreferencesUtility.java +++ b/app/src/main/java/com/naman14/timber/utils/PreferencesUtility.java @@ -15,11 +15,18 @@ package com.naman14.timber.utils; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Bundle; import android.os.Environment; import android.preference.PreferenceManager; +import com.naman14.timber.MusicPlayer; +import com.naman14.timber.MusicService; + public final class PreferencesUtility { public static final String ARTIST_SORT_ORDER = "artist_sort_order"; @@ -46,11 +53,21 @@ public final class PreferencesUtility { public static final String LAST_ADDED_CUTOFF = "last_added_cutoff"; public static final String GESTURES = "gestures"; + public static final String FULL_UNLOCKED = "full_version_unlocked"; + + private static final String SHOW_LOCKSCREEN_ALBUMART = "show_albumart_lockscreen"; + private static final String ARTIST_ALBUM_IMAGE = "artist_album_image"; + private static final String ARTIST_ALBUM_IMAGE_MOBILE = "artist_album_image_mobile"; + private static final String ALWAYS_LOAD_ALBUM_IMAGES_LASTFM = "always_load_album_images_lastfm"; + private static PreferencesUtility sInstance; private static SharedPreferences mPreferences; + private static Context context; + private ConnectivityManager connManager = null; public PreferencesUtility(final Context context) { + this.context = context; mPreferences = PreferenceManager.getDefaultSharedPreferences(context); } @@ -66,14 +83,6 @@ public void setOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener mPreferences.registerOnSharedPreferenceChangeListener(listener); } - public boolean getAnimations() { - return mPreferences.getBoolean(TOGGLE_ANIMATIONS, true); - } - - public boolean getSystemAnimations() { - return mPreferences.getBoolean(TOGGLE_SYSTEM_ANIMATIONS, true); - } - public boolean isArtistsInGrid() { return mPreferences.getBoolean(TOGGLE_ARTIST_GRID, true); } @@ -235,5 +244,43 @@ public void storeLastFolder(String path) { public String getLastFolder() { return mPreferences.getString(LAST_FOLDER, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getPath()); } + + public boolean fullUnlocked() { + return mPreferences.getBoolean(FULL_UNLOCKED, true); + } + + public void setFullUnlocked(final boolean b) { + final SharedPreferences.Editor editor = mPreferences.edit(); + editor.putBoolean(FULL_UNLOCKED, b); + editor.apply(); + } + + public boolean getSetAlbumartLockscreen() { + return mPreferences.getBoolean(SHOW_LOCKSCREEN_ALBUMART, true); + } + + public void updateService(Bundle extras) { + if(!MusicPlayer.isPlaybackServiceConnected())return; + final Intent intent = new Intent(context, MusicService.class); + intent.setAction(MusicService.UPDATE_PREFERENCES); + intent.putExtras(extras); + context.startService(intent); + } + + public boolean loadArtistAndAlbumImages() { + if (mPreferences.getBoolean(ARTIST_ALBUM_IMAGE, true)) { + if (!mPreferences.getBoolean(ARTIST_ALBUM_IMAGE_MOBILE, true)) { + if (connManager == null) connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo ni = connManager.getActiveNetworkInfo(); + return ni != null && ni.getType() == ConnectivityManager.TYPE_WIFI; + } + return true; + } + return false; + } + + public boolean alwaysLoadAlbumImagesFromLastfm() { + return mPreferences.getBoolean(ALWAYS_LOAD_ALBUM_IMAGES_LASTFM, false); + } } diff --git a/app/src/main/java/com/naman14/timber/utils/SlideTrackSwitcher.java b/app/src/main/java/com/naman14/timber/utils/SlideTrackSwitcher.java index e5b618ab8..86ffbd8b2 100644 --- a/app/src/main/java/com/naman14/timber/utils/SlideTrackSwitcher.java +++ b/app/src/main/java/com/naman14/timber/utils/SlideTrackSwitcher.java @@ -1,6 +1,6 @@ package com.naman14.timber.utils; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; diff --git a/app/src/main/java/com/naman14/timber/utils/TimberUtils.java b/app/src/main/java/com/naman14/timber/utils/TimberUtils.java index a62cf9e78..5650db5ee 100644 --- a/app/src/main/java/com/naman14/timber/utils/TimberUtils.java +++ b/app/src/main/java/com/naman14/timber/utils/TimberUtils.java @@ -28,8 +28,8 @@ import android.os.Build; import android.provider.BaseColumns; import android.provider.MediaStore; -import android.support.annotation.NonNull; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.NonNull; + import android.util.Log; import android.util.TypedValue; import android.widget.Toast; @@ -38,16 +38,26 @@ import com.afollestad.materialdialogs.MaterialDialog; import com.naman14.timber.MusicPlayer; import com.naman14.timber.R; +import com.naman14.timber.adapters.BaseQueueAdapter; +import com.naman14.timber.adapters.BaseSongAdapter; import com.naman14.timber.provider.RecentStore; import com.naman14.timber.provider.SongPlayCount; import java.io.File; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.Collections; +import java.util.List; public class TimberUtils { public static final String MUSIC_ONLY_SELECTION = MediaStore.Audio.AudioColumns.IS_MUSIC + "=1" + " AND " + MediaStore.Audio.AudioColumns.TITLE + " != ''"; + public static boolean isOreo() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; + } + public static boolean isMarshmallow() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; } @@ -220,7 +230,7 @@ public static void clearLastAdded(Context context) { .setLastAddedCutoff(System.currentTimeMillis()); } - public static void showDeleteDialog(final Context context, final String name, final long[] list, final RecyclerView.Adapter adapter, final int pos) { + public static void showDeleteDialog(final Context context, final String name, final long[] list, final BaseSongAdapter adapter, final int pos) { new MaterialDialog.Builder(context) .title("Delete song?") @@ -231,7 +241,9 @@ public static void showDeleteDialog(final Context context, final String name, fi @Override public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { TimberUtils.deleteTracks(context, list); + adapter.removeSongAt(pos); adapter.notifyItemRemoved(pos); + adapter.notifyItemRangeChanged(pos, adapter.getItemCount()); } }) .onNegative(new MaterialDialog.SingleButtonCallback() { @@ -241,10 +253,34 @@ public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) } }) .show(); + } + public static void showDeleteDialog(final Context context, final String name, final long[] list, final BaseQueueAdapter qAdapter, final int pos) { - + new MaterialDialog.Builder(context) + .title("Delete song?") + .content("Are you sure you want to delete " + name + " ?") + .positiveText("Delete") + .negativeText("Cancel") + .onPositive(new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + TimberUtils.deleteTracks(context, list); + qAdapter.removeSongAt(pos); + qAdapter.notifyItemRemoved(pos); + qAdapter.notifyItemRangeChanged(pos, qAdapter.getItemCount()); + } + }) + .onNegative(new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + dialog.dismiss(); + } + }) + .show(); } + + public static void deleteTracks(final Context context, final long[] list) { final String[] projection = new String[]{ BaseColumns._ID, MediaStore.MediaColumns.DATA, MediaStore.Audio.AudioColumns.ALBUM_ID @@ -306,5 +342,73 @@ public static void deleteTracks(final Context context, final long[] list) { MusicPlayer.refresh(); } + public static void shareTrack(final Context context, long id) { + + try { + Intent share = new Intent(Intent.ACTION_SEND); + share.setType("audio/*"); + share.putExtra(Intent.EXTRA_STREAM, getSongUri(context, id)); + context.startActivity(Intent.createChooser(share, "Share")); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static Uri getSongUri(Context context, long id) { + final String[] projection = new String[]{ + BaseColumns._ID, MediaStore.MediaColumns.DATA, MediaStore.Audio.AudioColumns.ALBUM_ID + }; + final StringBuilder selection = new StringBuilder(); + selection.append(BaseColumns._ID + " IN ("); + selection.append(id); + selection.append(")"); + final Cursor c = context.getContentResolver().query( + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection.toString(), + null, null); + + if (c == null) { + return null; + } + c.moveToFirst(); + + + try { + + Uri uri = Uri.parse(c.getString(1)); + c.close(); + + return uri; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static String getIPAddress(boolean useIPv4) { + try { + List interfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); + for (NetworkInterface intf : interfaces) { + List addrs = Collections.list(intf.getInetAddresses()); + for (InetAddress addr : addrs) { + if (!addr.isLoopbackAddress()) { + String sAddr = addr.getHostAddress(); + //boolean isIPv4 = InetAddressUtils.isIPv4Address(sAddr); + boolean isIPv4 = sAddr.indexOf(':')<0; + + if (useIPv4) { + if (isIPv4) + return sAddr; + } else { + if (!isIPv4) { + int delim = sAddr.indexOf('%'); // drop ip6 zone suffix + return delim<0 ? sAddr.toUpperCase() : sAddr.substring(0, delim).toUpperCase(); + } + } + } + } + } + } catch (Exception ex) { } + return ""; + } } diff --git a/app/src/main/java/com/naman14/timber/widgets/BaseRecyclerView.java b/app/src/main/java/com/naman14/timber/widgets/BaseRecyclerView.java new file mode 100644 index 000000000..a5576251b --- /dev/null +++ b/app/src/main/java/com/naman14/timber/widgets/BaseRecyclerView.java @@ -0,0 +1,72 @@ +package com.naman14.timber.widgets; + +import android.content.Context; + +import androidx.recyclerview.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; + +import com.afollestad.appthemeengine.Config; +import com.naman14.timber.utils.Helpers; + +import net.steamcrafted.materialiconlib.MaterialDrawableBuilder; + +public class BaseRecyclerView extends RecyclerView { + + private View emptyView; + + private AdapterDataObserver emptyObserver = new AdapterDataObserver() { + + @Override + public void onChanged() { + Adapter adapter = getAdapter(); + if(adapter != null && emptyView != null) { + if(adapter.getItemCount() == 0) { + emptyView.setVisibility(View.VISIBLE); + BaseRecyclerView.this.setVisibility(View.GONE); + } + else { + emptyView.setVisibility(View.GONE); + BaseRecyclerView.this.setVisibility(View.VISIBLE); + } + } + + } + }; + + public BaseRecyclerView(Context context) { + super(context); + } + + public BaseRecyclerView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public BaseRecyclerView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public void setAdapter(Adapter adapter) { + super.setAdapter(adapter); + + if(adapter != null) { + adapter.registerAdapterDataObserver(emptyObserver); + } + + emptyObserver.onChanged(); + } + + public void setEmptyView(Context context, View emptyView, String text) { + this.emptyView = emptyView; + ((TextView) emptyView).setText(text); + + MaterialDrawableBuilder builder = MaterialDrawableBuilder.with(context) + .setIcon(MaterialDrawableBuilder.IconValue.MUSIC_NOTE) + .setColor(Config.textColorPrimary(context, Helpers.getATEKey(context))) + .setSizeDp(30); + + ((TextView) emptyView).setCompoundDrawables(null, builder.build(), null, null); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/naman14/timber/widgets/CircleImageView.java b/app/src/main/java/com/naman14/timber/widgets/CircleImageView.java index 710b44e22..21368e8f6 100644 --- a/app/src/main/java/com/naman14/timber/widgets/CircleImageView.java +++ b/app/src/main/java/com/naman14/timber/widgets/CircleImageView.java @@ -15,15 +15,16 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.support.annotation.ColorRes; -import android.support.annotation.DrawableRes; -import android.support.v4.content.ContextCompat; + +import androidx.annotation.ColorRes; +import androidx.annotation.DrawableRes; +import androidx.appcompat.widget.AppCompatImageView; +import androidx.core.content.ContextCompat; import android.util.AttributeSet; -import android.widget.ImageView; import com.naman14.timber.R; -public class CircleImageView extends ImageView { +public class CircleImageView extends AppCompatImageView { private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP; diff --git a/app/src/main/java/com/naman14/timber/widgets/DividerItemDecoration.java b/app/src/main/java/com/naman14/timber/widgets/DividerItemDecoration.java index 3f9856aac..80323eebb 100644 --- a/app/src/main/java/com/naman14/timber/widgets/DividerItemDecoration.java +++ b/app/src/main/java/com/naman14/timber/widgets/DividerItemDecoration.java @@ -5,9 +5,9 @@ import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.support.v4.content.ContextCompat; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.view.View; import com.naman14.timber.R; diff --git a/app/src/main/java/com/naman14/timber/widgets/DragSortRecycler.java b/app/src/main/java/com/naman14/timber/widgets/DragSortRecycler.java index a851e1fa8..0db007d48 100644 --- a/app/src/main/java/com/naman14/timber/widgets/DragSortRecycler.java +++ b/app/src/main/java/com/naman14/timber/widgets/DragSortRecycler.java @@ -5,8 +5,8 @@ import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; -import android.support.annotation.Nullable; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; import android.util.Log; import android.view.MotionEvent; import android.view.View; diff --git a/app/src/main/java/com/naman14/timber/widgets/FastScroller.java b/app/src/main/java/com/naman14/timber/widgets/FastScroller.java index 7bced1119..cc8e98e20 100644 --- a/app/src/main/java/com/naman14/timber/widgets/FastScroller.java +++ b/app/src/main/java/com/naman14/timber/widgets/FastScroller.java @@ -5,9 +5,9 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; -import android.support.annotation.NonNull; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -17,7 +17,7 @@ import com.naman14.timber.R; -import static android.support.v7.widget.RecyclerView.OnScrollListener; +import static androidx.recyclerview.widget.RecyclerView.OnScrollListener; public class FastScroller extends LinearLayout { private static final int BUBBLE_ANIMATION_DURATION = 100; diff --git a/app/src/main/java/com/naman14/timber/widgets/MultiViewPager.java b/app/src/main/java/com/naman14/timber/widgets/MultiViewPager.java index a7db8d211..e1a75befc 100644 --- a/app/src/main/java/com/naman14/timber/widgets/MultiViewPager.java +++ b/app/src/main/java/com/naman14/timber/widgets/MultiViewPager.java @@ -3,7 +3,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Point; -import android.support.v4.view.ViewPager; +import androidx.viewpager.widget.ViewPager; import android.util.AttributeSet; import android.view.View; diff --git a/app/src/main/java/com/naman14/timber/widgets/MusicVisualizer.java b/app/src/main/java/com/naman14/timber/widgets/MusicVisualizer.java index 4ff455721..168bdc95e 100644 --- a/app/src/main/java/com/naman14/timber/widgets/MusicVisualizer.java +++ b/app/src/main/java/com/naman14/timber/widgets/MusicVisualizer.java @@ -21,8 +21,8 @@ public class MusicVisualizer extends View { @Override public void run() { - //run every 150 ms - postDelayed(this, 150); + //run every 100 ms + postDelayed(this, 120); invalidate(); } @@ -48,9 +48,9 @@ protected void onDraw(Canvas canvas) { //set paint style, Style.FILL will fill the color, Style.STROKE will stroke the color paint.setStyle(Paint.Style.FILL); - canvas.drawRect(getDimensionInPixel(0), getHeight() - (20 + random.nextInt((int) (getHeight() / 1.5f) - 19)), getDimensionInPixel(7), getHeight(), paint); - canvas.drawRect(getDimensionInPixel(10), getHeight() - (20 + random.nextInt((int) (getHeight() / 1.5f) - 19)), getDimensionInPixel(17), getHeight(), paint); - canvas.drawRect(getDimensionInPixel(20), getHeight() - (20 + random.nextInt((int) (getHeight() / 1.5f) - 19)), getDimensionInPixel(27), getHeight(), paint); + canvas.drawRect(getDimensionInPixel(0), getHeight() - (40 + random.nextInt((int) (getHeight() / 1.5f) - 25)), getDimensionInPixel(7), getHeight() - 15, paint); + canvas.drawRect(getDimensionInPixel(10), getHeight() - (40 + random.nextInt((int) (getHeight() / 1.5f) - 25)), getDimensionInPixel(17), getHeight() -15, paint); + canvas.drawRect(getDimensionInPixel(20), getHeight() - (40 + random.nextInt((int) (getHeight() / 1.5f) - 25)), getDimensionInPixel(27), getHeight() -15, paint); } public void setColor(int color) { diff --git a/app/src/main/java/com/naman14/timber/widgets/PlayPauseButton.java b/app/src/main/java/com/naman14/timber/widgets/PlayPauseButton.java index 13043df32..a6796965a 100644 --- a/app/src/main/java/com/naman14/timber/widgets/PlayPauseButton.java +++ b/app/src/main/java/com/naman14/timber/widgets/PlayPauseButton.java @@ -9,7 +9,7 @@ import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.util.AttributeSet; import android.view.View; diff --git a/app/src/main/java/com/naman14/timber/widgets/PlayPauseDrawable.java b/app/src/main/java/com/naman14/timber/widgets/PlayPauseDrawable.java index 56ff26bc1..a4b293772 100644 --- a/app/src/main/java/com/naman14/timber/widgets/PlayPauseDrawable.java +++ b/app/src/main/java/com/naman14/timber/widgets/PlayPauseDrawable.java @@ -39,7 +39,7 @@ import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.drawable.Drawable; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.util.Log; import android.util.Property; import android.view.animation.DecelerateInterpolator; diff --git a/app/src/main/java/com/naman14/timber/widgets/PopupImageView.java b/app/src/main/java/com/naman14/timber/widgets/PopupImageView.java index 48fa7daee..d1ccf4197 100644 --- a/app/src/main/java/com/naman14/timber/widgets/PopupImageView.java +++ b/app/src/main/java/com/naman14/timber/widgets/PopupImageView.java @@ -1,18 +1,18 @@ package com.naman14.timber.widgets; -import android.annotation.TargetApi; import android.content.Context; import android.graphics.Color; import android.preference.PreferenceManager; import android.util.AttributeSet; -import android.widget.ImageView; + +import androidx.appcompat.widget.AppCompatImageView; import com.afollestad.appthemeengine.util.TintHelper; /** * Created by naman on 29/10/16. */ -public class PopupImageView extends ImageView { +public class PopupImageView extends AppCompatImageView { public PopupImageView(Context context) { super(context); @@ -29,12 +29,6 @@ public PopupImageView(Context context, AttributeSet attrs, int defStyleAttr) { tint(); } - @TargetApi(21) - public PopupImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - tint(); - } - private void tint() { if (PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean("dark_theme", false)) { TintHelper.setTint(this, Color.parseColor("#eeeeee")); diff --git a/app/src/main/java/com/naman14/timber/widgets/SquareImageView.java b/app/src/main/java/com/naman14/timber/widgets/SquareImageView.java index 901d0f314..1d42f6d5f 100644 --- a/app/src/main/java/com/naman14/timber/widgets/SquareImageView.java +++ b/app/src/main/java/com/naman14/timber/widgets/SquareImageView.java @@ -2,9 +2,10 @@ import android.content.Context; import android.util.AttributeSet; -import android.widget.ImageView; -public class SquareImageView extends ImageView { +import androidx.appcompat.widget.AppCompatImageView; + +public class SquareImageView extends AppCompatImageView { public SquareImageView(Context context) { diff --git a/app/src/main/java/com/naman14/timber/widgets/desktop/BaseWidget.java b/app/src/main/java/com/naman14/timber/widgets/desktop/BaseWidget.java index 6325c3d0d..6bb5569dd 100644 --- a/app/src/main/java/com/naman14/timber/widgets/desktop/BaseWidget.java +++ b/app/src/main/java/com/naman14/timber/widgets/desktop/BaseWidget.java @@ -5,7 +5,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.support.annotation.LayoutRes; +import android.os.Bundle; +import androidx.annotation.LayoutRes; import android.widget.RemoteViews; import com.naman14.timber.MusicService; @@ -22,10 +23,14 @@ public abstract class BaseWidget extends AppWidgetProvider { @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + onUpdate(context, appWidgetManager, appWidgetIds, null); + } + + private void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,Bundle extras){ ComponentName serviceName = new ComponentName(context, MusicService.class); RemoteViews remoteViews = new RemoteViews(context.getPackageName(), getLayoutRes()); try { - onViewsUpdate(context, remoteViews, serviceName); + onViewsUpdate(context, remoteViews, serviceName, extras); appWidgetManager.updateAppWidget(appWidgetIds, remoteViews); } catch (Exception e) { e.printStackTrace(); @@ -39,13 +44,13 @@ public void onReceive(Context context, Intent intent) { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); ComponentName thisAppWidget = new ComponentName(context.getPackageName(), this.getClass().getName()); int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget); - onUpdate(context, appWidgetManager, appWidgetIds); + onUpdate(context, appWidgetManager, appWidgetIds, intent.getExtras()); } else { super.onReceive(context, intent); } } - abstract void onViewsUpdate(Context context, RemoteViews remoteViews, ComponentName serviceName); + abstract void onViewsUpdate(Context context, RemoteViews remoteViews, ComponentName serviceName, Bundle extras); abstract @LayoutRes int getLayoutRes(); } diff --git a/app/src/main/java/com/naman14/timber/widgets/desktop/SmallWidget.java b/app/src/main/java/com/naman14/timber/widgets/desktop/SmallWidget.java index 8f1d6a686..d7433aaa2 100644 --- a/app/src/main/java/com/naman14/timber/widgets/desktop/SmallWidget.java +++ b/app/src/main/java/com/naman14/timber/widgets/desktop/SmallWidget.java @@ -5,9 +5,9 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; +import android.os.Bundle; import android.widget.RemoteViews; -import com.naman14.timber.MusicPlayer; import com.naman14.timber.MusicService; import com.naman14.timber.R; import com.naman14.timber.utils.NavigationUtils; @@ -26,7 +26,7 @@ int getLayoutRes() { } @Override - void onViewsUpdate(Context context, RemoteViews remoteViews, ComponentName serviceName) { + void onViewsUpdate(Context context, RemoteViews remoteViews, ComponentName serviceName, Bundle extras) { remoteViews.setOnClickPendingIntent(R.id.image_next, PendingIntent.getService( context, REQUEST_NEXT, @@ -43,24 +43,26 @@ void onViewsUpdate(Context context, RemoteViews remoteViews, ComponentName servi .setComponent(serviceName), 0 )); - String t = MusicPlayer.getTrackName(); - if (t != null) { - remoteViews.setTextViewText(R.id.textView_title, t); - } - t = MusicPlayer.getArtistName(); - if (t != null) { - remoteViews.setTextViewText(R.id.textView_subtitle, t); - } - remoteViews.setImageViewResource(R.id.image_playpause, - MusicPlayer.isPlaying() ? R.drawable.ic_pause_white_36dp : R.drawable.ic_play_white_36dp); - long albumId = MusicPlayer.getCurrentAlbumId(); - if (albumId != -1) { - Bitmap artwork; - artwork = ImageLoader.getInstance().loadImageSync(TimberUtils.getAlbumArtUri(albumId).toString()); - if (artwork == null) { - artwork = ImageLoader.getInstance().loadImageSync("drawable://" + R.drawable.ic_empty_music2); + if (extras != null) { + String t = extras.getString("track"); + if (t != null) { + remoteViews.setTextViewText(R.id.textView_title, t); + } + t = extras.getString("artist"); + if (t != null) { + remoteViews.setTextViewText(R.id.textView_subtitle, t); + } + remoteViews.setImageViewResource(R.id.image_playpause, + extras.getBoolean("playing") ? R.drawable.ic_pause_white_36dp : R.drawable.ic_play_white_36dp); + long albumId = extras.getLong("albumid"); + if (albumId != -1) { + Bitmap artwork = ImageLoader.getInstance().loadImageSync(TimberUtils.getAlbumArtUri(albumId).toString()); + if (artwork != null) { + remoteViews.setImageViewBitmap(R.id.imageView_cover, artwork); + } else { + remoteViews.setImageViewResource(R.id.imageView_cover, R.drawable.ic_empty_music2); + } } - remoteViews.setImageViewBitmap(R.id.imageView_cover, artwork); } remoteViews.setOnClickPendingIntent(R.id.textView_title, PendingIntent.getActivity( context, diff --git a/app/src/main/java/com/naman14/timber/widgets/desktop/StandardWidget.java b/app/src/main/java/com/naman14/timber/widgets/desktop/StandardWidget.java index 35ced01b1..c4197c169 100644 --- a/app/src/main/java/com/naman14/timber/widgets/desktop/StandardWidget.java +++ b/app/src/main/java/com/naman14/timber/widgets/desktop/StandardWidget.java @@ -5,10 +5,10 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; +import android.os.Bundle; import android.text.TextUtils; import android.widget.RemoteViews; -import com.naman14.timber.MusicPlayer; import com.naman14.timber.MusicService; import com.naman14.timber.R; import com.naman14.timber.utils.NavigationUtils; @@ -27,7 +27,7 @@ int getLayoutRes() { } @Override - void onViewsUpdate(Context context, RemoteViews remoteViews, ComponentName serviceName) { + void onViewsUpdate(Context context, RemoteViews remoteViews, ComponentName serviceName, Bundle extras) { remoteViews.setOnClickPendingIntent(R.id.image_next, PendingIntent.getService( context, REQUEST_NEXT, @@ -52,28 +52,33 @@ void onViewsUpdate(Context context, RemoteViews remoteViews, ComponentName servi .setComponent(serviceName), 0 )); - String t = MusicPlayer.getTrackName(); - if (t != null) { - remoteViews.setTextViewText(R.id.textView_title, t); - } - t = MusicPlayer.getArtistName(); - if (t != null) { - String album = MusicPlayer.getAlbumName(); - if (!TextUtils.isEmpty(album)) { - t += " - " + album; + + if (extras != null) { + String t = extras.getString("track"); + if (t != null) { + remoteViews.setTextViewText(R.id.textView_title, t); } - remoteViews.setTextViewText(R.id.textView_subtitle, t); - } - remoteViews.setImageViewResource(R.id.image_playpause, - MusicPlayer.isPlaying() ? R.drawable.ic_pause_white_36dp : R.drawable.ic_play_white_36dp); - long albumId = MusicPlayer.getCurrentAlbumId(); - if (albumId != -1) { - Bitmap artwork; - artwork = ImageLoader.getInstance().loadImageSync(TimberUtils.getAlbumArtUri(albumId).toString()); - if (artwork == null) { - artwork = ImageLoader.getInstance().loadImageSync("drawable://" + R.drawable.ic_empty_music2); + t = extras.getString("artist"); + ; + if (t != null) { + String album = extras.getString("album"); + ; + if (!TextUtils.isEmpty(album)) { + t += " - " + album; + } + remoteViews.setTextViewText(R.id.textView_subtitle, t); + } + remoteViews.setImageViewResource(R.id.image_playpause, + extras.getBoolean("playing") ? R.drawable.ic_pause_white_36dp : R.drawable.ic_play_white_36dp); + long albumId = extras.getLong("albumid"); + if (albumId != -1) { + Bitmap artwork = ImageLoader.getInstance().loadImageSync(TimberUtils.getAlbumArtUri(albumId).toString()); + if (artwork != null) { + remoteViews.setImageViewBitmap(R.id.imageView_cover, artwork); + } else { + remoteViews.setImageViewResource(R.id.imageView_cover, R.drawable.ic_empty_music2); + } } - remoteViews.setImageViewBitmap(R.id.imageView_cover, artwork); } remoteViews.setOnClickPendingIntent(R.id.imageView_cover, PendingIntent.getActivity( context, diff --git a/app/src/main/res/anim/design_fab_out.xml b/app/src/main/res/anim/design_fab_out.xml new file mode 100644 index 000000000..ba76fb597 --- /dev/null +++ b/app/src/main/res/anim/design_fab_out.xml @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable-hdpi/ic_lock_outline_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_lock_outline_white_24dp.png new file mode 100644 index 000000000..a6b80507b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_lock_outline_white_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_lock_outline_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_lock_outline_white_24dp.png new file mode 100644 index 000000000..5700e9b3c Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_lock_outline_white_24dp.png differ diff --git a/app/src/main/res/drawable-nodpi/timber_5_nowplaying_x.png b/app/src/main/res/drawable-nodpi/timber_5_nowplaying_x.png new file mode 100755 index 000000000..6a4a03559 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/timber_5_nowplaying_x.png differ diff --git a/app/src/main/res/drawable-nodpi/timber_6_nowplaying_x.png b/app/src/main/res/drawable-nodpi/timber_6_nowplaying_x.png new file mode 100755 index 000000000..b593773f9 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/timber_6_nowplaying_x.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_lock_outline_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_lock_outline_white_24dp.png new file mode 100644 index 000000000..f60060132 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_lock_outline_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_lock_outline_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_lock_outline_white_24dp.png new file mode 100644 index 000000000..409703a60 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_lock_outline_white_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_lock_outline_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_lock_outline_white_24dp.png new file mode 100644 index 000000000..53dbe2361 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_lock_outline_white_24dp.png differ diff --git a/app/src/main/res/layout/activity_donate.xml b/app/src/main/res/layout/activity_donate.xml index 2a408f004..dc40f91da 100644 --- a/app/src/main/res/layout/activity_donate.xml +++ b/app/src/main/res/layout/activity_donate.xml @@ -8,7 +8,7 @@ android:layout_height="match_parent" android:orientation="vertical"> - + android:text="Show your appreciation and support the development of Timber. Purchasing any of the below product also unlocks additional now playing styles."/> - - - \ 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 14a75bb56..8be76c470 100644 --- a/app/src/main/res/layout/activity_playlist_detail.xml +++ b/app/src/main/res/layout/activity_playlist_detail.xml @@ -23,7 +23,7 @@ android:layout_height="match_parent" android:orientation="vertical"> - - - - - - diff --git a/app/src/main/res/layout/empty_view.xml b/app/src/main/res/layout/empty_view.xml new file mode 100644 index 000000000..64b95f6ab --- /dev/null +++ b/app/src/main/res/layout/empty_view.xml @@ -0,0 +1,12 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_album_detail.xml b/app/src/main/res/layout/fragment_album_detail.xml index 0273825d0..51f67a578 100644 --- a/app/src/main/res/layout/fragment_album_detail.xml +++ b/app/src/main/res/layout/fragment_album_detail.xml @@ -1,18 +1,18 @@ - - - @@ -86,7 +87,7 @@ - - - + + - - - + diff --git a/app/src/main/res/layout/fragment_artist_bio.xml b/app/src/main/res/layout/fragment_artist_bio.xml index 61e047959..7aed7a9bf 100644 --- a/app/src/main/res/layout/fragment_artist_bio.xml +++ b/app/src/main/res/layout/fragment_artist_bio.xml @@ -1,5 +1,5 @@ - - @@ -22,7 +22,7 @@ android:id="@+id/artist_bio" android:textSize="40sp"/> - + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_artist_detail.xml b/app/src/main/res/layout/fragment_artist_detail.xml index 42596772f..bf7d53270 100644 --- a/app/src/main/res/layout/fragment_artist_detail.xml +++ b/app/src/main/res/layout/fragment_artist_detail.xml @@ -1,18 +1,18 @@ - - - - - + - + - + - + diff --git a/app/src/main/res/layout/fragment_artist_music.xml b/app/src/main/res/layout/fragment_artist_music.xml index b8a4d0d04..2fe8fc52a 100644 --- a/app/src/main/res/layout/fragment_artist_music.xml +++ b/app/src/main/res/layout/fragment_artist_music.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_folders.xml b/app/src/main/res/layout/fragment_folders.xml index b9dccb6de..64818271a 100644 --- a/app/src/main/res/layout/fragment_folders.xml +++ b/app/src/main/res/layout/fragment_folders.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index 54e818f3c..376f5bd5e 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -1,34 +1,34 @@ - - - - - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_playlist.xml b/app/src/main/res/layout/fragment_playlist.xml index 38f317659..effe2d9a8 100644 --- a/app/src/main/res/layout/fragment_playlist.xml +++ b/app/src/main/res/layout/fragment_playlist.xml @@ -11,7 +11,7 @@ android:layout_height="match_parent" android:orientation="vertical"> - - + diff --git a/app/src/main/res/layout/fragment_queue.xml b/app/src/main/res/layout/fragment_queue.xml index 8b3b019c2..aef2c65b1 100644 --- a/app/src/main/res/layout/fragment_queue.xml +++ b/app/src/main/res/layout/fragment_queue.xml @@ -1,24 +1,28 @@ - - - - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_recyclerview.xml b/app/src/main/res/layout/fragment_recyclerview.xml index d82da0624..ca19ae5d6 100644 --- a/app/src/main/res/layout/fragment_recyclerview.xml +++ b/app/src/main/res/layout/fragment_recyclerview.xml @@ -6,7 +6,7 @@ android:layout_height="match_parent" android:layout_marginBottom="55dp"> - + + + diff --git a/app/src/main/res/layout/fragment_timber1.xml b/app/src/main/res/layout/fragment_timber1.xml index 36a5073de..5631c28f0 100644 --- a/app/src/main/res/layout/fragment_timber1.xml +++ b/app/src/main/res/layout/fragment_timber1.xml @@ -1,11 +1,11 @@ - - - - - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_timber2.xml b/app/src/main/res/layout/fragment_timber2.xml index b07a9dd93..c11ca606b 100644 --- a/app/src/main/res/layout/fragment_timber2.xml +++ b/app/src/main/res/layout/fragment_timber2.xml @@ -1,11 +1,11 @@ - - + android:textSize="21sp" + android:visibility="gone"/> + android:textSize="13sp" + android:visibility="gone"/> - - + - - - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_timber3.xml b/app/src/main/res/layout/fragment_timber3.xml index b90c6b1bf..f58b61334 100644 --- a/app/src/main/res/layout/fragment_timber3.xml +++ b/app/src/main/res/layout/fragment_timber3.xml @@ -1,11 +1,11 @@ - - - + android:textSize="16sp" /> - - + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_timber4.xml b/app/src/main/res/layout/fragment_timber4.xml index 0b7c27936..b1b75a533 100644 --- a/app/src/main/res/layout/fragment_timber4.xml +++ b/app/src/main/res/layout/fragment_timber4.xml @@ -1,5 +1,5 @@ - @@ -15,7 +15,7 @@ android:layout_height="match_parent" android:background="#77000000" /> - + android:textSize="23sp" + android:visibility="gone"/> + android:textSize="13sp" + android:visibility="gone"/> - - + - - - - + - + diff --git a/app/src/main/res/layout/fragment_timber5.xml b/app/src/main/res/layout/fragment_timber5.xml new file mode 100644 index 000000000..adfb0804a --- /dev/null +++ b/app/src/main/res/layout/fragment_timber5.xml @@ -0,0 +1,248 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_timber6.xml b/app/src/main/res/layout/fragment_timber6.xml new file mode 100644 index 000000000..ab83c80dc --- /dev/null +++ b/app/src/main/res/layout/fragment_timber6.xml @@ -0,0 +1,294 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/include_list_viewpager.xml b/app/src/main/res/layout/include_list_viewpager.xml index 0787cbc8a..a60074536 100644 --- a/app/src/main/res/layout/include_list_viewpager.xml +++ b/app/src/main/res/layout/include_list_viewpager.xml @@ -1,29 +1,35 @@ - - + android:layout_width="match_parent" + android:id="@+id/content_root" + xmlns:app="http://schemas.android.com/apk/res-auto"> - + android:layout_height="match_parent" + android:gravity="bottom" + sothree:fadeColor="@android:color/transparent" + sothree:panelHeight="55dp" + sothree:shadowHeight="5dp"> - + + + + + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/item_artist_album.xml b/app/src/main/res/layout/item_artist_album.xml index 9728b0ad8..ec0529fb1 100644 --- a/app/src/main/res/layout/item_artist_album.xml +++ b/app/src/main/res/layout/item_artist_album.xml @@ -1,4 +1,4 @@ - - + android:src="@drawable/ic_empty_music2" + android:transitionName="transition_album_art" /> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/item_donate_product.xml b/app/src/main/res/layout/item_donate_product.xml index 209ee5609..8ccf3f31c 100644 --- a/app/src/main/res/layout/item_donate_product.xml +++ b/app/src/main/res/layout/item_donate_product.xml @@ -17,7 +17,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btn_donate" - android:text="Donate" + android:text="Purchase" android:layout_alignParentRight="true" android:layout_marginRight="15dp" /> diff --git a/app/src/main/res/layout/item_song.xml b/app/src/main/res/layout/item_song.xml index 46c980098..7ca186819 100644 --- a/app/src/main/res/layout/item_song.xml +++ b/app/src/main/res/layout/item_song.xml @@ -18,7 +18,8 @@ android:layout_width="55dp" android:layout_height="55dp" android:layout_alignParentLeft="true" - android:padding="1dp" /> + android:padding="1dp" + android:src="@drawable/ic_empty_music2"/> + android:layout_width="55dp" + android:layout_height="55dp" + android:scaleType="centerCrop" + android:layout_margin="10dp"/> - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/album_detail.xml b/app/src/main/res/menu/album_detail.xml new file mode 100644 index 000000000..3cfb69860 --- /dev/null +++ b/app/src/main/res/menu/album_detail.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/album_song_sort_by.xml b/app/src/main/res/menu/album_song_sort_by.xml index 0149dbef6..d765f4c3c 100644 --- a/app/src/main/res/menu/album_song_sort_by.xml +++ b/app/src/main/res/menu/album_song_sort_by.xml @@ -1,9 +1,10 @@ - + diff --git a/app/src/main/res/menu/album_sort_by.xml b/app/src/main/res/menu/album_sort_by.xml index 1adcf5e15..af2f0ddc3 100644 --- a/app/src/main/res/menu/album_sort_by.xml +++ b/app/src/main/res/menu/album_sort_by.xml @@ -1,9 +1,10 @@ - + diff --git a/app/src/main/res/menu/artist_detail.xml b/app/src/main/res/menu/artist_detail.xml new file mode 100644 index 000000000..3eb82847f --- /dev/null +++ b/app/src/main/res/menu/artist_detail.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/artist_sort_by.xml b/app/src/main/res/menu/artist_sort_by.xml index bc1e0f3a9..27207b59c 100644 --- a/app/src/main/res/menu/artist_sort_by.xml +++ b/app/src/main/res/menu/artist_sort_by.xml @@ -1,11 +1,12 @@ - + + android:id="@+id/menu_sort_by" + android:orderInCategory="50" + android:title="@string/menu_sort_by" + app:showAsAction="never"> - diff --git a/app/src/main/res/menu/menu_cast.xml b/app/src/main/res/menu/menu_cast.xml new file mode 100644 index 000000000..9de23f7b8 --- /dev/null +++ b/app/src/main/res/menu/menu_cast.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_expanded_controller.xml b/app/src/main/res/menu/menu_expanded_controller.xml new file mode 100644 index 000000000..6dd1591ce --- /dev/null +++ b/app/src/main/res/menu/menu_expanded_controller.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_folders.xml b/app/src/main/res/menu/menu_folders.xml new file mode 100644 index 000000000..cea31e313 --- /dev/null +++ b/app/src/main/res/menu/menu_folders.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_playlist.xml b/app/src/main/res/menu/menu_playlist.xml index 80ceec69a..db74ca983 100644 --- a/app/src/main/res/menu/menu_playlist.xml +++ b/app/src/main/res/menu/menu_playlist.xml @@ -15,10 +15,10 @@ app:showAsAction="never" /> + android:id="@+id/menu_show_as" + android:orderInCategory="40" + android:title="@string/menu_show_as" + app:showAsAction="never"> diff --git a/app/src/main/res/menu/menu_show_as.xml b/app/src/main/res/menu/menu_show_as.xml index 614fb6cdd..cbaafb0a2 100644 --- a/app/src/main/res/menu/menu_show_as.xml +++ b/app/src/main/res/menu/menu_show_as.xml @@ -1,9 +1,10 @@ - + diff --git a/app/src/main/res/menu/now_playing.xml b/app/src/main/res/menu/now_playing.xml new file mode 100644 index 000000000..d06031aea --- /dev/null +++ b/app/src/main/res/menu/now_playing.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/popup_playing_queue.xml b/app/src/main/res/menu/popup_playing_queue.xml index fce2206b3..9ceea26c9 100644 --- a/app/src/main/res/menu/popup_playing_queue.xml +++ b/app/src/main/res/menu/popup_playing_queue.xml @@ -4,6 +4,10 @@ + + + + android:title="@string/delete_from_device" /> \ No newline at end of file diff --git a/app/src/main/res/menu/song_sort_by.xml b/app/src/main/res/menu/song_sort_by.xml index 2f35dc059..2479c1a2f 100644 --- a/app/src/main/res/menu/song_sort_by.xml +++ b/app/src/main/res/menu/song_sort_by.xml @@ -1,9 +1,10 @@ - + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e6c677a12..b14382ec6 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -79,6 +79,13 @@ Timber ist ein offener Musik-Player entwickelt von Naman Dwivedi. Wiedergabe pausieren wenn Kopfhörer entfernt werden. Pause beim entfernen + Sperrbildschirm + Albumcover im Sperrbildschirm anzeigen. + Interpretenbilder + Lade Interpretenbilder aus dem Internet. + Mobiledaten + Interpretenbilder auch ohne Wifi laden. + %d Song %d Songs @@ -87,5 +94,14 @@ %d Interpret %d Interpreten + Bei LastFM einloggen um zuscrobblen + Fehler beim einloggen + Bei LastFM einloggen + Eingeloggt als %s + + Abbrechen + + Songtext anzeigen + Songtext nicht gefunden. diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 5748711d4..144387281 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -110,7 +110,6 @@ - Timber %1$s | %2$s %1$s %2$s %2$d:%3$02d diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index 28abb02ff..c2c744863 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -1,7 +1,6 @@ - Timber Pengaturan Main diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 8996633e6..a747d5948 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -54,7 +54,7 @@ 저장소 재생 목록 재생 중인 대기열 - 재생 주 + 재생 중 설정 소개 도움말과 피드백 @@ -99,6 +99,13 @@ 색깔 있는 내비게이션바 내비게이션바 색깔을 유지합니다 + 제스처 + 제스처로 트랙을 변경합니다 + Timber 표준 위젯 + Timber 작은 위젯 + 탭 하여 Timber를 시작하기 + Timber 밝은 위젯 + %d 음악 %d 음악 %d 앨범 @@ -111,4 +118,16 @@ %d 음악이 재생 목록에 추가됐습니다. %d 음악이 재생 목록에 추가됐습니다. + 재생 + 재생 목록에 추가 + 앨범으로 이동 + 음악가로 이동 + 대기열에 추가 + 다음곡 + 새로운 재생 목록 + + + 검색 결과가 없습니다 + 폴더 + diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml new file mode 100644 index 000000000..8e3e14169 --- /dev/null +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -0,0 +1,134 @@ + + + + Innstillinger + + Spill + Pause + Neste + Forrige + Veksle mellom sanger + Veksle mellom alt + Gjenta + Gjenta alle + Gjenta én + Åpne avspillingskø + Utforsk musikk + + Sortering + A-Å + Å-A + Artist + Album + År + Spornummer + Varighet + Dato lagt til + Sporliste + Antall spor + Antall album + Filnavn + + Feil ved avspilling av spor + + Sist lagt til + Nylig spilt + Mine mest spilte spor + + Innrapporter eventuelle feil her + Angi funksjonsforespørsler i Google+brukerfellesskapet her + + Vis som + Liste + Rutenett + Forvalg + + Lys + Mørk + Svart + + Sist åpnet + + Spor + Album + Artister + Bibliotek + Spillelister + Avspillingskø + Spilles nå + Innstillinger + Om + Hjelp og tilbakemeldinger + Søk + Veksle mellom alle + Tonekontroll + Søk i bibliotek + + Velg drakt + Velg en drakt til bruk i programmet. + Velg en startside til visning ved oppstart. + Startside + Artister i rutenett + Veksle for visning av artister i 2x2-rutenett. + Veksle for systemanimasjoner i hele programmet. + System-animasjoner + Veksle for overgangsanimasjon i hele programmet. + Animasjoner + Velg en "Nå spilles"-skjerm blandt fire forskjellige stilarter. + Brukertilpasning + Annet + Avansert + Timber er en fri programvare-musikkavspiller, utviklet av Naman Dwivedi. + Veksle for pausing av musikkavspilling når hodetelefoner kobles fra + Pause ved frakobling + Krever Xposed og XMediaNotificationTrackSelector Xposed-modul for å kunne installeres. Dette gir tilgang til avspillingskø fra merknadsmenyen. + Avspillingskø i merknadsfelt + Valgt + + Mørk drakt + Ikle programmet en mørk drakt + + Hovedfarge + Endre draktens hovedfarge + + Sekundærfarge + Endre draktens sekundærfarge + + Farget statusfelt + Veksle fargelegging av statusfelt + + Farget navigationsfelt + Veksle fargelegging av navigasjonsfelt + + Strøk + Bytt spor ved å stryke + Timber forvalgt miniprogram + Timber lite miniprogram + Trykk for å starte Timber + Timber lyst miniprogram + + %d sang + %d sanger + %d album + %d album + %d artist + %d artister + + %d sang lagt til i avspillingskø. + %d songer lagt til i avspillingskø. + %d sang lagt til i spilleliste. + %d sanger lagt til i spilleliste. + + Spill + Legg til i spilleliste + Gå til album + Gå til artist + Legg til i avspillingskø + Spill neste + Ny spilleliste + + + Ingenting funnet + Mapper + + diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 6f6dc5571..1c8092209 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -10,8 +10,8 @@ Baralhar Baralhar tudo Repetição - Repetir uma - Repetir todas + Repetir todas + Repetir uma Abrir fila Explorar músicas @@ -107,5 +107,7 @@ %d faixas adicionadas à fila. %d faixa adicionada à lista de reprodução. %d faixas adicionadas à lista de reprodução. + Não foi selecionado musíca para tocar. + Carregando... diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index a71df3406..edd89af05 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -110,7 +110,6 @@ - Timber %1$s | %2$s %1$s %2$s %2$d:%3$02d diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 15a3a82c1..64f8886c6 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -1,6 +1,7 @@ + Timber 设置 播放 @@ -12,35 +13,36 @@ 循环 全部循环 单曲循环 - 开启队列 + 打开队列 浏览音乐 排序 - A 到 Z - Z 到 A + A-Z + Z-A 艺术家 专辑 年份 - 曲目编号 + 曲目数 时长 - 添加时间 + 日期已添加 曲目列表 - 歌曲数量 - 专辑数量 + 歌曲数 + 专辑数 文件名 - 播放曲目错误 + 曲目播放错误 最新添加 最近播放 - 我的最佳单曲 + 我的最佳曲目 - 报告任何 bug,在 这里 - 提出任何特性请求通过 Google+ 社区,在 这里 + 这里 报告漏洞 + 这里 通过 Google+ 提出特性请求 视图 列表 网格 + 默认 默认主题 似水年华 @@ -57,52 +59,94 @@ 正在播放 设置 关于 - 帮助与反馈 + 帮助和反馈 搜索 - 随机 - 均衡 + 全部随机 + 均衡器 在库中搜索 选择主题 - 选择一个主题,在整个应用中。 - 选择起始页,在启动时显示。 + 为整个应用选择一个主题 + 选择启动时显示的页面 起始页 - 艺术家网格显示 - 切换艺术家显示,在 2×2 的网格中。 - 切换系统动画,在整个应用中。 + 网格显示艺术家 + 在 2x2 网格中显示艺术家 + 切换整个应用的系统动画 系统动画 - 切换过渡动画,在整个应用中。 + 切换整个应用的过渡动画 动画 - 选择正在播放画面,从4个不同风格中。 + 从4中不同的播放界面风格中选择一个 个性化 其他 - Timber 是一个开源音乐播放器,由 Naman Dwivedi 开发。 - 切换为暂停播放,当耳机被拔出时。 - 拔出暂停 + 高级 + Timber 是 Naman Dwivedi 开发的一款开源的音乐播放器 + 当耳机拔出时暂停播放 + 拔出时暂停 + 需要安装 Xposed 和 Xposed 模块 XMediaNotificationTrackSelector。这将在通知中提供播放队列控制。 + 通知中的播放队列支持 当前选择 - 夜间模式 应用夜间模式到整个应用 基础色 - 更改主题基础色 + 更改基础主题色 强调色 + 更改强调主题色 + + 状态栏着色 + 切换状态栏的着色状态 + + 导航栏着色 + 切换导航栏的着色状态 - 更改主题强调色 + 锁屏 + 在锁屏界面显示专辑封面 - 彩色导航栏 - 切换导航栏色彩 + 加载艺术家图像 + 从 Last.fm 加载艺术家图像 - 彩色状态栏 - 切换状态栏色彩 + 从设备数据加载艺术家图像 + 无 Wi-Fi 时也能加载艺术家图像 - %d 首歌 - %d 首歌 + 手势 + 使用手势切换曲目 + Timber 标准 + Timber 小 + 单击打开 Timber + Timber 亮 + + %d 首歌曲 + %d 首歌曲 %d 张专辑 %d 张专辑 %d 位艺术家 %d 位艺术家 + 已将 %d 首歌曲添加到队列 + 已将 %d 首歌曲添加到队列 + 已将 %d 首歌曲添加到播放列表 + 已将 %d 首歌曲添加到播放列表 + + 播放 + 添加到播放列表 + 转到专辑 + 转到艺术家 + 添加到队列 + 下一首播放 + 新的播放列表 + + + 没有结果 + 文件夹 + 选择存储器 + + 登录 Last.fm 以上传偏好数据 + 登录失败 + 登录 Last.fm + 已登录为 %s + + 取消 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e19924a7b..198cd0a8e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,7 @@ + Timber Settings Play @@ -100,6 +101,15 @@ Colored Navigation Bar Toggle navigation bar coloring + Lockscreen + Show album art in lockscreen + + Load artist/album images + Load artist/album images from lastfm + + Load artist/album images on mobile data + Load artist/album images even if there is no wifi + Gestures Switching tracks using gestures Timber standard widget @@ -121,7 +131,6 @@ - Timber %1$s | %2$s %1$s %2$s %2$d:%3$02d @@ -134,9 +143,35 @@ Add to queue Play next New playlist - + Share + Delete from device + Remove from queue Nothing found Folders - + Select storage + + Login to LastFM to scrobble + Failed to Login + Login to LastFM + Logged in as %s + + Cancel + Always load album images from Last.fm + If unchecked, album art will be fetched locally from song file if available + + Show lyrics + No lyrics found. + Powered by makeitpersonal.co + Cast + \"No current track selected. + Loading... + + Timber is a fully featured open source music player.

+ Designed and developed by Naman Dwivedi. Check out my + Website and GitHub +

Also checkout TimberX, + material themed music player that works across all form factors. + ]]>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 5487ea9b8..3d263f5f9 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -107,6 +107,9 @@ 29sp + + @@ -134,7 +137,4 @@ @color/window_background_black - - - diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 318f5bf84..989a36e25 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -13,34 +13,6 @@ - - - - - - - - - - - - - - - - - - + + + + + + @@ -130,5 +130,19 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/widget_small.xml b/app/src/main/res/xml/widget_small.xml index a1d1a98bd..77e595fe7 100644 --- a/app/src/main/res/xml/widget_small.xml +++ b/app/src/main/res/xml/widget_small.xml @@ -4,4 +4,5 @@ android:minHeight="40dp" android:updatePeriodMillis="86400000" android:previewImage="@drawable/widget_small" + android:resizeMode="horizontal|vertical" android:initialLayout="@layout/widget_small" /> \ No newline at end of file diff --git a/app/src/main/res/xml/widget_standard.xml b/app/src/main/res/xml/widget_standard.xml index 37b7ac025..7e0484df0 100644 --- a/app/src/main/res/xml/widget_standard.xml +++ b/app/src/main/res/xml/widget_standard.xml @@ -4,4 +4,5 @@ android:minHeight="40dp" android:updatePeriodMillis="86400000" android:previewImage="@drawable/widget_standard" + android:resizeMode="horizontal|vertical" android:initialLayout="@layout/widget_standard" /> \ No newline at end of file diff --git a/app/src/main/res/xml/widget_white.xml b/app/src/main/res/xml/widget_white.xml index a1a60a95f..344845230 100644 --- a/app/src/main/res/xml/widget_white.xml +++ b/app/src/main/res/xml/widget_white.xml @@ -4,4 +4,5 @@ android:minHeight="40dp" android:updatePeriodMillis="86400000" android:previewImage="@drawable/widget_white" + android:resizeMode="horizontal|vertical" android:initialLayout="@layout/widget_white" /> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 57d469176..c7a0720c9 100644 --- a/build.gradle +++ b/build.gradle @@ -2,18 +2,35 @@ buildscript { repositories { + mavenCentral() jcenter() + google() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files + classpath 'com.android.tools.build:gradle:4.2.2' + } } allprojects { repositories { + mavenCentral() jcenter() + google() maven { url "https://jitpack.io" } } } + +ext { + // Sdk and tools + minSdkVersion = 16 + + targetSdkVersion = 31 + compileSdkVersion = 31 + + // App dependencies + supportLibraryVersion = '27.0.2' + retrofitVersion = '2.3.0' + okHttp3Version = '3.9.0' +} + diff --git a/gradle.properties b/gradle.properties index 1d3591c8a..915f0e66f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,4 +15,6 @@ # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true \ No newline at end of file +# org.gradle.parallel=true +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9851e5157..c0e0018b3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Mon May 16 20:34:02 CEST 2022 distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip +zipStoreBase=GRADLE_USER_HOME diff --git a/graphics/Screenshot_2015-08-16-20-38-46.png b/graphics/Screenshot_2015-08-16-20-38-46.png deleted file mode 100644 index 89528d64e..000000000 Binary files a/graphics/Screenshot_2015-08-16-20-38-46.png and /dev/null differ diff --git a/graphics/Screenshot_2015-08-29-22-44-26.png b/graphics/Screenshot_2015-08-29-22-44-26.png deleted file mode 100644 index 290850e7c..000000000 Binary files a/graphics/Screenshot_2015-08-29-22-44-26.png and /dev/null differ diff --git a/graphics/Screenshot_2015-08-31-11-50-50.png b/graphics/Screenshot_2015-08-31-11-50-50.png deleted file mode 100644 index beb95cf55..000000000 Binary files a/graphics/Screenshot_2015-08-31-11-50-50.png and /dev/null differ diff --git a/graphics/Screenshot_2015-08-31-11-52-50.png b/graphics/Screenshot_2015-08-31-11-52-50.png deleted file mode 100644 index b178f2efa..000000000 Binary files a/graphics/Screenshot_2015-08-31-11-52-50.png and /dev/null differ diff --git a/graphics/Screenshot_2015-09-18-12-33-27.png b/graphics/Screenshot_2015-09-18-12-33-27.png deleted file mode 100644 index bd51c4583..000000000 Binary files a/graphics/Screenshot_2015-09-18-12-33-27.png and /dev/null differ diff --git a/graphics/screen1.png b/graphics/screen1.png new file mode 100755 index 000000000..309a36a89 Binary files /dev/null and b/graphics/screen1.png differ diff --git a/graphics/screen10.jpg b/graphics/screen10.jpg new file mode 100644 index 000000000..ca09e575d Binary files /dev/null and b/graphics/screen10.jpg differ diff --git a/graphics/screen11.jpg b/graphics/screen11.jpg new file mode 100644 index 000000000..e9e3550f1 Binary files /dev/null and b/graphics/screen11.jpg differ diff --git a/graphics/screen12.jpg b/graphics/screen12.jpg new file mode 100644 index 000000000..5954f47a8 Binary files /dev/null and b/graphics/screen12.jpg differ diff --git a/graphics/screen2.png b/graphics/screen2.png new file mode 100755 index 000000000..fe7c94660 Binary files /dev/null and b/graphics/screen2.png differ diff --git a/graphics/screen3.png b/graphics/screen3.png new file mode 100755 index 000000000..6525c5936 Binary files /dev/null and b/graphics/screen3.png differ diff --git a/graphics/screen4.png b/graphics/screen4.png new file mode 100755 index 000000000..a07f8667c Binary files /dev/null and b/graphics/screen4.png differ diff --git a/graphics/screen5.png b/graphics/screen5.png new file mode 100755 index 000000000..72625fc7f Binary files /dev/null and b/graphics/screen5.png differ diff --git a/graphics/screen6.png b/graphics/screen6.png new file mode 100755 index 000000000..5bc6c537a Binary files /dev/null and b/graphics/screen6.png differ diff --git a/graphics/Screenshot_2015-08-05-14-23-03.png b/graphics/screen7.png similarity index 100% rename from graphics/Screenshot_2015-08-05-14-23-03.png rename to graphics/screen7.png diff --git a/graphics/screen8.png b/graphics/screen8.png new file mode 100644 index 000000000..2418a3af0 Binary files /dev/null and b/graphics/screen8.png differ diff --git a/graphics/screen9.jpg b/graphics/screen9.jpg new file mode 100644 index 000000000..675cccd5b Binary files /dev/null and b/graphics/screen9.jpg differ diff --git a/mock.gradle b/mock.gradle new file mode 100644 index 000000000..8699deb5a --- /dev/null +++ b/mock.gradle @@ -0,0 +1,20 @@ +// This script must be applied in app/build.gradle for the paths here to work correctly + +def copyMockFilesNeeded() { + def srcGoogleServicesFile = file("../mock/mock-google-services.json") + def srcCastSecretFile = file("../mock/mock-secret.xml") + + def destGoogleServicesFile = file("google-services.json") + def destCastSecretFile = file("src/main/res/values/secret.xml") + + if (!destGoogleServicesFile.exists()) { + destGoogleServicesFile.write(srcGoogleServicesFile.text) + } + if (!destCastSecretFile.exists()) { + destCastSecretFile.write(srcCastSecretFile.text) + } +} + +afterEvaluate { + copyMockFilesNeeded() +} \ No newline at end of file diff --git a/mock/mock-google-services.json b/mock/mock-google-services.json new file mode 100644 index 000000000..225aa9c98 --- /dev/null +++ b/mock/mock-google-services.json @@ -0,0 +1,42 @@ +{ + "project_info": { + "project_number": "123456789000", + "firebase_url": "https://mockproject-1234.firebaseio.com", + "project_id": "mockproject-1234", + "storage_bucket": "mockproject-1234.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:123456789000:android:f1bf012572b04063", + "android_client_info": { + "package_name": "naman14.timber" + } + }, + "oauth_client": [ + { + "client_id": "123456789000-hjugbg6ud799v4c49dim8ce2usclthar.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzbSzCn1N6LWIe6wthYyrgUUSAlUsdqMb-wvTo" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 1, + "other_platform_oauth_client": [] + }, + "ads_service": { + "status": 2 + } + } + } + ], + "configuration_version": "1" +} diff --git a/mock/mock-secret.xml b/mock/mock-secret.xml new file mode 100644 index 000000000..36915d077 --- /dev/null +++ b/mock/mock-secret.xml @@ -0,0 +1,12 @@ + + + + + 123456 + 123456 +