├── .codebeatignore
├── .gitignore
├── .idea
└── gradle.xml
├── .travis.yml
├── CODEOWNERS
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── schemas
│ └── com.koalatea.thehollidayinn.softwareengineeringdaily.data.AppDatabase
│ │ └── 1.json
└── src
│ ├── androidTest
│ ├── java
│ │ └── com
│ │ │ └── koalatea
│ │ │ └── thehollidayinn
│ │ │ └── softwareengineeringdaily
│ │ │ └── ExampleInstrumentedTest.java
│ └── kotlin
│ │ └── com
│ │ └── koalatea
│ │ └── thehollidayinn
│ │ └── softwareengineeringdaily
│ │ └── ExampleInstrumentedKTest.kt
│ ├── dev
│ └── google-services.json
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ └── Roboto-Regular.ttf
│ ├── ic_launcher-web.png
│ ├── java
│ │ └── com
│ │ │ └── koalatea
│ │ │ └── thehollidayinn
│ │ │ └── softwareengineeringdaily
│ │ │ ├── MainActivity.java
│ │ │ ├── MainNavAdapter.java
│ │ │ ├── PlaybackControllerActivity.java
│ │ │ ├── analytics
│ │ │ ├── AnalyticsFacade.java
│ │ │ ├── AnalyticsFacadeImpl.java
│ │ │ └── AnalyticsModule.java
│ │ │ ├── app
│ │ │ ├── AppComponent.java
│ │ │ ├── AppModule.java
│ │ │ ├── AppScope.java
│ │ │ └── SEDApp.java
│ │ │ ├── audio
│ │ │ ├── LocalPlayback.java
│ │ │ ├── MediaIDHelper.java
│ │ │ ├── MediaNotificationManager.java
│ │ │ ├── MusicProvider.java
│ │ │ ├── MusicService.java
│ │ │ ├── Playback.java
│ │ │ ├── PlaybackManager.java
│ │ │ ├── QueueHelper.java
│ │ │ └── QueueManager.java
│ │ │ ├── auth
│ │ │ └── LoginRegisterActivity.java
│ │ │ ├── dagger
│ │ │ └── NetworkModule.java
│ │ │ ├── data
│ │ │ ├── AppDatabase.java
│ │ │ ├── models
│ │ │ │ ├── Bookmark.java
│ │ │ │ ├── Content.java
│ │ │ │ ├── Download.java
│ │ │ │ ├── Post.java
│ │ │ │ ├── PostItem.java
│ │ │ │ ├── SubscriptionResponse.java
│ │ │ │ ├── Title.java
│ │ │ │ ├── User.java
│ │ │ │ └── UserResponse.java
│ │ │ ├── remote
│ │ │ │ └── APIInterface.java
│ │ │ └── repositories
│ │ │ │ ├── BookmarkDao.java
│ │ │ │ ├── DownloadDao.java
│ │ │ │ └── DownloadRepository.java
│ │ │ ├── downloads
│ │ │ └── MP3FileManager.java
│ │ │ ├── latest
│ │ │ ├── RecentPodcastFragment.java
│ │ │ └── RecentPodcastsPageAdapter.java
│ │ │ ├── mediaui
│ │ │ ├── FullscreenPlayerFragment.java
│ │ │ └── SpeedDialog.java
│ │ │ ├── notifications
│ │ │ ├── DailyAlarmReceiver.kt
│ │ │ └── NotificationActivity.kt
│ │ │ ├── playbar
│ │ │ ├── PlaybarFragment.java
│ │ │ └── PlaybarViewModel.java
│ │ │ ├── podcast
│ │ │ ├── PodListFragment.java
│ │ │ ├── PodcastAdapter.java
│ │ │ ├── PodcastDetailActivity.java
│ │ │ ├── PodcastListViewModel.java
│ │ │ ├── PodcastSessionStateManager.java
│ │ │ └── TopRecListFragment.java
│ │ │ ├── repositories
│ │ │ ├── DownloadNotificationManager.java
│ │ │ ├── FilterRepository.java
│ │ │ ├── PodcastDownloadsRepository.java
│ │ │ ├── PostRepository.java
│ │ │ ├── RepoConstants.kt
│ │ │ ├── RepositoryModule.java
│ │ │ └── UserRepository.java
│ │ │ ├── subscription
│ │ │ ├── PaymentFragment.kt
│ │ │ ├── PlanFragment.kt
│ │ │ ├── PlanInfoFragment.kt
│ │ │ └── SubscriptionActivity.kt
│ │ │ └── util
│ │ │ ├── AlertUtil.java
│ │ │ └── ReactiveUtil.java
│ └── res
│ │ ├── drawable-hdpi
│ │ ├── ic_bookmark.png
│ │ ├── ic_bookmark_set.png
│ │ ├── ic_launcher_app.png
│ │ ├── ic_pause_black_36dp.png
│ │ ├── ic_play_arrow_black_36dp.png
│ │ ├── ic_tab_mic.png
│ │ ├── ic_tab_recommendation.png
│ │ └── ic_tab_top.png
│ │ ├── drawable-mdpi
│ │ ├── ic_bookmark.png
│ │ ├── ic_launcher_app.png
│ │ ├── ic_pause_black_36dp.png
│ │ ├── ic_play_arrow_black_36dp.png
│ │ ├── ic_tab_mic.png
│ │ ├── ic_tab_recommendation.png
│ │ └── ic_tab_top.png
│ │ ├── drawable-xhdpi
│ │ ├── ic_bookmark.png
│ │ ├── ic_bookmark_set.png
│ │ ├── ic_launcher_app.png
│ │ ├── ic_pause_black_36dp.png
│ │ ├── ic_play_arrow_black_36dp.png
│ │ ├── ic_tab_mic.png
│ │ ├── ic_tab_recommendation.png
│ │ └── ic_tab_top.png
│ │ ├── drawable-xxhdpi
│ │ ├── ic_bookmark.png
│ │ ├── ic_bookmark_set.png
│ │ ├── ic_launcher_app.png
│ │ ├── ic_pause_black_36dp.png
│ │ ├── ic_play_arrow_black_36dp.png
│ │ ├── ic_tab_mic.png
│ │ ├── ic_tab_recommendation.png
│ │ └── ic_tab_top.png
│ │ ├── drawable-xxxhdpi
│ │ ├── ic_bookmark.png
│ │ ├── ic_bookmark_set.png
│ │ ├── ic_launcher_app.png
│ │ ├── ic_pause_black_36dp.png
│ │ ├── ic_play_arrow_black_36dp.png
│ │ ├── ic_tab_mic.png
│ │ ├── ic_tab_recommendation.png
│ │ └── ic_tab_top.png
│ │ ├── drawable
│ │ ├── gradient.xml
│ │ ├── ic_down_arrow.xml
│ │ ├── ic_up_arrow.xml
│ │ ├── sedaily_logo.png
│ │ └── textlines.xml
│ │ ├── layout
│ │ ├── activity_login_register.xml
│ │ ├── activity_main.xml
│ │ ├── activity_notification.xml
│ │ ├── activity_podcast_detail.xml
│ │ ├── activity_subscription.xml
│ │ ├── content_login_register.xml
│ │ ├── content_subscription.xml
│ │ ├── fragment_payment.xml
│ │ ├── fragment_plan.xml
│ │ ├── fragment_plan_info.xml
│ │ ├── fragment_playback_controls.xml
│ │ ├── fragment_podcast_grid.xml
│ │ ├── fragment_podcast_horizontal.xml
│ │ ├── fragment_podcast_list.xml
│ │ ├── fragment_recent_podcast.xml
│ │ ├── fragment_toprec_list.xml
│ │ ├── item_skeleton_news.xml
│ │ └── login_screen.xml
│ │ ├── menu
│ │ ├── bottom_navigation_main.xml
│ │ ├── menu_main.xml
│ │ └── podcast_detail_menu.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── transition
│ │ ├── change_image_transform.xml
│ │ └── explode.xml
│ │ ├── values
│ │ ├── arrays.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings-localize.xml
│ │ ├── strings-login.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ │ └── xml
│ │ ├── backup_descriptor.xml
│ │ └── searchable.xml
│ └── test
│ ├── java
│ └── com
│ │ └── koalatea
│ │ └── thehollidayinn
│ │ └── softwareengineeringdaily
│ │ ├── ExampleUnitTest.java
│ │ └── ExampleUnitTest2.kt
│ ├── kotlin
│ └── com
│ │ └── koalatea
│ │ └── thehollidayinn
│ │ └── softwareengineeringdaily
│ │ └── data
│ │ └── ExampleUnitTest.kt
│ └── resources
│ └── api
│ ├── 200_auth_response.json
│ ├── 200_post_response.json
│ ├── 200_post_response_logged_in.json
│ ├── 201_registration_success_response.json
│ ├── 400_registration_missing_password_response.json
│ ├── 401_login_wrong_password_response.json
│ ├── 401_registration_user_exists_response.json
│ └── 404_login_user_not_found_response.json
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── issue_template.md
└── settings.gradle
/.codebeatignore:
--------------------------------------------------------------------------------
1 | app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/data/models/*
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | app/app-release.apk
2 | app/google-services.json
3 |
4 | ### Android ###
5 |
6 | # Generated files
7 | bin/
8 | gen/
9 | out/
10 | app/release/
11 |
12 | # Gradle files
13 | .gradle/
14 | build/
15 |
16 | # Local configuration file (sdk path, etc)
17 | local.properties
18 |
19 | # Proguard folder generated by Eclipse
20 | proguard/
21 |
22 | # Log Files
23 | *.log
24 |
25 | # Android Studio Navigation editor temp files
26 | .navigation/
27 |
28 | # Android Studio captures folder
29 | captures/
30 |
31 | # Intellij
32 | *.iml
33 | .idea/
34 | # External native build folder generated in Android Studio 2.2 and later
35 | .externalNativeBuild
36 |
37 | # Freeline
38 | freeline.py
39 | freeline/
40 | freeline_project_description.json
41 |
42 | ### Android Patch ###
43 | gen-external-apklibs
44 |
45 | # Signing files
46 | .signing/
47 |
48 | # Local configuration file (sdk path, etc)
49 |
50 | # Proguard folder generated by Eclipse
51 |
52 | # Log Files
53 |
54 | # Android Studio
55 | /*/local.properties
56 | /*/out
57 | /*/*/build
58 | /*/*/production
59 | *.ipr
60 | *~
61 | *.swp
62 |
63 | # IntelliJ IDEA
64 | *.iws
65 | /out/
66 |
67 | # OS-specific files
68 | .DS_Store
69 | .DS_Store?
70 | ._*
71 | .Spotlight-V100
72 | .Trashes
73 | ehthumbs.db
74 | Thumbs.db
75 |
76 | # Legacy Eclipse project files
77 | .classpath
78 | .project
79 |
80 | # Mobile Tools for Java (J2ME)
81 | .mtj.tmp/
82 |
83 | # Package Files #
84 | *.war
85 | *.ear
86 |
87 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
88 | hs_err_pid*
89 |
90 | ## Plugin-specific files:
91 |
92 | # mpeltonen/sbt-idea plugin
93 | .idea_modules/
94 |
95 | # JIRA plugin
96 | atlassian-ide-plugin.xml
97 |
98 | # Mongo Explorer plugin
99 |
100 | # Crashlytics plugin (for Android Studio and IntelliJ)
101 | com_crashlytics_export_strings.xml
102 | crashlytics.properties
103 | crashlytics-build.properties
104 | fabric.properties
105 |
106 | ### AndroidStudio Patch ###
107 |
108 | !/gradle/wrapper/gradle-wrapper.jar
109 |
110 | ### Java ###
111 | # Compiled class file
112 |
113 | # Log file
114 |
115 | # BlueJ files
116 | *.ctxt
117 |
118 | # Mobile Tools for Java (J2ME)
119 |
120 | # Package Files #
121 | *.jar
122 | *.zip
123 | *.tar.gz
124 | *.rar
125 |
126 | ### Linux ###
127 |
128 | # temporary files which can be created if a process still has a handle open of a deleted file
129 | .fuse_hidden*
130 |
131 | # KDE directory preferences
132 | .directory
133 |
134 | # Linux trash folder which might appear on any partition or disk
135 | .Trash-*
136 |
137 | # .nfs files are created when an open file is removed but is still being accessed
138 | .nfs*
139 |
140 | ### macOS ###
141 | *.DS_Store
142 | .AppleDouble
143 | .LSOverride
144 |
145 | # Icon must end with two \r
146 | Icon
147 |
148 | # Files that might appear in the root of a volume
149 | .DocumentRevisions-V100
150 | .fseventsd
151 | .TemporaryItems
152 | .VolumeIcon.icns
153 | .com.apple.timemachine.donotpresent
154 |
155 | # Directories potentially created on remote AFP share
156 | .AppleDB
157 | .AppleDesktop
158 | Network Trash Folder
159 | Temporary Items
160 | .apdisk
161 |
162 | ### Windows ###
163 | # Windows thumbnail cache files
164 | ehthumbs_vista.db
165 |
166 | # Folder config file
167 | Desktop.ini
168 |
169 | # Recycle Bin used on file shares
170 | $RECYCLE.BIN/
171 |
172 | # Windows Installer files
173 | *.cab
174 | *.msi
175 | *.msm
176 | *.msp
177 |
178 | # Windows shortcuts
179 | *.lnk
180 | app/src/main/res/raw/third_party_license_metadata
181 | app/src/main/res/raw/third_party_licenses
182 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 | language: android
3 | jdk: oraclejdk8
4 |
5 | android:
6 | components:
7 | - tools
8 | - platform-tools
9 | - tools # Second invocation to get the latest SDK tools
10 | # The BuildTools version used by your project
11 | - build-tools-$ANDROID_BUILD_TOOLS
12 | # The SDK version used to compile your project
13 | - android-$API
14 |
15 | # Additional components
16 | - extra-google-android-support
17 | - extra-google-google_play_services
18 | - extra-google-m2repository
19 | - extra-android-m2repository
20 | - addon-google_apis-google-$API
21 |
22 | # Specify at least one system image,
23 | # if you need to run emulator(s) during your tests
24 | - sys-img-armeabi-v7a-android-$API
25 |
26 | licenses:
27 | - 'android-sdk-license-.+'
28 | - 'android-sdk-preview-license-.+'
29 | - 'google-gdk-license-.+'
30 |
31 | # Avoid uploading the cache after every build
32 | before_cache:
33 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
34 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
35 | cache:
36 | directories:
37 | - $HOME/.gradle/caches/
38 | - $HOME/.gradle/wrapper/
39 | - $HOME/.android/build-cache
40 |
41 | env:
42 | global:
43 | - API=27 # Android API level default
44 | - ABI=armeabi-v7a # ARM ABI v7a by default
45 | - ANDROID_BUILD_TOOLS=27.0.3
46 | before_install:
47 | # Update sdk tools to latest version and install/update components
48 | - yes | sdkmanager "platforms;android-$API"
49 | # Ensure Gradle wrapper is executable, download wrapper and show version
50 | - chmod +x ./gradlew; ls -l gradlew; ./gradlew wrapper -v
51 | install:
52 | # Check/list components status
53 | - sdkmanager --list || true
54 |
55 | script:
56 | - ./gradlew clean build -Pbuild=dev jacocoTestReport
57 | - ./gradlew test -Pbuild=dev
58 | # script: ./gradlew connectedAndroidTest -Pbuild=dev
59 |
60 | after_success:
61 | - bash <(curl -s https://codecov.io/bash)
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # This is a comment.
2 | # Each line is a file pattern followed by one or more owners.
3 |
4 | # These owners will be the default owners for everything in
5 | # the repo. Unless a later match takes precedence,
6 | # @global-owner1 and @global-owner2 will be requested for
7 | # review when someone opens a pull request.
8 | * @thehollidayinn
9 |
10 | # Order is important; the last matching pattern takes the most
11 | # precedence. When someone opens a pull request that only
12 | # modifies JS files, only @js-owner and not the global
13 | # owner(s) will be requested for a review.
14 | * @kvithayathil
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016-2017 Kunal Kapadia
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | [](https://softwareengineeringdaily.com/)
3 |
4 | [](https://travis-ci.org/SoftwareEngineeringDaily/SEDaily-Android)
5 | [](https://codecov.io/gh/SoftwareEngineeringDaily/SEDaily-Android)
6 | [](https://codebeat.co/projects/github-com-softwareengineeringdaily-sedaily-android-develop)
7 | [](https://www.codetriage.com/softwareengineeringdaily/sedaily-android)
8 |
9 | # SEDaily-Android
10 |
11 | A player for the Software Engineering Daily Podcast
12 |
13 | ## Getting Started
14 |
15 | ### Cloning the Project
16 | ```sh
17 | $ git clone https://github.com/SoftwareEngineeringDaily/SEDaily-Android.git
18 | $ cd SEDaily-Android
19 | ```
20 |
21 | ### Setting up Firebase
22 | 1. Go to the [Firebase website](https://firebase.google.com/) and create an account if you do not already have one
23 | 2. Create a new project on the Firebase dashboard
24 | 3. Add a new Android app in the Firebase Dashboard.
25 | 4. Set the package name to `com.koalatea.thehollidayinn.softwareengineeringdaily.debug`
26 | 5. Download `google-services.json` and place it in `/app/`
27 |
28 |
29 | Compile the project with Gradle using
30 | ```sh
31 | ./gradlew build
32 | ```
33 |
34 |
35 | ## Built With
36 |
37 | * [Android](https://www.android.com/)
38 | * [Gradle](https://gradle.org/)
39 | * [Firebase](https://firebase.google.com/)
40 | * [Material UI Chip Input](https://github.com/TeamWertarbyte/material-ui-chip-input)
41 | * [ReactiveX](http://reactivex.io/)
42 | * [Leak Canary](https://github.com/square/leakcanary)
43 | * [OkHttp](https://github.com/square/okhttp)
44 | * [Retrofit](https://github.com/square/retrofit)
45 |
46 | ## Contributing
47 |
48 | ## License
49 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\Users\krh12\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/schemas/com.koalatea.thehollidayinn.softwareengineeringdaily.data.AppDatabase/1.json:
--------------------------------------------------------------------------------
1 | {
2 | "formatVersion": 1,
3 | "database": {
4 | "version": 1,
5 | "identityHash": "659a50ac2fb84b17beb61aba662cb055",
6 | "entities": [
7 | {
8 | "tableName": "bookmark",
9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`postId` TEXT NOT NULL, `active` INTEGER, PRIMARY KEY(`postId`))",
10 | "fields": [
11 | {
12 | "fieldPath": "postId",
13 | "columnName": "postId",
14 | "affinity": "TEXT",
15 | "notNull": true
16 | },
17 | {
18 | "fieldPath": "active",
19 | "columnName": "active",
20 | "affinity": "INTEGER",
21 | "notNull": false
22 | }
23 | ],
24 | "primaryKey": {
25 | "columnNames": [
26 | "postId"
27 | ],
28 | "autoGenerate": false
29 | },
30 | "indices": [],
31 | "foreignKeys": []
32 | }
33 | ],
34 | "setupQueries": [
35 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
36 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"659a50ac2fb84b17beb61aba662cb055\")"
37 | ]
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/koalatea/thehollidayinn/softwareengineeringdaily/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.koalatea.thehollidayinn.softwareengineeringdaily.debug", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/androidTest/kotlin/com/koalatea/thehollidayinn/softwareengineeringdaily/ExampleInstrumentedKTest.kt:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily
2 |
3 | import android.support.test.InstrumentationRegistry
4 | import android.support.test.runner.AndroidJUnit4
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Created by Kurian on 05-Nov-17.
11 | * Instrumentation test, which will execute on an Android device.
12 | *
13 | * @see [Testing documentation](http://d.android.com/tools/testing)
14 | */
15 | @RunWith(AndroidJUnit4::class)
16 | class ExampleInstrumentedKTest {
17 |
18 | @Test
19 | @Throws(Exception::class)
20 | fun useAppContext() {
21 | // Context of the app under test.
22 | val appContext = InstrumentationRegistry.getTargetContext()
23 | assertEquals("com.koalatea.thehollidayinn.softwareengineeringdaily.debug",
24 | appContext.packageName)
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/dev/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "",
4 | "project_id": "dev",
5 | "firebase_url": "https://www.example.com",
6 | "storage_bucket": "sed-android.com"
7 | },
8 | "client": [
9 | {
10 | "client_info": {
11 | "mobilesdk_app_id": "1:000000000000:android:aaaaaaaaaaaaaaaa",
12 | "android_client_info": {
13 | "package_name": "com.koalatea.thehollidayinn.softwareengineeringdaily.dev"
14 | }
15 | },
16 | "oauth_client": [
17 | {
18 | "client_id": "",
19 | "client_type": 3
20 | }
21 | ],
22 | "api_key": [
23 | {
24 | "current_key": ""
25 | }
26 | ],
27 | "services": {
28 | "analytics_service": {
29 | "status": 1
30 | },
31 | "appinvite_service": {
32 | "status": 1,
33 | "other_platform_oauth_client": []
34 | },
35 | "ads_service": {
36 | "status": 2
37 | }
38 | }
39 | },
40 | {
41 | "client_info": {
42 | "mobilesdk_app_id": "1:000000000000:android:ffffffffffffffff",
43 | "android_client_info": {
44 | "package_name": "com.koalatea.thehollidayinn.softwareengineeringdaily.dev"
45 | }
46 | },
47 | "oauth_client": [
48 | {
49 | "client_id": "",
50 | "client_type": 1,
51 | "android_info": {
52 | "package_name": "com.koalatea.thehollidayinn.softwareengineeringdaily.dev",
53 | "certificate_hash": ""
54 | }
55 | },
56 | {
57 | "client_id": "",
58 | "client_type": 3
59 | }
60 | ],
61 | "api_key": [
62 | {
63 | "current_key": ""
64 | }
65 | ],
66 | "services": {
67 | "analytics_service": {
68 | "status": 1
69 | },
70 | "appinvite_service": {
71 | "status": 2,
72 | "other_platform_oauth_client": [
73 | {
74 | "client_id": "",
75 | "client_type": 3
76 | }
77 | ]
78 | },
79 | "ads_service": {
80 | "status": 2
81 | }
82 | }
83 | }
84 | ],
85 | "configuration_version": "1"
86 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
37 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
51 |
52 |
53 |
54 |
55 |
56 |
61 |
64 |
65 |
69 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/app/src/main/assets/Roboto-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoftwareEngineeringDaily/SEDaily-Android/3c845c0ebddd60fd1a060e5244d6a9351cf175f4/app/src/main/assets/Roboto-Regular.ttf
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SoftwareEngineeringDaily/SEDaily-Android/3c845c0ebddd60fd1a060e5244d6a9351cf175f4/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/MainNavAdapter.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily;
2 |
3 | import android.support.v4.app.Fragment;
4 | import android.support.v4.app.FragmentManager;
5 | import android.support.v4.app.FragmentPagerAdapter;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /*
11 | Created by krh12 on 5/22/2017.
12 | */
13 |
14 | class MainNavAdapter extends FragmentPagerAdapter {
15 | private final List mFragmentList = new ArrayList<>();
16 | private final List mFragmentTitleList = new ArrayList<>();
17 |
18 | public MainNavAdapter(FragmentManager manager) {
19 | super(manager);
20 | }
21 |
22 | @Override
23 | public Fragment getItem(int position) {
24 | return mFragmentList.get(position);
25 | }
26 |
27 | @Override
28 | public int getCount() {
29 | return mFragmentList.size();
30 | }
31 |
32 | public void addFragment(Fragment fragment, String title) {
33 | mFragmentList.add(fragment);
34 | mFragmentTitleList.add(title);
35 | }
36 |
37 | @Override
38 | public CharSequence getPageTitle(int position) {
39 | return mFragmentTitleList.get(position);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/analytics/AnalyticsFacade.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.analytics;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | /**
6 | * Facade to capture events for use in any analytics processing
7 | * Created by Kurian on 20-Oct-17.
8 | */
9 | public interface AnalyticsFacade {
10 |
11 | /**
12 | * Track an up-vote event
13 | * @param postId Id of the post that's been up-voted
14 | */
15 | void trackUpVote(@NonNull String postId);
16 |
17 | /**
18 | * Track an down-vote event
19 | * @param postId Id of the post that's been down-voted
20 | */
21 | void trackDownVote(@NonNull String postId);
22 |
23 | /**
24 | * Track a user registration event
25 | * @param username the username that has been registered
26 | */
27 | void trackRegistration(@NonNull String username);
28 |
29 | /**
30 | * Track a user login event
31 | * @param username the username that has been logged in
32 | */
33 | void trackLogin(@NonNull String username);
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/analytics/AnalyticsFacadeImpl.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.analytics;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.NonNull;
5 | import com.google.firebase.analytics.FirebaseAnalytics;
6 |
7 | /**
8 | * Created by Kurian on 20-Oct-17.
9 | */
10 | class AnalyticsFacadeImpl implements AnalyticsFacade {
11 |
12 | private final FirebaseAnalytics firebaseAnalytics;
13 |
14 | AnalyticsFacadeImpl(@NonNull FirebaseAnalytics firebaseAnalytics) {
15 | this.firebaseAnalytics = firebaseAnalytics;
16 | }
17 |
18 | private void trackFirebaseBasic(String itemId, String name, String contentType) {
19 | Bundle bundle = new Bundle();
20 | bundle.putString(FirebaseAnalytics.Param.ITEM_ID, itemId);
21 | bundle.putString(FirebaseAnalytics.Param.ITEM_NAME, name);
22 | bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, contentType);
23 | firebaseAnalytics.logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, bundle);
24 | }
25 |
26 | @Override
27 | public void trackUpVote(@NonNull String postId) {
28 | trackFirebaseBasic(postId, "UP", "VOTE");
29 | }
30 |
31 | @Override
32 | public void trackDownVote(@NonNull String postId) {
33 | trackFirebaseBasic(postId, "DOWN", "VOTE");
34 | }
35 |
36 | @Override
37 | public void trackRegistration(@NonNull String username) {
38 | trackFirebaseBasic(username, "Register", "Register");
39 | }
40 |
41 | @Override
42 | public void trackLogin(@NonNull String username) {
43 | trackFirebaseBasic(username, "Login", "Login");
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/analytics/AnalyticsModule.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.analytics;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import com.google.firebase.analytics.FirebaseAnalytics;
6 |
7 | import javax.inject.Singleton;
8 |
9 | import dagger.Module;
10 | import dagger.Provides;
11 |
12 | /**
13 | * Created by Kurian on 20-Oct-17.
14 | */
15 | @Module
16 | public class AnalyticsModule {
17 |
18 | @Provides
19 | @Singleton
20 | FirebaseAnalytics providesFirebaseAnalytics(@NonNull Context context) {
21 | return FirebaseAnalytics.getInstance(context);
22 | }
23 |
24 | @Provides
25 | @Singleton
26 | AnalyticsFacade providesAnalyticsManager(@NonNull FirebaseAnalytics firebaseAnalytics) {
27 | return new AnalyticsFacadeImpl(firebaseAnalytics);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/app/AppComponent.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.app;
2 |
3 | import android.content.Context;
4 |
5 | import com.koalatea.thehollidayinn.softwareengineeringdaily.analytics.AnalyticsFacade;
6 | import com.koalatea.thehollidayinn.softwareengineeringdaily.analytics.AnalyticsModule;
7 | import com.koalatea.thehollidayinn.softwareengineeringdaily.dagger.NetworkModule;
8 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.remote.APIInterface;
9 | import com.koalatea.thehollidayinn.softwareengineeringdaily.repositories.RepositoryModule;
10 | import com.koalatea.thehollidayinn.softwareengineeringdaily.repositories.UserRepository;
11 |
12 | import javax.inject.Singleton;
13 |
14 | import dagger.Component;
15 |
16 | /**
17 | * Created by Kurian on 25-Sep-17.
18 | */
19 | @Singleton
20 | @Component(modules = {
21 | AppModule.class,
22 | AnalyticsModule.class,
23 | NetworkModule.class,
24 | RepositoryModule.class})
25 | public interface AppComponent {
26 | Context context();
27 | AnalyticsFacade analyticsFacade();
28 | APIInterface kibblService();
29 | UserRepository userRepository();
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/app/AppModule.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.app;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.content.SharedPreferences;
6 | import android.preference.PreferenceManager;
7 | import android.support.annotation.NonNull;
8 |
9 | import javax.inject.Singleton;
10 |
11 | import dagger.Module;
12 | import dagger.Provides;
13 |
14 | /**
15 | * Created by Kurian on 25-Sep-17.
16 | */
17 | @Module
18 | public class AppModule {
19 |
20 | private final SEDApp app;
21 |
22 | public AppModule(@NonNull SEDApp app) {
23 | this.app = app;
24 | }
25 |
26 | @Provides
27 | @Singleton
28 | Application providesApplication() {
29 | return this.app;
30 | }
31 |
32 | @Provides
33 | @Singleton
34 | Context providesContext() {
35 | return this.app;
36 | }
37 |
38 | @Provides
39 | @Singleton
40 | SharedPreferences providesSharedPreferences(@NonNull Context context) {
41 | return PreferenceManager.getDefaultSharedPreferences(context);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/app/AppScope.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.app;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | import javax.inject.Scope;
7 |
8 | /**
9 | * Created by Kurian on 25-Sep-17.
10 | */
11 | @Scope
12 | @Retention(RetentionPolicy.RUNTIME)
13 | public @interface AppScope {
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/app/SEDApp.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.app;
2 |
3 | import android.app.Application;
4 | import android.support.annotation.VisibleForTesting;
5 |
6 | import com.akaita.java.rxjava2debug.RxJava2Debug;
7 | import com.koalatea.thehollidayinn.softwareengineeringdaily.BuildConfig;
8 |
9 | import com.koalatea.thehollidayinn.softwareengineeringdaily.R;
10 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.AppDatabase;
11 | import com.squareup.leakcanary.LeakCanary;
12 | import timber.log.Timber;
13 | import uk.co.chrisjenx.calligraphy.CalligraphyConfig;
14 |
15 | /**
16 | * Created by Kurian on 25-Sep-17.
17 | */
18 |
19 | public class SEDApp extends Application {
20 |
21 | @VisibleForTesting
22 | public static AppComponent component;
23 |
24 | @Override
25 | public void onCreate() {
26 | super.onCreate();
27 |
28 | CalligraphyConfig.initDefault(new CalligraphyConfig.Builder()
29 | .setDefaultFontPath("Roboto-RobotoRegular.ttf")
30 | .setFontAttrId(R.attr.fontPath)
31 | .build()
32 | );
33 |
34 | initLeakCanary();
35 | initDependencies();
36 | createLogger();
37 | // Enable RxJava assembly stack collection, to make RxJava crash reports clear and unique
38 | // Make sure this is called AFTER setting up any Crash reporting mechanism as Crashlytics
39 | RxJava2Debug.enableRxJava2AssemblyTracking(new String[] { BuildConfig.APPLICATION_ID });
40 | }
41 |
42 | private void initLeakCanary() {
43 | if (LeakCanary.isInAnalyzerProcess(this)) {
44 | // This process is dedicated to LeakCanary for heap analysis.
45 | // You should not init your app in this process.
46 | return;
47 | }
48 | LeakCanary.install(this);
49 | }
50 |
51 | private void createLogger() {
52 | if (BuildConfig.DEBUG) {
53 | Timber.plant(new Timber.DebugTree());
54 | }
55 | }
56 |
57 | private void initDependencies() {
58 | if (component == null) {
59 | component = DaggerAppComponent.builder()
60 | .appModule(new AppModule(this))
61 | .build();
62 | }
63 | }
64 |
65 | public static AppComponent component() {
66 | return component;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/audio/MediaIDHelper.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.audio;
2 |
3 | import android.app.Activity;
4 | import android.support.annotation.NonNull;
5 | import android.support.v4.media.MediaBrowserCompat;
6 | import android.support.v4.media.session.MediaControllerCompat;
7 | import android.text.TextUtils;
8 |
9 | import java.util.Arrays;
10 |
11 | /**
12 | * Created by keithholliday on 5/2/18.
13 | */
14 |
15 | public class MediaIDHelper {
16 |
17 | // Media IDs used on browseable items of MediaBrowser
18 | public static final String MEDIA_ID_EMPTY_ROOT = "__EMPTY_ROOT__";
19 | public static final String MEDIA_ID_ROOT = "__ROOT__";
20 | public static final String MEDIA_ID_MUSICS_BY_GENRE = "__BY_GENRE__";
21 | public static final String MEDIA_ID_MUSICS_BY_SEARCH = "__BY_SEARCH__";
22 |
23 | private static final char CATEGORY_SEPARATOR = '/';
24 | private static final char LEAF_SEPARATOR = '|';
25 |
26 | /**
27 | * Create a String value that represents a playable or a browsable media.
28 | *
29 | * Encode the media browseable categories, if any, and the unique music ID, if any,
30 | * into a single String mediaID.
31 | *
32 | * MediaIDs are of the form /|, to make it easy
33 | * to find the category (like genre) that a music was selected from, so we
34 | * can correctly build the playing queue. This is specially useful when
35 | * one music can appear in more than one list, like "by genre -> genre_1"
36 | * and "by artist -> artist_1".
37 | * @param musicID Unique music ID for playable items, or null for browseable items.
38 | * @param categories hierarchy of categories representing this item's browsing parents
39 | * @return a hierarchy-aware media ID
40 | */
41 | public static String createMediaID(String musicID, String... categories) {
42 | StringBuilder sb = new StringBuilder();
43 | if (categories != null) {
44 | for (int i=0; i < categories.length; i++) {
45 | if (!isValidCategory(categories[i])) {
46 | throw new IllegalArgumentException("Invalid category: " + categories[i]);
47 | }
48 | sb.append(categories[i]);
49 | if (i < categories.length - 1) {
50 | sb.append(CATEGORY_SEPARATOR);
51 | }
52 | }
53 | }
54 | if (musicID != null) {
55 | sb.append(LEAF_SEPARATOR).append(musicID);
56 | }
57 | return sb.toString();
58 | }
59 |
60 | private static boolean isValidCategory(String category) {
61 | return category == null ||
62 | (
63 | category.indexOf(CATEGORY_SEPARATOR) < 0 &&
64 | category.indexOf(LEAF_SEPARATOR) < 0
65 | );
66 | }
67 |
68 | /**
69 | * Extracts unique musicID from the mediaID. mediaID is, by this sample's convention, a
70 | * concatenation of category (eg "by_genre"), categoryValue (eg "Classical") and unique
71 | * musicID. This is necessary so we know where the user selected the music from, when the music
72 | * exists in more than one music list, and thus we are able to correctly build the playing queue.
73 | *
74 | * @param mediaID that contains the musicID
75 | * @return musicID
76 | */
77 | public static String extractMusicIDFromMediaID(@NonNull String mediaID) {
78 | int pos = mediaID.indexOf(LEAF_SEPARATOR);
79 | if (pos >= 0) {
80 | return mediaID.substring(pos+1);
81 | }
82 | return null;
83 | }
84 |
85 | /**
86 | * Extracts category and categoryValue from the mediaID. mediaID is, by this sample's
87 | * convention, a concatenation of category (eg "by_genre"), categoryValue (eg "Classical") and
88 | * mediaID. This is necessary so we know where the user selected the music from, when the music
89 | * exists in more than one music list, and thus we are able to correctly build the playing queue.
90 | *
91 | * @param mediaID that contains a category and categoryValue.
92 | */
93 | public static @NonNull String[] getHierarchy(@NonNull String mediaID) {
94 | int pos = mediaID.indexOf(LEAF_SEPARATOR);
95 | if (pos >= 0) {
96 | mediaID = mediaID.substring(0, pos);
97 | }
98 | return mediaID.split(String.valueOf(CATEGORY_SEPARATOR));
99 | }
100 |
101 | public static String extractBrowseCategoryValueFromMediaID(@NonNull String mediaID) {
102 | String[] hierarchy = getHierarchy(mediaID);
103 | if (hierarchy.length == 2) {
104 | return hierarchy[1];
105 | }
106 | return null;
107 | }
108 |
109 | public static boolean isBrowseable(@NonNull String mediaID) {
110 | return mediaID.indexOf(LEAF_SEPARATOR) < 0;
111 | }
112 |
113 | public static String getParentMediaID(@NonNull String mediaID) {
114 | String[] hierarchy = getHierarchy(mediaID);
115 | if (!isBrowseable(mediaID)) {
116 | return createMediaID(null, hierarchy);
117 | }
118 | if (hierarchy.length <= 1) {
119 | return MEDIA_ID_ROOT;
120 | }
121 | String[] parentHierarchy = Arrays.copyOf(hierarchy, hierarchy.length-1);
122 | return createMediaID(null, parentHierarchy);
123 | }
124 |
125 | public static boolean isMediaItemPlaying(Activity context, MediaBrowserCompat.MediaItem mediaItem) {
126 | // Media item is considered to be playing or paused based on the controller's current
127 | // media id
128 | MediaControllerCompat controller = MediaControllerCompat.getMediaController(context);
129 | if (controller != null && controller.getMetadata() != null) {
130 | String currentPlayingMediaId = controller.getMetadata().getDescription()
131 | .getMediaId();
132 | String itemMusicId = MediaIDHelper.extractMusicIDFromMediaID(
133 | mediaItem.getDescription().getMediaId());
134 | if (currentPlayingMediaId != null
135 | && TextUtils.equals(currentPlayingMediaId, itemMusicId)) {
136 | return true;
137 | }
138 | }
139 | return false;
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/audio/Playback.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.audio;
2 |
3 | import android.support.v4.media.session.MediaSessionCompat;
4 |
5 | /**
6 | * Created by keithholliday on 4/25/18.
7 | */
8 |
9 | public interface Playback {
10 | /**
11 | * Start/setup the playback.
12 | * Resources/listeners would be allocated by implementations.
13 | */
14 | void start();
15 |
16 | /**
17 | * Stop the playback. All resources can be de-allocated by implementations here.
18 | * @param notifyListeners if true and a callback has been set by setCallback,
19 | * callback.onPlaybackStatusChanged will be called after changing
20 | * the state.
21 | */
22 | void stop(boolean notifyListeners);
23 |
24 | /**
25 | * Set the latest playback state as determined by the caller.
26 | */
27 | void setState(int state);
28 |
29 | /**
30 | * Get the current {@link android.media.session.PlaybackState#getState()}
31 | */
32 | int getState();
33 |
34 | /**
35 | * @return boolean that indicates that this is ready to be used.
36 | */
37 | boolean isConnected();
38 |
39 | /**
40 | * @return boolean indicating whether the player is playing or is supposed to be
41 | * playing when we gain audio focus.
42 | */
43 | boolean isPlaying();
44 |
45 | /**
46 | * @return pos if currently playing an item
47 | */
48 | long getCurrentStreamPosition();
49 |
50 | /**
51 | * Queries the underlying stream and update the internal last known stream position.
52 | */
53 | void updateLastKnownStreamPosition();
54 |
55 | void play(MediaSessionCompat.QueueItem item);
56 |
57 | void pause();
58 |
59 | void seekTo(long position);
60 |
61 | void setCurrentMediaId(String mediaId);
62 |
63 | String getCurrentMediaId();
64 |
65 | void setSpeed(int speed);
66 |
67 | void moveForward(int distance);
68 |
69 | void moveBack(int distance);
70 |
71 | interface Callback {
72 | /**
73 | * On current music completed.
74 | */
75 | void onCompletion();
76 | /**
77 | * on Playback status changed
78 | * Implementations can use this callback to update
79 | * playback state on the media sessions.
80 | */
81 | void onPlaybackStatusChanged(int state);
82 |
83 | /**
84 | * @param error to be added to the PlaybackState
85 | */
86 | void onError(String error);
87 |
88 | /**
89 | * @param mediaId being currently played
90 | */
91 | void setCurrentMediaId(String mediaId);
92 | }
93 |
94 | void setCallback(Callback callback);
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/dagger/NetworkModule.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.dagger;
2 |
3 | import android.app.Application;
4 |
5 | import com.google.gson.FieldNamingPolicy;
6 | import com.google.gson.Gson;
7 | import com.google.gson.GsonBuilder;
8 | import com.koalatea.thehollidayinn.softwareengineeringdaily.BuildConfig;
9 | import com.koalatea.thehollidayinn.softwareengineeringdaily.app.SEDApp;
10 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.remote.APIInterface;
11 | import com.koalatea.thehollidayinn.softwareengineeringdaily.repositories.UserRepository;
12 |
13 | import java.io.IOException;
14 |
15 | import javax.inject.Singleton;
16 |
17 | import dagger.Module;
18 | import dagger.Provides;
19 | import okhttp3.Cache;
20 | import okhttp3.Interceptor;
21 | import okhttp3.OkHttpClient;
22 | import okhttp3.Request;
23 | import okhttp3.Response;
24 | import okhttp3.logging.HttpLoggingInterceptor;
25 | import retrofit2.Retrofit;
26 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
27 | import retrofit2.converter.gson.GsonConverterFactory;
28 |
29 | /**
30 | * Created by keithholliday on 1/3/18.
31 | */
32 |
33 | @Module
34 | public class NetworkModule {
35 | String baseUrl = "https://software-enginnering-daily-api.herokuapp.com/api/";
36 | // Staging
37 | // String baseUrl = "https://sedaily-backend-staging.herokuapp.com/api/";
38 |
39 | // Local
40 | // String baseUrl = "http://192.168.1.251:4040/api/";
41 |
42 | // public NetworkModule(String baseUrl) {
43 | // this.baseUrl = baseUrl;
44 | // }
45 |
46 | @Provides
47 | @Singleton
48 | Cache provideHttpCache(Application application) {
49 | int cacheSize = 10 * 1024 * 1024;
50 | Cache cache = new Cache(application.getCacheDir(), cacheSize);
51 | return cache;
52 | }
53 |
54 | @Provides
55 | @Singleton
56 | Gson provideGson() {
57 | GsonBuilder gsonBuilder = new GsonBuilder();
58 | gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
59 | return gsonBuilder.create();
60 | }
61 |
62 | @Provides
63 | @Singleton
64 | OkHttpClient provideOkhttpClient(Cache cache) {
65 | HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
66 | if (BuildConfig.DEBUG) {
67 | logging.setLevel(HttpLoggingInterceptor.Level.BODY);
68 | }
69 | final UserRepository userLogin = UserRepository.getInstance(SEDApp.component.context());
70 | OkHttpClient.Builder clientbuilder = new OkHttpClient.Builder()
71 | .addInterceptor(new Interceptor() {
72 | @Override
73 | public Response intercept(Interceptor.Chain chain) throws IOException {
74 | Request.Builder ongoing = chain.request().newBuilder();
75 | ongoing.addHeader("Accept", "application/json;versions=1");
76 |
77 | if (!userLogin.getToken().isEmpty()) {
78 | ongoing.addHeader("Authorization", "Bearer " + userLogin.getToken());
79 | }
80 |
81 | return chain.proceed(ongoing.build());
82 | }
83 | });
84 |
85 | if (BuildConfig.DEBUG) {
86 | clientbuilder.addInterceptor(logging);
87 | }
88 | // clientbuilder.cache(cache);
89 |
90 | return clientbuilder.build();
91 | }
92 |
93 | @Provides
94 | @Singleton
95 | Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
96 | return new Retrofit.Builder()
97 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
98 | .addConverterFactory(GsonConverterFactory.create(gson))
99 | .baseUrl(baseUrl)
100 | .client(okHttpClient)
101 | .build();
102 | }
103 |
104 | @Provides
105 | APIInterface providesKibbleService(Retrofit retrofitAdapter) {
106 | return retrofitAdapter.create(APIInterface.class);
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/data/AppDatabase.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.data;
2 |
3 | import android.arch.persistence.room.Database;
4 | import android.arch.persistence.room.Room;
5 | import android.arch.persistence.room.RoomDatabase;
6 | import android.content.Context;
7 |
8 | import com.koalatea.thehollidayinn.softwareengineeringdaily.app.SEDApp;
9 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.models.Bookmark;
10 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.models.Download;
11 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.repositories.BookmarkDao;
12 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.repositories.DownloadDao;
13 |
14 | /**
15 | * Created by samuelrey on 12/1/17.
16 | */
17 |
18 | @Database(entities = {Bookmark.class, Download.class}, version = 2)
19 | public abstract class AppDatabase extends RoomDatabase {
20 | public abstract BookmarkDao bookmarkDao();
21 | public abstract DownloadDao downloadDao();
22 |
23 | private static AppDatabase INSTANCE;
24 |
25 | public static AppDatabase getDatabase() {
26 | Context context = SEDApp.component().context();
27 |
28 | if (INSTANCE == null) {
29 | synchronized (AppDatabase.class) {
30 | if (INSTANCE == null) {
31 | INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
32 | AppDatabase.class, "sed-db")
33 | .fallbackToDestructiveMigration()
34 | .build();
35 | }
36 | }
37 | }
38 | return INSTANCE;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/data/models/Bookmark.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.data.models;
2 |
3 | import android.arch.persistence.room.ColumnInfo;
4 | import android.arch.persistence.room.Entity;
5 | import android.arch.persistence.room.PrimaryKey;
6 |
7 | import org.jetbrains.annotations.NotNull;
8 |
9 | /**
10 | * Created by samuelrey on 11/30/17.
11 | */
12 |
13 | @Entity(tableName = "bookmark")
14 | public class Bookmark {
15 | @PrimaryKey
16 | @NotNull
17 | private String postId;
18 |
19 | @ColumnInfo(name = "active")
20 | private Boolean active;
21 |
22 | public Bookmark(@NotNull Post post) {
23 | this.postId = post.get_id();
24 | }
25 |
26 | public Bookmark(String postId, Boolean active) {
27 | this.postId = postId;
28 | this.active = active;
29 | }
30 |
31 | public String getPostId() {
32 | return postId;
33 | }
34 |
35 | public void setPostId(String postId) {
36 | this.postId = postId;
37 | }
38 |
39 | public Boolean getActive() {
40 | return active;
41 | }
42 |
43 | public void setActive(Boolean active) {
44 | this.active = active;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/data/models/Content.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.data.models;
2 |
3 | /*
4 | * Created by krh12 on 6/17/2017.
5 | */
6 |
7 | public class Content {
8 | private String rendered;
9 |
10 | public String getRendered() {
11 | return rendered;
12 | }
13 |
14 | public void setRendered(String rendered) {
15 | this.rendered = rendered;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/data/models/Download.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.data.models;
2 |
3 | import android.arch.persistence.room.ColumnInfo;
4 | import android.arch.persistence.room.Entity;
5 | import android.arch.persistence.room.PrimaryKey;
6 | import android.support.annotation.NonNull;
7 |
8 | @Entity(tableName = "downloads_table")
9 | public class Download {
10 |
11 | @PrimaryKey
12 | @NonNull
13 | @ColumnInfo(name = "postId")
14 | private String postId;
15 |
16 | public Download(String postId) {this.postId = postId;}
17 |
18 | public String getPostId(){return this.postId;}
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/data/models/Post.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.data.models;
2 |
3 | import com.google.gson.annotations.SerializedName;
4 |
5 | import java.util.Date;
6 |
7 | /*
8 | * Created by krh12 on 6/17/2017.
9 | */
10 |
11 | public class Post {
12 | private String _id;
13 | private String id;
14 | private Date date;
15 | private String slug;
16 | private String link;
17 | private Title title;
18 | private Content content;
19 | private Integer score;
20 | private Boolean upvoted;
21 | private Boolean downvoted;
22 | private String mp3;
23 | @SerializedName("featuredImage")
24 | private String featuredImage;
25 |
26 | public String get_id() {
27 | return _id;
28 | }
29 |
30 | public void set_id(String _id) {
31 | this._id = _id;
32 | }
33 |
34 | public String getId() {
35 | return id;
36 | }
37 |
38 | public void setId(String id) {
39 | this.id = id;
40 | }
41 |
42 | public Date getDate() {
43 | return date;
44 | }
45 |
46 | public void setDate(Date date) {
47 | this.date = date;
48 | }
49 |
50 | public String getSlug() {
51 | return slug;
52 | }
53 |
54 | public void setSlug(String slug) {
55 | this.slug = slug;
56 | }
57 |
58 | public String getLink() {
59 | return link;
60 | }
61 |
62 | public void setLink(String link) {
63 | this.link = link;
64 | }
65 |
66 | public Title getTitle() {
67 | return title;
68 | }
69 |
70 | public void setTitle(Title title) {
71 | this.title = title;
72 | }
73 |
74 | public Content getContent() {
75 | return content;
76 | }
77 |
78 | public void setContent(Content content) {
79 | this.content = content;
80 | }
81 |
82 | public Integer getScore() {
83 | return score;
84 | }
85 |
86 | public void setScore(Integer score) {
87 | this.score = score;
88 | }
89 |
90 | public Boolean getUpvoted() {
91 | return upvoted;
92 | }
93 |
94 | public void setUpvoted(Boolean upvoted) {
95 | this.upvoted = upvoted;
96 | }
97 |
98 | public Boolean getDownvoted() {
99 | return downvoted;
100 | }
101 |
102 | public void setDownvoted(Boolean downvoted) {
103 | this.downvoted = downvoted;
104 | }
105 |
106 | public String getMp3() {
107 | return mp3;
108 | }
109 |
110 | public void setMp3(String mp3) {
111 | this.mp3 = mp3;
112 | }
113 |
114 | public String getFeaturedImage() {
115 | return featuredImage;
116 | }
117 |
118 | public void setFeaturedImage(String featuredImage) {
119 | this.featuredImage = featuredImage;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/data/models/PostItem.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.data.models;
2 |
3 | import com.google.auto.value.AutoValue;
4 |
5 | import java.util.Date;
6 |
7 | /**
8 | * Created by Kurian on 26-Sep-17.
9 | */
10 | @AutoValue
11 | public abstract class PostItem {
12 | public abstract String id();
13 | public abstract Date date();
14 | public abstract String episodeLink();
15 | public abstract String audioLink();
16 | public abstract String featuredImgLink();
17 | public abstract String content();
18 | public abstract String title();
19 | public abstract int score();
20 | public abstract boolean upVoted();
21 | public abstract boolean downVoted();
22 |
23 | public static Builder builder() {
24 | return new AutoValue_PostItem.Builder();
25 | }
26 |
27 | @AutoValue.Builder
28 | public abstract static class Builder {
29 | public abstract Builder id(String id);
30 | public abstract Builder date(Date date);
31 | public abstract Builder episodeLink(String episodeLink);
32 | public abstract Builder audioLink(String audioLink);
33 | public abstract Builder featuredImgLink(String featuredImgLink);
34 | public abstract Builder content(String content);
35 | public abstract Builder title(String title);
36 | public abstract Builder score(int score);
37 | public abstract Builder upVoted(boolean upVoted);
38 | public abstract Builder downVoted(boolean downVoted);
39 | public abstract PostItem build();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/data/models/SubscriptionResponse.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.data.models;
2 |
3 | /**
4 | * Created by keithholliday on 1/7/18.
5 | */
6 |
7 | public class SubscriptionResponse {
8 | public Boolean active;
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/data/models/Title.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.data.models;
2 |
3 | /*
4 | * Created by krh12 on 6/17/2017.
5 | */
6 |
7 | public class Title {
8 | private String rendered;
9 |
10 | public String getRendered() {
11 | return rendered;
12 | }
13 |
14 | public void setRendered(String rendered) {
15 | this.rendered = rendered;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/data/models/User.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.data.models;
2 |
3 | /*
4 | * Created by krh12 on 6/22/2017.
5 | */
6 |
7 | public class User {
8 | private String token;
9 |
10 | public String getToken() {
11 | return token;
12 | }
13 |
14 | public void setToken(String token) {
15 | this.token = token;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/data/models/UserResponse.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.data.models;
2 |
3 | /**
4 | * Created by keithholliday on 1/6/18.
5 | */
6 |
7 | public class UserResponse {
8 | SubscriptionResponse subscription;
9 |
10 | public SubscriptionResponse getSubscription() {
11 | return subscription;
12 | }
13 |
14 | public void setSubscription(SubscriptionResponse subscription) {
15 | this.subscription = subscription;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/data/remote/APIInterface.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.data.remote;
2 |
3 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.models.Post;
4 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.models.User;
5 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.models.UserResponse;
6 |
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | import io.reactivex.Completable;
11 | import io.reactivex.Observable;
12 | import retrofit2.http.DELETE;
13 | import retrofit2.http.Field;
14 | import retrofit2.http.FormUrlEncoded;
15 | import retrofit2.http.GET;
16 | import retrofit2.http.POST;
17 | import retrofit2.http.Path;
18 | import retrofit2.http.QueryMap;
19 |
20 | /*
21 | * Created by krh12 on 6/17/2017.
22 | */
23 |
24 | public interface APIInterface {
25 | @GET("posts")
26 | Observable> getPosts(@QueryMap Map options);
27 |
28 | @GET("posts/recommendations")
29 | Observable> getRecommendations(@QueryMap Map options);
30 |
31 | @POST("posts/{postid}/upvote")
32 | Observable upVote(@Path("postid") String postId);
33 |
34 | @POST("posts/{postid}/downvote")
35 | Observable downVote(@Path("postid") String postId);
36 |
37 | @FormUrlEncoded
38 | @POST("auth/login")
39 | Observable login(@Field("username") String username, @Field("email") String email, @Field("password") String password);
40 |
41 | @FormUrlEncoded
42 | @POST("auth/register")
43 | Observable register(@Field("username") String username, @Field("email") String email, @Field("password") String password);
44 |
45 | @FormUrlEncoded
46 | @POST("subscription")
47 | Completable createSubscription(@Field("stripeToken") String stripeToken, @Field("planType") String planType);
48 |
49 | @DELETE("subscription")
50 | Completable cancelSubscription();
51 |
52 | @GET("users/me")
53 | Observable me();
54 |
55 | @GET("users/me/bookmarked")
56 | Observable> getBookmarks();
57 |
58 | @POST("posts/{postid}/favorite")
59 | Observable addBookmark(@Path("postid") String postid);
60 |
61 | @POST("posts/{postid}/unfavorite")
62 | Observable removeBookmark(@Path("postid") String postid);
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/data/repositories/BookmarkDao.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.data.repositories;
2 |
3 | import android.arch.persistence.room.Dao;
4 | import android.arch.persistence.room.Delete;
5 | import android.arch.persistence.room.Insert;
6 | import android.arch.persistence.room.OnConflictStrategy;
7 | import android.arch.persistence.room.Query;
8 |
9 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.models.Bookmark;
10 |
11 | import java.util.List;
12 |
13 | /**
14 | * Created by samuelrey on 11/30/17.
15 | */
16 |
17 | @Dao
18 | public interface BookmarkDao {
19 | @Query("SELECT * FROM bookmark")
20 | List getAll();
21 |
22 | @Query("SELECT * FROM bookmark WHERE postId == :postId")
23 | Bookmark loadById(String postId);
24 |
25 | @Insert
26 | void insertOne(Bookmark bookmark);
27 |
28 | @Insert(onConflict = OnConflictStrategy.REPLACE)
29 | void insertAll(List bookmarks);
30 |
31 | @Delete
32 | void delete(Bookmark bookmark);
33 |
34 | @Query("DELETE FROM bookmark")
35 | void deleteAll();
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/data/repositories/DownloadDao.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.data.repositories;
2 |
3 | import android.arch.persistence.room.Dao;
4 | import android.arch.persistence.room.Delete;
5 | import android.arch.persistence.room.Insert;
6 | import android.arch.persistence.room.OnConflictStrategy;
7 | import android.arch.persistence.room.Query;
8 |
9 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.models.Download;
10 |
11 | import java.util.List;
12 |
13 | @Dao
14 | public interface DownloadDao {
15 | @Query("SELECT * FROM downloads_table")
16 | List getAll();
17 |
18 | @Query("SELECT * FROM bookmark WHERE postId == :postId")
19 | Download loadById(String postId);
20 |
21 | @Insert(onConflict = OnConflictStrategy.REPLACE)
22 | void insertOne(Download download);
23 |
24 | @Insert(onConflict = OnConflictStrategy.REPLACE)
25 | void insertAll(List downloads);
26 |
27 | @Delete
28 | void delete(Download download);
29 |
30 | @Query("DELETE FROM downloads_table")
31 | void deleteAll();
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/data/repositories/DownloadRepository.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.data.repositories;
2 |
3 | import android.arch.lifecycle.LiveData;
4 | import android.os.AsyncTask;
5 |
6 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.AppDatabase;
7 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.models.Download;
8 |
9 | import java.util.List;
10 |
11 | public class DownloadRepository {
12 | private DownloadDao downloadDao;
13 |
14 | public DownloadRepository() {
15 | AppDatabase db = AppDatabase.getDatabase();
16 | downloadDao = db.downloadDao();
17 | }
18 |
19 | public void insert(Download download) {
20 | new insertAsyncTask(downloadDao).execute(download);
21 | }
22 |
23 | public void remove(String podcastId) {
24 | new removeAsyncTask(downloadDao).execute(podcastId);
25 | }
26 |
27 | public List getDownloads() {
28 | return downloadDao.getAll();
29 | }
30 |
31 | private static class insertAsyncTask extends AsyncTask {
32 | private DownloadDao downloadAsyncDao;
33 |
34 | insertAsyncTask(DownloadDao dao) {
35 | downloadAsyncDao = dao;
36 | }
37 |
38 | @Override
39 | protected Void doInBackground(final Download... params) {
40 | downloadAsyncDao.insertOne(params[0]);
41 | return null;
42 | }
43 | }
44 |
45 | private static class removeAsyncTask extends AsyncTask {
46 | private DownloadDao downloadAsyncDao;
47 |
48 | removeAsyncTask(DownloadDao dao) {
49 | downloadAsyncDao = dao;
50 | }
51 |
52 | @Override
53 | protected Void doInBackground(final String... params) {
54 | Download download = downloadAsyncDao.loadById(params[0]);
55 | if (download == null) return null;
56 | downloadAsyncDao.delete(download);
57 | return null;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/downloads/MP3FileManager.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.downloads;
2 |
3 | import android.content.Context;
4 | import android.os.Environment;
5 | import android.support.v4.content.ContextCompat;
6 |
7 | import java.io.File;
8 |
9 | /**
10 | * Created by keithholliday on 10/15/17.
11 | */
12 |
13 | public class MP3FileManager {
14 | public String getRootDirPath(Context context) {
15 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
16 | File file = ContextCompat.getExternalFilesDirs(context.getApplicationContext(),
17 | null)[0];
18 | return file.getAbsolutePath();
19 | } else {
20 | return context.getApplicationContext().getFilesDir().getAbsolutePath();
21 | }
22 | }
23 |
24 | public String getFileNameFromUrl(String urlString) {
25 | return urlString.substring(urlString.lastIndexOf('/') + 1, urlString.length());
26 | }
27 |
28 | public File getFileFromUrl (String urlString, Context context) {
29 | return new File(context.getFilesDir(), getFileNameFromUrl(urlString));
30 | }
31 |
32 | public String getExternalFileString (String urlString, Context context) {
33 | return context.getFilesDir() + "/" + getFileNameFromUrl(urlString);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/latest/RecentPodcastFragment.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.latest;
2 |
3 | import android.os.Bundle;
4 | import android.support.design.widget.TabLayout;
5 | import android.support.v4.app.Fragment;
6 | import android.support.v4.view.ViewPager;
7 | import android.support.v7.widget.Toolbar;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 |
12 | import com.koalatea.thehollidayinn.softwareengineeringdaily.MainActivity;
13 | import com.koalatea.thehollidayinn.softwareengineeringdaily.R;
14 |
15 | import butterknife.BindView;
16 | import butterknife.ButterKnife;
17 |
18 | /**
19 | * Created by keithholliday on 9/16/17.
20 | */
21 |
22 | public class RecentPodcastFragment extends Fragment {
23 | RecentPodcastsPageAdapter recentPodcatsPageAdapter;
24 |
25 | @BindView(R.id.pager)
26 | ViewPager viewPager;
27 |
28 | private TabLayout tabLayout;
29 |
30 | public static RecentPodcastFragment newInstance() {
31 | RecentPodcastFragment f = new RecentPodcastFragment();
32 | return f;
33 | }
34 |
35 | @Override
36 | public View onCreateView(LayoutInflater inflater,
37 | ViewGroup container, Bundle savedInstanceState) {
38 |
39 | View rootView = (View) inflater.inflate(
40 | R.layout.fragment_recent_podcast, container, false);
41 |
42 | ButterKnife.bind(this, rootView);
43 |
44 | recentPodcatsPageAdapter = new RecentPodcastsPageAdapter(getChildFragmentManager());
45 | viewPager.setAdapter(recentPodcatsPageAdapter);
46 |
47 | tabLayout = this.getActivity().findViewById(R.id.tabs);
48 | tabLayout.post(new Runnable() {
49 | @Override
50 | public void run() {
51 | tabLayout.setupWithViewPager(viewPager);
52 | }
53 | });
54 |
55 | return rootView;
56 | }
57 |
58 | public void goHome () {
59 | TabLayout.Tab tab = tabLayout.getTabAt(0);
60 | tab.select();
61 | }
62 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/latest/RecentPodcastsPageAdapter.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.latest;
2 |
3 | import android.support.v4.app.Fragment;
4 | import android.support.v4.app.FragmentManager;
5 | import android.support.v4.app.FragmentPagerAdapter;
6 |
7 | import com.koalatea.thehollidayinn.softwareengineeringdaily.podcast.PodListFragment;
8 |
9 | import java.util.ArrayList;
10 | import java.util.Arrays;
11 | import java.util.List;
12 |
13 | /*
14 | * Created by keithholliday on 9/16/17.
15 | */
16 |
17 | class RecentPodcastsPageAdapter extends FragmentPagerAdapter {
18 | private List fragmentList = new ArrayList<>();
19 | private List titles;
20 |
21 | public RecentPodcastsPageAdapter(FragmentManager fm) {
22 | super(fm);
23 |
24 | String LATEST = "Latest";
25 | titles = Arrays.asList(
26 | "All",
27 | "Business and Philosophy",
28 | "Blockchain",
29 | "Cloud Engineering",
30 | "Data",
31 | "JavaScript",
32 | "Machine Learning",
33 | "Open Source",
34 | "Security",
35 | "Hackers",
36 | "Greatest Hits"
37 | );
38 |
39 | List categories = Arrays.asList(
40 | "",
41 | "1068",
42 | "1082",
43 | "1079",
44 | "1081",
45 | "1084",
46 | "1080",
47 | "1078",
48 | "1083",
49 | "1085",
50 | "1069"
51 | );
52 |
53 | for (String category : categories) {
54 | fragmentList.add(PodListFragment.newInstance(LATEST, category));
55 | }
56 | }
57 |
58 | @Override
59 | public int getCount() {
60 | return titles.size();
61 | }
62 |
63 | @Override
64 | public Fragment getItem(int position) {
65 | return fragmentList.get(position);
66 | }
67 |
68 | @Override
69 | public CharSequence getPageTitle(int position) {
70 | return titles.get(position);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/mediaui/FullscreenPlayerFragment.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.mediaui;
2 |
3 | import android.app.Fragment;
4 |
5 | /**
6 | * Created by George Lin on 10/19/2017.
7 | */
8 |
9 | public class FullscreenPlayerFragment extends Fragment{
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/mediaui/SpeedDialog.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.mediaui;
2 |
3 | import android.app.Dialog;
4 | import android.content.DialogInterface;
5 | import android.os.Bundle;
6 | import android.support.v4.app.DialogFragment;
7 | import android.support.v7.app.AlertDialog;
8 | import com.koalatea.thehollidayinn.softwareengineeringdaily.R;
9 | import com.koalatea.thehollidayinn.softwareengineeringdaily.podcast.PodcastSessionStateManager;
10 |
11 | /*
12 | * Created by keithholliday on 11/3/17.
13 | */
14 |
15 | public class SpeedDialog extends DialogFragment {
16 | @Override
17 | public Dialog onCreateDialog(Bundle savedInstanceState) {
18 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
19 |
20 | builder.setTitle(R.string.speed)
21 | .setItems(R.array.speed_options, new DialogInterface.OnClickListener() {
22 | public void onClick(DialogInterface dialog, int which) {
23 | PodcastSessionStateManager.getInstance().setCurrentSpeed(which);
24 | }
25 | });
26 |
27 | return builder.create();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/notifications/DailyAlarmReceiver.kt:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.notifications
2 |
3 | import android.app.PendingIntent
4 | import android.content.BroadcastReceiver
5 | import android.content.Context
6 | import android.content.Intent
7 | import android.media.RingtoneManager
8 | import android.net.Uri
9 | import android.support.v4.app.NotificationCompat
10 | import android.support.v4.app.TaskStackBuilder
11 | import com.koalatea.thehollidayinn.softwareengineeringdaily.MainActivity
12 | import com.koalatea.thehollidayinn.softwareengineeringdaily.R
13 | import android.support.v4.app.NotificationManagerCompat
14 | import android.app.NotificationChannel
15 | import android.app.NotificationManager
16 | import android.os.Build
17 | import android.util.Log
18 |
19 |
20 | /**
21 | * Created by keithholliday on 3/23/18.
22 | */
23 | class DailyAlarmReceiver: BroadcastReceiver() {
24 | var DAILY_REMINDER_REQUEST_CODE = 198762999
25 | var CHANNEL_ID = "sedaily-channel-local"
26 |
27 | override fun onReceive(context: Context, intent: Intent) {
28 | // @TODO: Restart on reboot
29 | // if (intent.action != null)
30 | // {
31 | // if (intent.action.equalsIgnoreCase(Intent.ACTION_BOOT_COMPLETED))
32 | // {
33 | // val localData = LocalData(context)
34 | // NotificationScheduler.setReminder(context, AlarmReceiver::class.java,
35 | // localData.get_hour(), localData.get_min())
36 | // return
37 | // }
38 | // }
39 | showNotification(context, "We have some news podcasts for you!", "Come check them out. :D")
40 | }
41 |
42 | fun showNotification(context: Context, title: String, content: String) {
43 | var alarmSound: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
44 |
45 | var notificationIntent = Intent(context, MainActivity::class.java)
46 | notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
47 |
48 | val stackBuilder = TaskStackBuilder.create(context)
49 | stackBuilder.addParentStack(MainActivity::class.java)
50 | stackBuilder.addNextIntent(notificationIntent)
51 |
52 | val pendingIntent = stackBuilder.getPendingIntent(
53 | DAILY_REMINDER_REQUEST_CODE, PendingIntent.FLAG_UPDATE_CURRENT)
54 |
55 | val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
56 |
57 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
58 | val importance = NotificationManager.IMPORTANCE_DEFAULT
59 | val channel = NotificationChannel(CHANNEL_ID, CHANNEL_ID, importance)
60 | // val notificationManager = NotificationManagerCompat.from(context)
61 | notificationManager.createNotificationChannel(channel)
62 | }
63 |
64 | val builder = NotificationCompat.Builder(context, CHANNEL_ID)
65 | val notification = builder.setContentTitle(title)
66 | .setContentText(content).setAutoCancel(true)
67 | .setSound(alarmSound).setSmallIcon(R.drawable.sedaily_logo)
68 | .setContentIntent(pendingIntent).build()
69 |
70 |
71 | notificationManager.notify(DAILY_REMINDER_REQUEST_CODE, notification)
72 | }
73 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/notifications/NotificationActivity.kt:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.notifications
2 |
3 | import android.app.AlarmManager
4 | import android.app.PendingIntent
5 | import android.content.ComponentName
6 | import android.content.Context
7 | import android.content.Intent
8 | import android.content.pm.PackageManager
9 | import android.support.v7.app.AppCompatActivity
10 | import android.os.Bundle
11 | import android.view.View
12 | import com.koalatea.thehollidayinn.softwareengineeringdaily.MainActivity
13 | import com.koalatea.thehollidayinn.softwareengineeringdaily.R
14 | import com.koalatea.thehollidayinn.softwareengineeringdaily.repositories.UserRepository
15 | import kotlinx.android.synthetic.main.activity_notification.*
16 | import java.util.*
17 |
18 | // http://droidmentor.com/schedule-notifications-using-alarmmanager/
19 |
20 | class NotificationActivity : AppCompatActivity() {
21 |
22 | var DAILY_REMINDER_REQUEST_CODE = 198762999
23 | var DAILY_REMINDER_REQUEST_CODE2 = 198762998
24 | var DAILY_REMINDER_REQUEST_CODE3 = 198762998
25 |
26 | override fun onCreate(savedInstanceState: Bundle?) {
27 | super.onCreate(savedInstanceState)
28 | setContentView(R.layout.activity_notification)
29 |
30 | val userRepo: UserRepository = UserRepository.getInstance(this);
31 |
32 | if (userRepo.subscribed) {
33 | switch1.isChecked = true
34 | }
35 |
36 | switch1.setOnClickListener(object : View.OnClickListener {
37 | override fun onClick(v: View?) {
38 | if (userRepo.subscribed) {
39 | userRepo.subscribed = false
40 | cancelReminder(applicationContext, DAILY_REMINDER_REQUEST_CODE)
41 | cancelReminder(applicationContext, DAILY_REMINDER_REQUEST_CODE2)
42 | cancelReminder(applicationContext, DAILY_REMINDER_REQUEST_CODE3)
43 | return
44 | }
45 |
46 | setReminder(applicationContext, Calendar.MONDAY,10, 0, DAILY_REMINDER_REQUEST_CODE)
47 | setReminder(applicationContext, Calendar.WEDNESDAY, 10, 0, DAILY_REMINDER_REQUEST_CODE2)
48 | setReminder(applicationContext, Calendar.FRIDAY, 10, 0, DAILY_REMINDER_REQUEST_CODE3)
49 | userRepo.subscribed = true
50 | }
51 | })
52 | }
53 |
54 | fun setReminder(context: Context, day: Int, hour: Int, min: Int, code: Int) {
55 | var setCalendar: Calendar = Calendar.getInstance()
56 |
57 | setCalendar.set(Calendar.DAY_OF_WEEK, day)
58 | setCalendar.set(Calendar.HOUR_OF_DAY, hour)
59 | setCalendar.set(Calendar.MINUTE, min)
60 | setCalendar.set(Calendar.SECOND, 0)
61 |
62 | cancelReminder(context, code)
63 |
64 | val receiver = ComponentName(context, MainActivity::class.java)
65 | val pm = context.packageManager
66 | pm.setComponentEnabledSetting(receiver,
67 | PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
68 | PackageManager.DONT_KILL_APP)
69 |
70 | val intent1 = Intent(context, DailyAlarmReceiver::class.java)
71 | val pendingIntent = PendingIntent.getBroadcast(context,
72 | code,
73 | intent1,
74 | PendingIntent.FLAG_UPDATE_CURRENT)
75 |
76 | val alarmMgr = context.getSystemService(ALARM_SERVICE) as AlarmManager
77 | alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, setCalendar.timeInMillis,
78 | AlarmManager.INTERVAL_DAY, pendingIntent)
79 | }
80 |
81 | fun cancelReminder(context:Context, code: Int) {
82 | // Disable a receiver
83 | val receiver = ComponentName(context, MainActivity::class.java)
84 |
85 | val pm = context.packageManager
86 | pm.setComponentEnabledSetting(receiver,
87 | PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
88 | PackageManager.DONT_KILL_APP)
89 |
90 | val intent1 = Intent(context, MainActivity::class.java)
91 | val pendingIntent = PendingIntent.getBroadcast(
92 | context,
93 | code,
94 | intent1,
95 | PendingIntent.FLAG_UPDATE_CURRENT)
96 |
97 | val am = context.getSystemService(ALARM_SERVICE) as AlarmManager
98 | am.cancel(pendingIntent)
99 |
100 | pendingIntent.cancel()
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/playbar/PlaybarViewModel.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.playbar;
2 |
3 | import android.app.Activity;
4 | import android.arch.lifecycle.ViewModel;
5 | import android.os.Bundle;
6 | import android.os.SystemClock;
7 | import android.support.v4.media.session.MediaControllerCompat;
8 | import android.support.v4.media.session.PlaybackStateCompat;
9 |
10 | import com.koalatea.thehollidayinn.softwareengineeringdaily.podcast.PodcastSessionStateManager;
11 |
12 | /**
13 | * Created by keithholliday on 2/6/18.
14 | */
15 |
16 | public class PlaybarViewModel extends ViewModel {
17 | public void sendSpeedChangeIntent(int currentSpeed, Activity activity) {
18 | MediaControllerCompat controller = MediaControllerCompat.getMediaController(activity);
19 | if (controller != null) {
20 | Bundle args = new Bundle();
21 | args.putInt("SPEED", currentSpeed);
22 | // @TODO: Make constant
23 | controller.getTransportControls().sendCustomAction("SPEED_CHANGE", args);
24 | }
25 | }
26 |
27 | public long setListenedProgress(PlaybackStateCompat mLastPlaybackState) {
28 | long currentPosition = mLastPlaybackState.getPosition();
29 | if (mLastPlaybackState.getState() == PlaybackStateCompat.STATE_PLAYING) {
30 | // Calculate the elapsed time between the last position update and now and unless
31 | // paused, we can assume (delta * speed) + current position is approximately the
32 | // latest position. This ensure that we do not repeatedly call the getPlaybackState()
33 | // on MediaControllerCompat.
34 | long timeDelta = SystemClock.elapsedRealtime() -
35 | mLastPlaybackState.getLastPositionUpdateTime();
36 | currentPosition += (int) timeDelta * mLastPlaybackState.getPlaybackSpeed();
37 | }
38 |
39 | // Save progress for episode
40 | String postTile = PodcastSessionStateManager.getInstance().getCurrentTitle();
41 | if (!postTile.isEmpty()) {
42 | PodcastSessionStateManager.getInstance().setProgressForEpisode(postTile, currentPosition);
43 | }
44 |
45 | return currentPosition;
46 | }
47 |
48 | public void playPause(MediaControllerCompat controller) {
49 | PlaybackStateCompat stateObj = controller.getPlaybackState();
50 | final int state = stateObj == null ? PlaybackStateCompat.STATE_NONE : stateObj.getState();
51 |
52 | if (state == PlaybackStateCompat.STATE_PAUSED ||
53 | state == PlaybackStateCompat.STATE_STOPPED ||
54 | state == PlaybackStateCompat.STATE_NONE) {
55 | playMedia(controller);
56 | } else if (state == PlaybackStateCompat.STATE_PLAYING ||
57 | state == PlaybackStateCompat.STATE_BUFFERING ||
58 | state == PlaybackStateCompat.STATE_CONNECTING) {
59 | pauseMedia(controller);
60 | }
61 | }
62 |
63 | private void playMedia(MediaControllerCompat controller) {
64 | if (controller != null) {
65 | controller.getTransportControls().play();
66 | }
67 | }
68 |
69 | private void pauseMedia(MediaControllerCompat controller) {
70 | if (controller != null) {
71 | controller.getTransportControls().pause();
72 | }
73 | }
74 |
75 | public void back15(Activity activity) {
76 | movePlayback(activity, "MOVE_BACK");
77 | }
78 |
79 | public void skip15(Activity activity) {
80 | movePlayback(activity, "MOVE_FORWARD");
81 | }
82 |
83 | private void movePlayback(Activity activity, String action) {
84 | MediaControllerCompat controller = MediaControllerCompat.getMediaController(activity);
85 | if (controller == null) return;
86 |
87 | Bundle args = new Bundle();
88 | args.putInt("DISTANCE", 15000);
89 | // @TODO: Make constant
90 | controller.getTransportControls().sendCustomAction(action, args);
91 |
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/podcast/PodcastAdapter.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.podcast;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.ImageView;
8 | import android.widget.TextView;
9 |
10 | import com.koalatea.thehollidayinn.softwareengineeringdaily.R;
11 | import com.koalatea.thehollidayinn.softwareengineeringdaily.app.SEDApp;
12 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.models.Post;
13 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.models.Title;
14 | import com.squareup.picasso.Picasso;
15 |
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 | import butterknife.BindView;
20 | import butterknife.ButterKnife;
21 | import io.reactivex.Observable;
22 | import io.reactivex.subjects.PublishSubject;
23 |
24 | /*
25 | * Created by krh12 on 5/22/2017.
26 | */
27 |
28 | class PodcastAdapter extends RecyclerView.Adapter {
29 | private List posts = new ArrayList<>();
30 | private final PublishSubject onClickSubject = PublishSubject.create();
31 |
32 | static class ViewHolder extends RecyclerView.ViewHolder {
33 | @BindView(R.id.card_title)
34 | TextView mTextView;
35 |
36 | @BindView(R.id.card_image)
37 | ImageView imageView;
38 |
39 | // @BindView(R.id.card_desc)
40 | // TextView description;
41 |
42 | private ViewHolder(View v) {
43 | super(v);
44 | ButterKnife.bind(this, v);
45 | }
46 | }
47 |
48 | void setPosts(List posts) {
49 | this.posts = posts;
50 | this.notifyDataSetChanged();
51 | }
52 |
53 | @Override
54 | public PodcastAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
55 | int viewType) {
56 | final View view = LayoutInflater.from(parent.getContext())
57 | .inflate(R.layout.fragment_podcast_grid, parent, false);
58 |
59 | final ViewHolder viewHolder = new ViewHolder(view);
60 |
61 | view.setOnClickListener(v -> {
62 | final int position = viewHolder.getAdapterPosition();
63 | Post post = posts.get(position);
64 |
65 | // @TODO: How to pass text view as well?
66 | onClickSubject.onNext(post);
67 | });
68 |
69 | return viewHolder;
70 | }
71 |
72 | @Override
73 | public void onBindViewHolder(ViewHolder holder, int position) {
74 | Post post = posts.get(position);
75 | Title postTitle = post.getTitle();
76 |
77 | if (postTitle == null) return;
78 |
79 | holder.mTextView.setText(postTitle.getRendered());
80 | // holder.description.setText(postTitle.getRendered());
81 |
82 | String imageLink = "https://softwareengineeringdaily.com/wp-content/uploads/2015/08/sed21.png";
83 | if (post.getFeaturedImage() != null) {
84 | imageLink = post.getFeaturedImage();
85 | }
86 |
87 | Picasso.with(SEDApp.component.context())
88 | .load(imageLink)
89 | .resize(100, 100)
90 | .centerCrop()
91 | .into(holder.imageView);
92 | }
93 |
94 | @Override
95 | public int getItemCount() {
96 | return posts.size();
97 | }
98 |
99 | public Observable getPositionClicks() {
100 | return onClickSubject;
101 | }
102 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/podcast/PodcastListViewModel.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.podcast;
2 |
3 | import android.arch.lifecycle.MutableLiveData;
4 | import android.arch.lifecycle.ViewModel;
5 | import android.util.Log;
6 |
7 | import com.koalatea.thehollidayinn.softwareengineeringdaily.app.SEDApp;
8 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.models.Post;
9 | import com.koalatea.thehollidayinn.softwareengineeringdaily.data.remote.APIInterface;
10 | import com.koalatea.thehollidayinn.softwareengineeringdaily.repositories.PostRepository;
11 | import com.koalatea.thehollidayinn.softwareengineeringdaily.repositories.UserRepository;
12 |
13 | import java.util.HashMap;
14 | import java.util.List;
15 | import java.util.Map;
16 |
17 | import io.reactivex.Observable;
18 | import io.reactivex.android.schedulers.AndroidSchedulers;
19 | import io.reactivex.observers.DisposableObserver;
20 | import io.reactivex.schedulers.Schedulers;
21 |
22 | /**
23 | * Created by keithholliday on 12/22/17.
24 | */
25 |
26 | public class PodcastListViewModel extends ViewModel {
27 | private MutableLiveData> postList;
28 |
29 | public PodcastListViewModel(){
30 | postList = new MutableLiveData<>();
31 | }
32 |
33 | public MutableLiveData> getPostList() {
34 | return this.postList;
35 | }
36 |
37 | public void setPostList(List postList) {
38 | this.postList.postValue(postList);
39 | }
40 |
41 | public void getPosts(String search, String title, String tagId) {
42 | APIInterface mService = SEDApp.component.kibblService();
43 |
44 | // @TODO: Replace tmp with query
45 |
46 | Map data = new HashMap<>();
47 | Observable> query = mService.getPosts(data);
48 |
49 | UserRepository userRepository = UserRepository.getInstance(SEDApp.component().context());
50 | final PostRepository postRepository = PostRepository.getInstance();
51 |
52 | if (title != null && title.equals("Greatest Hits")) {
53 | data.put("type", "top");
54 | } else if (title != null && title.equals("Just For You") && !userRepository.getToken().isEmpty()) {
55 | query = mService.getRecommendations(data);
56 | } else if (tagId != null && !tagId.isEmpty()) {
57 | data.put("categories", tagId);
58 | }
59 |
60 | if (!search.isEmpty()) {
61 | data.put("search", search);
62 | }
63 |
64 | query
65 | .subscribeOn(Schedulers.io())
66 | .observeOn(AndroidSchedulers.mainThread())
67 | .subscribe(new DisposableObserver>() {
68 | @Override
69 | public void onComplete() {
70 | }
71 |
72 | @Override
73 | public void onError(Throwable e) {
74 | // Log.v(TAG, e.toString());
75 | }
76 |
77 | @Override
78 | public void onNext(List posts) {
79 | setPostList(posts);
80 | postRepository.setPosts(posts);
81 | }
82 | });
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/com/koalatea/thehollidayinn/softwareengineeringdaily/podcast/PodcastSessionStateManager.java:
--------------------------------------------------------------------------------
1 | package com.koalatea.thehollidayinn.softwareengineeringdaily.podcast;
2 |
3 | import android.content.SharedPreferences;
4 | import android.preference.PreferenceManager;
5 | import android.support.v4.media.MediaMetadataCompat;
6 | import android.support.v4.media.session.PlaybackStateCompat;
7 | import android.util.Log;
8 |
9 | import com.google.gson.Gson;
10 | import com.google.gson.GsonBuilder;
11 | import com.google.gson.reflect.TypeToken;
12 | import com.koalatea.thehollidayinn.softwareengineeringdaily.app.SEDApp;
13 |
14 | import java.lang.reflect.Type;
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import io.reactivex.Observable;
19 | import io.reactivex.subjects.PublishSubject;
20 |
21 | /*
22 | * Created by keithholliday on 9/30/17.
23 | */
24 |
25 | // @TODO: This should probably be a viewmodal
26 | public class PodcastSessionStateManager {
27 | private static PodcastSessionStateManager instance = null;
28 |
29 | private final PublishSubject speedChangeObservable = PublishSubject.create();
30 | private final PublishSubject mediaMetaDataChange = PublishSubject.create();
31 |
32 | private final String PROGRESS_KEY = "sedaily-progress-key";
33 | private final SharedPreferences preferences;
34 | private Gson gson;
35 |
36 | private String currentTitle = "";
37 | private long previousSave = 0;
38 | private long currentProgress = 0;
39 | private int currentSpeed = 0;
40 | private MediaMetadataCompat mediaMetadataCompat;
41 | private Map episodeProgress;
42 |
43 | private PlaybackStateCompat lastPlaybackState;
44 |
45 | private PodcastSessionStateManager() {
46 | episodeProgress = new HashMap<>();
47 | preferences = PreferenceManager.getDefaultSharedPreferences(SEDApp.component().context());
48 | gson = new GsonBuilder().create();
49 | String progressString = preferences.getString(PROGRESS_KEY, "");
50 | if (!progressString.isEmpty()) {
51 | Type typeOfHashMap = new TypeToken