├── .gitignore ├── LICENSE ├── PagerSlidingTabStrip ├── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── project.properties ├── res │ ├── drawable │ │ └── background_tab.xml │ └── values │ │ ├── attrs.xml │ │ └── colors.xml └── src │ └── com │ └── astuetz │ └── PagerSlidingTabStrip.java ├── README.md ├── app ├── .gitignore ├── build.gradle ├── libs │ ├── cling-core-2.0.1-sources.jar │ ├── cling-core-2.0.1.jar │ ├── cling-support-2.0.1-sources.jar │ ├── cling-support-2.0.1.jar │ ├── jetty-client-8.1.12.v20130726-sources.jar │ ├── jetty-client-8.1.12.v20130726.jar │ ├── jetty-continuation-8.1.12.v20130726-sources.jar │ ├── jetty-continuation-8.1.12.v20130726.jar │ ├── jetty-http-8.1.12.v20130726-sources.jar │ ├── jetty-http-8.1.12.v20130726.jar │ ├── jetty-io-8.1.12.v20130726-sources.jar │ ├── jetty-io-8.1.12.v20130726.jar │ ├── jetty-security-8.1.12.v20130726-sources.jar │ ├── jetty-security-8.1.12.v20130726.jar │ ├── jetty-server-8.1.12.v20130726-sources.jar │ ├── jetty-server-8.1.12.v20130726.jar │ ├── jetty-servlet-8.1.12.v20130726-sources.jar │ ├── jetty-servlet-8.1.12.v20130726.jar │ ├── jetty-util-8.1.12.v20130726-sources.jar │ ├── jetty-util-8.1.12.v20130726.jar │ ├── seamless-http-1.1.0.jar │ ├── seamless-util-1.1.0.jar │ ├── seamless-xml-1.1.0.jar │ ├── servlet-api-3.0.jar │ └── slf4j-jdk14-1.7.5.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── kevinshen │ │ └── beyondupnp │ │ ├── ApplicationTest.java │ │ └── MainActivityTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── kevinshen │ │ └── beyondupnp │ │ ├── BeyondApplication.java │ │ ├── Intents.java │ │ ├── core │ │ ├── MediaPlayerController.java │ │ ├── PlaybackCommand.java │ │ ├── SystemManager.java │ │ ├── server │ │ │ ├── AudioResourceServlet.java │ │ │ ├── JettyResourceServer.java │ │ │ └── VideoResourceServlet.java │ │ └── upnp │ │ │ ├── AndroidJettyServletContainer.java │ │ │ └── BeyondContentDirectoryService.java │ │ ├── database │ │ ├── BeyondUpnpContract.java │ │ ├── MediaResourceDao.java │ │ ├── SQLiteHelper.java │ │ └── SystemProvider.java │ │ ├── service │ │ ├── BeyondUpnpService.java │ │ └── SystemService.java │ │ ├── ui │ │ ├── ContentContainerActivity.java │ │ ├── DeviceListDialogFragment.java │ │ ├── LibraryFragment.java │ │ ├── MainActivity.java │ │ ├── NowplayingFragment.java │ │ └── PlaylistFragment.java │ │ └── util │ │ ├── MD5.java │ │ └── Utils.java │ └── res │ ├── color │ ├── listview_item_bg_color.xml │ └── tab_text_color.xml │ ├── drawable-hdpi │ ├── actionbar.9.png │ ├── appwidget_bg.9.png │ ├── appwidget_bg_holo.9.png │ ├── bar_graph.9.png │ ├── beyond_holo_light_favorite_normal.png │ ├── beyond_holo_light_favorite_selected.png │ ├── beyond_holo_light_next.png │ ├── beyond_holo_light_overflow.png │ ├── beyond_holo_light_pause.png │ ├── beyond_holo_light_play.png │ ├── beyond_holo_light_previous.png │ ├── beyond_holo_light_repeat_all.png │ ├── beyond_holo_light_repeat_normal.png │ ├── beyond_holo_light_repeat_one.png │ ├── beyond_holo_light_search.png │ ├── beyond_holo_light_shuffle_normal.png │ ├── beyond_holo_light_shuffle_on.png │ ├── beyond_settings_themes.png │ ├── btn_drag_sort.png │ ├── btn_switch_queue.png │ ├── btn_switch_queue_active.png │ ├── dropdown_ic_arrow_normal_holo_light.png │ ├── list_section_divider_holo_custom.9.png │ ├── notify_panel_notification_icon_bg.png │ ├── queue_thumbnail_bg.9.png │ ├── recents_thumbnail_bg_press.9.png │ ├── scrubber_primary_holo.9.png │ ├── scrubber_secondary_holo.9.png │ ├── scrubber_track_holo_dark.9.png │ ├── stat_notify_music.png │ ├── tab_selected_holo.9.png │ ├── tab_selected_pressed_focused_holo.9.png │ ├── tab_selected_pressed_holo.9.png │ ├── tab_unselected_focused_holo.9.png │ ├── tab_unselected_holo.9.png │ ├── tab_unselected_pressed_holo.9.png │ ├── title_bar_shadow.9.png │ ├── view_more_album.png │ └── view_more_song.png │ ├── drawable-mdpi │ ├── actionbar.9.png │ ├── appwidget_bg.9.png │ ├── beyond_holo_light_favorite_normal.png │ ├── beyond_holo_light_favorite_selected.png │ ├── beyond_holo_light_next.png │ ├── beyond_holo_light_overflow.png │ ├── beyond_holo_light_pause.png │ ├── beyond_holo_light_play.png │ ├── beyond_holo_light_previous.png │ ├── beyond_holo_light_repeat_all.png │ ├── beyond_holo_light_repeat_normal.png │ ├── beyond_holo_light_repeat_one.png │ ├── beyond_holo_light_search.png │ ├── beyond_holo_light_shuffle_normal.png │ ├── beyond_holo_light_shuffle_on.png │ ├── beyond_settings_themes.png │ ├── btn_drag_sort.png │ ├── btn_switch_queue.png │ ├── btn_switch_queue_active.png │ ├── dropdown_ic_arrow_normal_holo_light.png │ ├── list_section_divider_holo_custom.9.png │ ├── notify_panel_notification_icon_bg.png │ ├── queue_thumbnail_bg.9.png │ ├── recents_thumbnail_bg_press.9.png │ ├── scrubber_primary_holo.9.png │ ├── scrubber_secondary_holo.9.png │ ├── scrubber_track_holo_dark.9.png │ ├── stat_notify_music.png │ ├── tab_selected_holo.9.png │ ├── tab_selected_pressed_focused_holo.9.png │ ├── tab_selected_pressed_holo.9.png │ ├── tab_unselected_focused_holo.9.png │ ├── tab_unselected_holo.9.png │ ├── tab_unselected_pressed_holo.9.png │ ├── title_bar_shadow.9.png │ ├── view_more_album.png │ └── view_more_song.png │ ├── drawable-xhdpi │ ├── actionbar.9.png │ ├── appwidget_bg.9.png │ ├── beyond_holo_light_favorite_normal.png │ ├── beyond_holo_light_favorite_selected.png │ ├── beyond_holo_light_next.png │ ├── beyond_holo_light_overflow.png │ ├── beyond_holo_light_pause.png │ ├── beyond_holo_light_play.png │ ├── beyond_holo_light_previous.png │ ├── beyond_holo_light_repeat_all.png │ ├── beyond_holo_light_repeat_normal.png │ ├── beyond_holo_light_repeat_one.png │ ├── beyond_holo_light_search.png │ ├── beyond_holo_light_shuffle_normal.png │ ├── beyond_holo_light_shuffle_on.png │ ├── beyond_settings_themes.png │ ├── btn_drag_sort.png │ ├── btn_switch_queue.png │ ├── btn_switch_queue_active.png │ ├── dropdown_ic_arrow_normal_holo_light.png │ ├── ic_launcher.png │ ├── ic_logo.png │ ├── list_section_divider_holo_custom.9.png │ ├── notify_panel_notification_icon_bg.png │ ├── queue_thumbnail_bg.9.png │ ├── recents_thumbnail_bg_press.9.png │ ├── scrubber_primary_holo.9.png │ ├── scrubber_secondary_holo.9.png │ ├── scrubber_track_holo_dark.9.png │ ├── stat_notify_music.png │ ├── tab_selected_holo.9.png │ ├── tab_selected_pressed_focused_holo.9.png │ ├── tab_selected_pressed_holo.9.png │ ├── tab_unselected_focused_holo.9.png │ ├── tab_unselected_holo.9.png │ ├── tab_unselected_pressed_holo.9.png │ ├── title_bar_shadow.9.png │ ├── view_more_album.png │ └── view_more_song.png │ ├── drawable │ ├── audio_player_seekbar.xml │ ├── colorstrip_shadow.9.png │ ├── gridview_item_background.xml │ ├── holo_selector.xml │ ├── ic_action_dock.png │ ├── listview_item_background.xml │ ├── no_art_normal.png │ ├── no_art_small.png │ ├── promo.png │ ├── queue_thumbnail_fg.xml │ ├── shuffle_background.xml │ ├── status_bg.xml │ ├── tab.xml │ ├── view_more.xml │ └── viewpager_margin.xml │ ├── layout │ ├── activity_content_container.xml │ ├── activity_main.xml │ ├── audio_controls.xml │ ├── context_menu.xml │ ├── fragment_device_list_dialog.xml │ ├── fragment_library.xml │ ├── fragment_nowplaying.xml │ ├── fragment_playlist.xml │ ├── list_separator.xml │ ├── listview.xml │ ├── listview_items.xml │ ├── listview_items_playlist.xml │ └── shadow.xml │ ├── menu │ ├── menu_content_container.xml │ ├── menu_item_actions.xml │ ├── menu_list_multi_select.xml │ └── menu_main.xml │ └── values │ ├── colors.xml │ ├── config.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── import-summary.txt └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | build/ 16 | .gradle/ 17 | 18 | # Project files 19 | *.iml 20 | .idea 21 | # .idea/workspace.xml 22 | 23 | # Local configuration file (sdk path, etc) 24 | local.properties 25 | keystore.properties 26 | 27 | # Windows thumbnail db 28 | .DS_Store 29 | 30 | # Idea non-crucial project fileS 31 | *.iws 32 | 33 | # Sandbox stuff 34 | _sandbox 35 | 36 | -------------------------------------------------------------------------------- /PagerSlidingTabStrip/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /PagerSlidingTabStrip/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'android-library' 2 | 3 | dependencies { 4 | compile 'com.android.support:support-v4:19.0.0' 5 | } 6 | 7 | android { 8 | compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) 9 | buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION 10 | 11 | defaultConfig { 12 | minSdkVersion 8 13 | targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) 14 | } 15 | 16 | sourceSets { 17 | main { 18 | manifest.srcFile 'AndroidManifest.xml' 19 | java.srcDirs = ['src'] 20 | res.srcDirs = ['res'] 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_7 25 | targetCompatibility JavaVersion.VERSION_1_7 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /PagerSlidingTabStrip/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=PagerSlidingTabStrip Library 2 | POM_ARTIFACT_ID=pagerslidingtabstrip 3 | POM_PACKAGING=aar -------------------------------------------------------------------------------- /PagerSlidingTabStrip/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-17 15 | android.library=true 16 | -------------------------------------------------------------------------------- /PagerSlidingTabStrip/res/drawable/background_tab.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PagerSlidingTabStrip/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /PagerSlidingTabStrip/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6633B5E5 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BeyondUPnP 2 | 3 | BeyondUPnP is an UPnP control point(DMC),Digital Media Server(Jetty Server) application for android. 4 | 5 | It discover the UPnP device,content provider and renderer.It allow you to browse and select the media you want to play on your television,TVBox or any UPnP renderer compatible device. 6 | 7 | ###Screenshot 8 | ![Screenshot](http://ww2.sinaimg.cn/mw690/7832b683gw1eop3lzts9zj206o0bjmxe.jpg) 9 | 10 | ###Libraries Used 11 | * Cling - https://github.com/4thline/cling 12 | * PagerSlidingTabStrip - https://github.com/astuetz/PagerSlidingTabStrip 13 | * Android Support Library - http://developer.android.com/tools/support-library/index.html 14 | 15 | ###License 16 | 17 | Copyright 2014 Kevin Shen 18 | 19 | Licensed under the Apache License, Version 2.0 (the "License"); 20 | you may not use this file except in compliance with the License. 21 | You may obtain a copy of the License at 22 | 23 | http://www.apache.org/licenses/LICENSE-2.0 24 | 25 | Unless required by applicable law or agreed to in writing, software 26 | distributed under the License is distributed on an "AS IS" BASIS, 27 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28 | See the License for the specific language governing permissions and 29 | limitations under the License. 30 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) 5 | buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION 6 | 7 | defaultConfig { 8 | applicationId "com.kevinshen.beyondupnp" 9 | minSdkVersion 14 10 | targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) 11 | versionCode 1 12 | versionName "1.0.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | compileOptions { 21 | sourceCompatibility JavaVersion.VERSION_1_7 22 | targetCompatibility JavaVersion.VERSION_1_7 23 | } 24 | packagingOptions { 25 | exclude 'META-INF/LICENSE.txt' 26 | exclude 'META-INF/beans.xml' 27 | } 28 | } 29 | 30 | dependencies { 31 | //Cling library 32 | compile files('libs/cling-core-2.0.1.jar') 33 | compile files('libs/cling-support-2.0.1.jar') 34 | compile files('libs/seamless-http-1.1.0.jar') 35 | compile files('libs/seamless-util-1.1.0.jar') 36 | compile files('libs/seamless-xml-1.1.0.jar') 37 | //Jetty library 38 | compile files('libs/jetty-client-8.1.12.v20130726.jar') 39 | compile files('libs/jetty-continuation-8.1.12.v20130726.jar') 40 | compile files('libs/jetty-http-8.1.12.v20130726.jar') 41 | compile files('libs/jetty-io-8.1.12.v20130726.jar') 42 | compile files('libs/jetty-security-8.1.12.v20130726.jar') 43 | compile files('libs/jetty-server-8.1.12.v20130726.jar') 44 | compile files('libs/jetty-servlet-8.1.12.v20130726.jar') 45 | compile files('libs/jetty-util-8.1.12.v20130726.jar') 46 | compile files('libs/servlet-api-3.0.jar') 47 | compile files('libs/slf4j-jdk14-1.7.5.jar') 48 | //Android 49 | compile 'com.android.support:support-v4:21.0.+' 50 | compile 'com.android.support:support-v13:21.0.0' 51 | 52 | compile project(':PagerSlidingTabStrip') 53 | } 54 | -------------------------------------------------------------------------------- /app/libs/cling-core-2.0.1-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/cling-core-2.0.1-sources.jar -------------------------------------------------------------------------------- /app/libs/cling-core-2.0.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/cling-core-2.0.1.jar -------------------------------------------------------------------------------- /app/libs/cling-support-2.0.1-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/cling-support-2.0.1-sources.jar -------------------------------------------------------------------------------- /app/libs/cling-support-2.0.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/cling-support-2.0.1.jar -------------------------------------------------------------------------------- /app/libs/jetty-client-8.1.12.v20130726-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-client-8.1.12.v20130726-sources.jar -------------------------------------------------------------------------------- /app/libs/jetty-client-8.1.12.v20130726.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-client-8.1.12.v20130726.jar -------------------------------------------------------------------------------- /app/libs/jetty-continuation-8.1.12.v20130726-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-continuation-8.1.12.v20130726-sources.jar -------------------------------------------------------------------------------- /app/libs/jetty-continuation-8.1.12.v20130726.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-continuation-8.1.12.v20130726.jar -------------------------------------------------------------------------------- /app/libs/jetty-http-8.1.12.v20130726-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-http-8.1.12.v20130726-sources.jar -------------------------------------------------------------------------------- /app/libs/jetty-http-8.1.12.v20130726.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-http-8.1.12.v20130726.jar -------------------------------------------------------------------------------- /app/libs/jetty-io-8.1.12.v20130726-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-io-8.1.12.v20130726-sources.jar -------------------------------------------------------------------------------- /app/libs/jetty-io-8.1.12.v20130726.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-io-8.1.12.v20130726.jar -------------------------------------------------------------------------------- /app/libs/jetty-security-8.1.12.v20130726-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-security-8.1.12.v20130726-sources.jar -------------------------------------------------------------------------------- /app/libs/jetty-security-8.1.12.v20130726.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-security-8.1.12.v20130726.jar -------------------------------------------------------------------------------- /app/libs/jetty-server-8.1.12.v20130726-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-server-8.1.12.v20130726-sources.jar -------------------------------------------------------------------------------- /app/libs/jetty-server-8.1.12.v20130726.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-server-8.1.12.v20130726.jar -------------------------------------------------------------------------------- /app/libs/jetty-servlet-8.1.12.v20130726-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-servlet-8.1.12.v20130726-sources.jar -------------------------------------------------------------------------------- /app/libs/jetty-servlet-8.1.12.v20130726.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-servlet-8.1.12.v20130726.jar -------------------------------------------------------------------------------- /app/libs/jetty-util-8.1.12.v20130726-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-util-8.1.12.v20130726-sources.jar -------------------------------------------------------------------------------- /app/libs/jetty-util-8.1.12.v20130726.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/jetty-util-8.1.12.v20130726.jar -------------------------------------------------------------------------------- /app/libs/seamless-http-1.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/seamless-http-1.1.0.jar -------------------------------------------------------------------------------- /app/libs/seamless-util-1.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/seamless-util-1.1.0.jar -------------------------------------------------------------------------------- /app/libs/seamless-xml-1.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/seamless-xml-1.1.0.jar -------------------------------------------------------------------------------- /app/libs/servlet-api-3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/servlet-api-3.0.jar -------------------------------------------------------------------------------- /app/libs/slf4j-jdk14-1.7.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/libs/slf4j-jdk14-1.7.5.jar -------------------------------------------------------------------------------- /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 C:\Work\Android\tools\android-sdk-x86_64\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 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/kevinshen/beyondupnp/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.kevinshen.beyondupnp; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | import android.util.Log; 6 | 7 | /** 8 | * Testing Fundamentals 9 | */ 10 | public class ApplicationTest extends ApplicationTestCase { 11 | public ApplicationTest() { 12 | super(Application.class); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/kevinshen/beyondupnp/MainActivityTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.kevinshen.beyondupnp; 18 | 19 | import android.test.ActivityInstrumentationTestCase2; 20 | 21 | import com.kevinshen.beyondupnp.ui.MainActivity; 22 | 23 | /** 24 | * Make sure that the main launcher activity opens up properly, which will be 25 | * verified by {@link #testActivityTestCaseSetUpProperly}. 26 | */ 27 | public class MainActivityTest extends ActivityInstrumentationTestCase2 { 28 | 29 | public MainActivityTest() { 30 | super(MainActivity.class); 31 | } 32 | 33 | /** 34 | * Verifies that activity under test can be launched. 35 | */ 36 | public void testActivityTestCaseSetUpProperly() { 37 | assertNotNull("activity should be launched successfully", getActivity()); 38 | } 39 | 40 | public void testStringEmpty() { 41 | assertSame("",""); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 44 | 45 | 48 | 49 | 53 | 54 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/BeyondApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp; 17 | 18 | import android.app.Application; 19 | 20 | import com.kevinshen.beyondupnp.core.server.JettyResourceServer; 21 | 22 | import java.util.concurrent.ExecutorService; 23 | import java.util.concurrent.Executors; 24 | 25 | public class BeyondApplication extends Application { 26 | private static BeyondApplication sBeyondApplication = null; 27 | private ExecutorService mThreadPool = Executors.newCachedThreadPool(); 28 | private JettyResourceServer mJettyResourceServer; 29 | 30 | @Override 31 | public void onCreate() { 32 | super.onCreate(); 33 | 34 | sBeyondApplication = this; 35 | 36 | mJettyResourceServer = new JettyResourceServer(); 37 | mThreadPool.execute(mJettyResourceServer); 38 | } 39 | 40 | synchronized public static BeyondApplication getApplication() { 41 | return sBeyondApplication; 42 | } 43 | 44 | synchronized public void stopServer() { 45 | mJettyResourceServer.stopIfRunning(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/Intents.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp; 17 | 18 | import android.content.Intent; 19 | 20 | import java.io.Serializable; 21 | 22 | public class Intents { 23 | /** 24 | * Prefix for all intents created 25 | */ 26 | public static final String INTENT_PREFIX = "com.kevinshen.beyondupup."; 27 | 28 | /** 29 | * Prefix for all extra data added to intents 30 | */ 31 | public static final String INTENT_EXTRA_PREFIX = INTENT_PREFIX + "extra."; 32 | 33 | /** 34 | * Prefix for all action in intents 35 | */ 36 | public static final String INTENT_ACTION_PREFIX = INTENT_PREFIX + "action."; 37 | 38 | /** 39 | * Playing action for MediaPlayer 40 | */ 41 | public static final String ACTION_PLAYING = INTENT_ACTION_PREFIX + "playing"; 42 | 43 | /** 44 | * Paused playback action for MediaPlayer 45 | */ 46 | public static final String ACTION_PAUSED_PLAYBACK = INTENT_ACTION_PREFIX + "paused_playback"; 47 | 48 | /** 49 | * Stopped action for MediaPlayer 50 | */ 51 | public static final String ACTION_STOPPED = INTENT_ACTION_PREFIX + "stopped"; 52 | 53 | /** 54 | * Change device action for MediaPlayer 55 | */ 56 | public static final String ACTION_CHANGE_DEVICE = INTENT_ACTION_PREFIX + "change_device"; 57 | 58 | /** 59 | * Set volume action for MediaPlayer 60 | */ 61 | public static final String ACTION_SET_VOLUME = INTENT_ACTION_PREFIX + "set_volume"; 62 | 63 | /** 64 | * Update the lastChange value action for MediaPlayer 65 | */ 66 | public static final String ACTION_UPDATE_LAST_CHANGE = INTENT_ACTION_PREFIX + "update_last_change"; 67 | 68 | /** 69 | * Builder for generating an intent configured with extra data. 70 | */ 71 | public static class Builder { 72 | 73 | private final Intent intent; 74 | 75 | /** 76 | * Create builder with suffix 77 | * 78 | * @param actionSuffix 79 | */ 80 | public Builder(String actionSuffix) { 81 | intent = new Intent(INTENT_PREFIX + actionSuffix); 82 | } 83 | 84 | /** 85 | * Add extra field data value to intent being built up 86 | * 87 | * @param fieldName 88 | * @param value 89 | * @return this builder 90 | */ 91 | public Builder add(String fieldName, String value) { 92 | intent.putExtra(fieldName, value); 93 | return this; 94 | } 95 | 96 | /** 97 | * Add extra field data values to intent being built up 98 | * 99 | * @param fieldName 100 | * @param values 101 | * @return this builder 102 | */ 103 | public Builder add(String fieldName, CharSequence[] values) { 104 | intent.putExtra(fieldName, values); 105 | return this; 106 | } 107 | 108 | /** 109 | * Add extra field data value to intent being built up 110 | * 111 | * @param fieldName 112 | * @param value 113 | * @return this builder 114 | */ 115 | public Builder add(String fieldName, int value) { 116 | intent.putExtra(fieldName, value); 117 | return this; 118 | } 119 | 120 | /** 121 | * Add extra field data value to intent being built up 122 | * 123 | * @param fieldName 124 | * @param values 125 | * @return this builder 126 | */ 127 | public Builder add(String fieldName, int[] values) { 128 | intent.putExtra(fieldName, values); 129 | return this; 130 | } 131 | 132 | /** 133 | * Add extra field data value to intent being built up 134 | * 135 | * @param fieldName 136 | * @param values 137 | * @return this builder 138 | */ 139 | public Builder add(String fieldName, boolean[] values) { 140 | intent.putExtra(fieldName, values); 141 | return this; 142 | } 143 | 144 | /** 145 | * Add extra field data value to intent being built up 146 | * 147 | * @param fieldName 148 | * @param value 149 | * @return this builder 150 | */ 151 | public Builder add(String fieldName, Serializable value) { 152 | intent.putExtra(fieldName, value); 153 | return this; 154 | } 155 | 156 | /** 157 | * Get built intent 158 | * 159 | * @return intent 160 | */ 161 | public Intent toIntent() { 162 | return intent; 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/core/MediaPlayerController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.core; 17 | 18 | import android.os.Handler; 19 | import android.util.Log; 20 | 21 | import org.fourthline.cling.support.model.TransportState; 22 | 23 | import java.util.concurrent.ExecutionException; 24 | import java.util.concurrent.ExecutorService; 25 | import java.util.concurrent.Executors; 26 | 27 | public class MediaPlayerController { 28 | private static final String TAG = MediaPlayerController.class.getSimpleName(); 29 | 30 | private TransportState mCurrentState = TransportState.STOPPED; 31 | private ExecutorService mMediaExecutorService = Executors.newSingleThreadExecutor(); 32 | private Object mPlaybackLock = new Object(); 33 | private volatile boolean isPaused = true; 34 | 35 | public MediaPlayerController(final Handler handler){ 36 | //Create SeekBar sync thread,mSeekFuture will be set null when press stop. 37 | mMediaExecutorService.execute(new Runnable() { 38 | @Override 39 | public void run() { 40 | Log.i(TAG, "Create SeekBar sync thread."); 41 | while (true) { 42 | synchronized (mPlaybackLock) { 43 | try { 44 | if (isPaused) mPlaybackLock.wait(); 45 | 46 | PlaybackCommand.getPositionInfo(handler); 47 | Thread.sleep(2000); 48 | } catch (InterruptedException e) { 49 | Log.e(TAG, "MediaPlayer shutdown"); 50 | } 51 | } 52 | } 53 | } 54 | }); 55 | } 56 | 57 | public TransportState getCurrentState() { 58 | return mCurrentState; 59 | } 60 | 61 | public void setCurrentState(TransportState currentState) { 62 | if (this.mCurrentState != currentState) { 63 | this.mCurrentState = currentState; 64 | } 65 | } 66 | 67 | public void startUpdateSeekBar() throws InterruptedException, ExecutionException { 68 | Log.i(TAG, "Execute startUpdateSeekBar"); 69 | if (this.mCurrentState == TransportState.PLAYING && isPaused) { 70 | synchronized (mPlaybackLock) { 71 | isPaused = false; 72 | Log.i(TAG, "Resume seekbar sync thread."); 73 | mPlaybackLock.notifyAll(); 74 | } 75 | } 76 | } 77 | 78 | public void pauseUpdateSeekBar() throws InterruptedException { 79 | Log.i(TAG, "Execute pauseUpdateSeekBar"); 80 | isPaused = true; 81 | } 82 | 83 | public boolean seekbarIsPaused(){ 84 | return isPaused; 85 | } 86 | 87 | public void destroy() { 88 | mMediaExecutorService.shutdownNow(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/core/SystemManager.java: -------------------------------------------------------------------------------- 1 | package com.kevinshen.beyondupnp.core; 2 | 3 | import com.kevinshen.beyondupnp.service.BeyondUpnpService; 4 | import com.kevinshen.beyondupnp.service.SystemService; 5 | 6 | import org.fourthline.cling.controlpoint.ControlPoint; 7 | import org.fourthline.cling.model.meta.Device; 8 | import org.fourthline.cling.model.types.DeviceType; 9 | import org.fourthline.cling.model.types.ServiceType; 10 | import org.fourthline.cling.model.types.UDADeviceType; 11 | import org.fourthline.cling.model.types.UDAServiceType; 12 | import org.fourthline.cling.registry.Registry; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Collection; 16 | import java.util.Collections; 17 | import java.util.List; 18 | 19 | /** 20 | * 21 | */ 22 | public class SystemManager { 23 | private static final String TAG = SystemManager.class.getSimpleName(); 24 | public static final ServiceType CONTENT_DIRECTORY_SERVICE = new UDAServiceType("ContentDirectory"); 25 | public static final ServiceType AV_TRANSPORT_SERVICE = new UDAServiceType("AVTransport"); 26 | public static final ServiceType RENDERING_CONTROL_SERVICE = new UDAServiceType("RenderingControl"); 27 | private DeviceType dmrDeviceType = new UDADeviceType("MediaRenderer"); 28 | 29 | private static SystemManager INSTANCE = null; 30 | private BeyondUpnpService mUpnpService = null; 31 | private SystemService.SystemServiceBinder mSystemServiceBinder; 32 | 33 | private SystemManager() { 34 | } 35 | 36 | public static SystemManager getInstance() { 37 | if (INSTANCE == null) { 38 | INSTANCE = new SystemManager(); 39 | } 40 | return INSTANCE; 41 | } 42 | 43 | public void setUpnpService(BeyondUpnpService upnpService) { 44 | mUpnpService = upnpService; 45 | } 46 | 47 | public void setSystemServiceBinder(SystemService.SystemServiceBinder systemServiceBinder) { 48 | this.mSystemServiceBinder = systemServiceBinder; 49 | } 50 | 51 | public void searchAllDevices() { 52 | mUpnpService.getControlPoint().search(); 53 | } 54 | 55 | public Collection getDmrDevices() { 56 | return mUpnpService.getRegistry().getDevices(dmrDeviceType); 57 | } 58 | 59 | public ControlPoint getControlPoint() { 60 | return mUpnpService.getControlPoint(); 61 | } 62 | 63 | public Registry getRegistry() { 64 | return mUpnpService.getRegistry(); 65 | } 66 | 67 | public Collection getDmcDevices() { 68 | if (mUpnpService == null) return Collections.EMPTY_LIST; 69 | 70 | List devices = new ArrayList<>(); 71 | devices.addAll(mUpnpService.getRegistry().getDevices(CONTENT_DIRECTORY_SERVICE)); 72 | return devices; 73 | } 74 | 75 | public Device getSelectedDevice() { 76 | return mSystemServiceBinder.getSelectedDevice(); 77 | } 78 | 79 | public void setSelectedDevice(Device selectedDevice) { 80 | mSystemServiceBinder.setSelectedDevice(selectedDevice, mUpnpService.getControlPoint()); 81 | } 82 | 83 | public int getDeviceVolume() { 84 | return mSystemServiceBinder.getDeviceVolume(); 85 | } 86 | 87 | public void setDeviceVolume(int currentVolume) { 88 | mSystemServiceBinder.setDeviceVolume(currentVolume); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/core/server/AudioResourceServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.core.server; 17 | 18 | import android.content.ContentUris; 19 | import android.database.Cursor; 20 | import android.net.Uri; 21 | import android.provider.MediaStore; 22 | import android.util.Log; 23 | 24 | import com.kevinshen.beyondupnp.BeyondApplication; 25 | import com.kevinshen.beyondupnp.util.Utils; 26 | 27 | import org.eclipse.jetty.servlet.DefaultServlet; 28 | import org.eclipse.jetty.util.resource.FileResource; 29 | import org.eclipse.jetty.util.resource.Resource; 30 | 31 | import java.io.File; 32 | 33 | public class AudioResourceServlet extends DefaultServlet { 34 | 35 | @Override 36 | public Resource getResource(String pathInContext) { 37 | Resource resource = null; 38 | 39 | Log.i(AudioResourceServlet.class.getSimpleName(),"Path:" + pathInContext); 40 | try { 41 | String id = Utils.parseResourceId(pathInContext); 42 | Log.i(AudioResourceServlet.class.getSimpleName(),"Id:" + id); 43 | 44 | Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, Long.parseLong(id)); 45 | Cursor cursor = BeyondApplication.getApplication().getContentResolver().query( 46 | uri, null,null, null, null); 47 | cursor.moveToFirst(); 48 | String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA)); 49 | File file = new File(path); 50 | if (file.exists()) { 51 | resource = FileResource.newResource(file); 52 | } 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | } 56 | 57 | return resource; 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/core/server/JettyResourceServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.core.server; 17 | 18 | import org.eclipse.jetty.server.Server; 19 | import org.eclipse.jetty.servlet.ServletContextHandler; 20 | import org.eclipse.jetty.servlet.ServletHolder; 21 | 22 | import java.util.logging.Logger; 23 | 24 | public class JettyResourceServer implements Runnable { 25 | final private static Logger log = Logger.getLogger(JettyResourceServer.class.getName()); 26 | 27 | public static final int JETTY_SERVER_PORT = 9090; 28 | private Server mServer; 29 | 30 | public JettyResourceServer() { 31 | mServer = new Server(JETTY_SERVER_PORT); // Has its own QueuedThreadPool 32 | mServer.setGracefulShutdown(1000); // Let's wait a second for ongoing transfers to complete 33 | } 34 | 35 | synchronized public void startIfNotRunning() { 36 | if (!mServer.isStarted() && !mServer.isStarting()) { 37 | log.info("Starting JettyResourceServer"); 38 | try { 39 | mServer.start(); 40 | } catch (Exception ex) { 41 | log.severe("Couldn't start Jetty server: " + ex); 42 | throw new RuntimeException(ex); 43 | } 44 | } 45 | } 46 | 47 | synchronized public void stopIfRunning() { 48 | if (!mServer.isStopped() && !mServer.isStopping()) { 49 | log.info("Stopping JettyResourceServer"); 50 | try { 51 | mServer.stop(); 52 | } catch (Exception ex) { 53 | log.severe("Couldn't stop Jetty server: " + ex); 54 | throw new RuntimeException(ex); 55 | } 56 | } 57 | } 58 | 59 | public String getServerState() { 60 | return mServer.getState(); 61 | } 62 | 63 | @Override 64 | public void run() { 65 | ServletContextHandler context = new ServletContextHandler(); 66 | context.setContextPath("/"); 67 | context.setInitParameter("org.eclipse.jetty.servlet.Default.gzip", "false"); 68 | mServer.setHandler(context); 69 | 70 | context.addServlet(AudioResourceServlet.class, "/audio/*"); 71 | context.addServlet(VideoResourceServlet.class, "/video/*"); 72 | 73 | startIfNotRunning(); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/core/server/VideoResourceServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.core.server; 17 | 18 | import android.content.ContentUris; 19 | import android.database.Cursor; 20 | import android.net.Uri; 21 | import android.provider.MediaStore; 22 | import android.util.Log; 23 | 24 | import com.kevinshen.beyondupnp.BeyondApplication; 25 | import com.kevinshen.beyondupnp.util.Utils; 26 | 27 | import org.eclipse.jetty.servlet.DefaultServlet; 28 | import org.eclipse.jetty.util.resource.FileResource; 29 | import org.eclipse.jetty.util.resource.Resource; 30 | 31 | import java.io.File; 32 | 33 | public class VideoResourceServlet extends DefaultServlet { 34 | 35 | @Override 36 | public Resource getResource(String pathInContext) { 37 | Resource resource = null; 38 | 39 | Log.i(VideoResourceServlet.class.getSimpleName(), "Path:" + pathInContext); 40 | try { 41 | String id = Utils.parseResourceId(pathInContext); 42 | Log.i(VideoResourceServlet.class.getSimpleName(), "Id:" + id); 43 | 44 | Uri uri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, Long.parseLong(id)); 45 | Cursor cursor = BeyondApplication.getApplication().getContentResolver().query( 46 | uri, null, null, null, null); 47 | cursor.moveToFirst(); 48 | String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA)); 49 | File file = new File(path); 50 | if (file.exists()) { 51 | resource = FileResource.newResource(file); 52 | } 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | } 56 | 57 | return resource; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/core/upnp/AndroidJettyServletContainer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.core.upnp; 17 | 18 | import org.eclipse.jetty.server.AbstractHttpConnection; 19 | import org.eclipse.jetty.server.Connector; 20 | import org.eclipse.jetty.server.Request; 21 | import org.eclipse.jetty.server.Server; 22 | import org.eclipse.jetty.server.bio.SocketConnector; 23 | import org.eclipse.jetty.servlet.ServletContextHandler; 24 | import org.eclipse.jetty.servlet.ServletHolder; 25 | import org.eclipse.jetty.util.thread.ExecutorThreadPool; 26 | import org.fourthline.cling.transport.spi.ServletContainerAdapter; 27 | 28 | import java.io.IOException; 29 | import java.net.Socket; 30 | import java.util.concurrent.ExecutorService; 31 | import java.util.logging.Level; 32 | import java.util.logging.Logger; 33 | 34 | import javax.servlet.Servlet; 35 | import javax.servlet.http.HttpServletRequest; 36 | 37 | public class AndroidJettyServletContainer implements ServletContainerAdapter { 38 | final private static Logger log = Logger.getLogger(AndroidJettyServletContainer.class.getName()); 39 | 40 | // Singleton 41 | public static final AndroidJettyServletContainer INSTANCE = new AndroidJettyServletContainer(); 42 | 43 | private AndroidJettyServletContainer() { 44 | resetServer(); 45 | } 46 | 47 | protected Server server; 48 | 49 | @Override 50 | synchronized public void setExecutorService(ExecutorService executorService) { 51 | if (INSTANCE.server.getThreadPool() == null) { 52 | INSTANCE.server.setThreadPool(new ExecutorThreadPool(executorService) { 53 | @Override 54 | protected void doStop() throws Exception { 55 | // Do nothing, don't shut down the Cling ExecutorService when Jetty stops! 56 | } 57 | }); 58 | } 59 | } 60 | 61 | @Override 62 | synchronized public int addConnector(String host, int port) throws IOException { 63 | SocketConnector connector = new SocketConnector(); 64 | connector.setHost(host); 65 | connector.setPort(port); 66 | 67 | // Open immediately so we can get the assigned local port 68 | connector.open(); 69 | 70 | // Only add if open() succeeded 71 | server.addConnector(connector); 72 | 73 | // stats the connector if the server is started (server starts all connectors when started) 74 | if (server.isStarted()) { 75 | try { 76 | connector.start(); 77 | } catch (Exception ex) { 78 | log.severe("Couldn't start connector: " + connector + " " + ex); 79 | throw new RuntimeException(ex); 80 | } 81 | } 82 | return connector.getLocalPort(); 83 | } 84 | 85 | synchronized public void removeConnector(String host, int port) { 86 | Connector[] connectors = server.getConnectors(); 87 | if (connectors == null) 88 | return; 89 | 90 | for (Connector connector : connectors) { 91 | //Fix getPort() 92 | if (connector.getHost().equals(host) && connector.getLocalPort() == port) { 93 | if (connector.isStarted() || connector.isStarting()) { 94 | try { 95 | connector.stop(); 96 | } catch (Exception ex) { 97 | log.severe("Couldn't stop connector: " + connector + " " + ex); 98 | throw new RuntimeException(ex); 99 | } 100 | } 101 | server.removeConnector(connector); 102 | if (connectors.length == 1) { 103 | log.info("No more connectors, stopping Jetty server"); 104 | stopIfRunning(); 105 | } 106 | break; 107 | } 108 | } 109 | } 110 | 111 | @Override 112 | synchronized public void registerServlet(String contextPath, Servlet servlet) { 113 | if (server.getHandler() != null) { 114 | return; 115 | } 116 | log.info("Registering UPnP servlet under context path: " + contextPath); 117 | ServletContextHandler servletHandler = 118 | new ServletContextHandler(ServletContextHandler.NO_SESSIONS); 119 | if (contextPath != null && contextPath.length() > 0) 120 | servletHandler.setContextPath(contextPath); 121 | ServletHolder s = new ServletHolder(servlet); 122 | servletHandler.addServlet(s, "/*"); 123 | server.setHandler(servletHandler); 124 | } 125 | 126 | @Override 127 | synchronized public void startIfNotRunning() { 128 | if (!server.isStarted() && !server.isStarting()) { 129 | log.info("Starting Jetty server... "); 130 | try { 131 | server.start(); 132 | } catch (Exception ex) { 133 | log.severe("Couldn't start Jetty server: " + ex); 134 | throw new RuntimeException(ex); 135 | } 136 | } 137 | } 138 | 139 | @Override 140 | synchronized public void stopIfRunning() { 141 | if (!server.isStopped() && !server.isStopping()) { 142 | log.info("Stopping Jetty server..."); 143 | try { 144 | server.stop(); 145 | } catch (Exception ex) { 146 | log.severe("Couldn't stop Jetty server: " + ex); 147 | throw new RuntimeException(ex); 148 | } finally { 149 | resetServer(); 150 | } 151 | } 152 | } 153 | 154 | protected void resetServer() { 155 | server = new Server(); // Has its own QueuedThreadPool 156 | server.setGracefulShutdown(1000); // Let's wait a second for ongoing transfers to complete 157 | } 158 | 159 | /** 160 | * Casts the request to a Jetty API and tries to write a space character to the output stream of the socket. 161 | *

162 | * This space character might confuse the HTTP client. The Cling transports for Jetty Client and 163 | * Apache HttpClient have been tested to work with space characters. Unfortunately, Sun JDK's 164 | * HttpURLConnection does not gracefully handle any garbage in the HTTP request! 165 | *

166 | */ 167 | public static boolean isConnectionOpen(HttpServletRequest request) { 168 | return isConnectionOpen(request, " ".getBytes()); 169 | } 170 | 171 | public static boolean isConnectionOpen(HttpServletRequest request, byte[] heartbeat) { 172 | Request jettyRequest = (Request) request; 173 | AbstractHttpConnection connection = jettyRequest.getConnection(); 174 | Socket socket = (Socket) connection.getEndPoint().getTransport(); 175 | if (log.isLoggable(Level.FINE)) 176 | log.fine("Checking if client connection is still open: " + socket.getRemoteSocketAddress()); 177 | try { 178 | socket.getOutputStream().write(heartbeat); 179 | socket.getOutputStream().flush(); 180 | return true; 181 | } catch (IOException ex) { 182 | if (log.isLoggable(Level.FINE)) 183 | log.fine("Client connection has been closed: " + socket.getRemoteSocketAddress()); 184 | return false; 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/core/upnp/BeyondContentDirectoryService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.core.upnp; 17 | 18 | import com.kevinshen.beyondupnp.core.server.JettyResourceServer; 19 | import com.kevinshen.beyondupnp.database.MediaResourceDao; 20 | import com.kevinshen.beyondupnp.util.Utils; 21 | 22 | import org.fourthline.cling.support.contentdirectory.AbstractContentDirectoryService; 23 | import org.fourthline.cling.support.contentdirectory.ContentDirectoryErrorCode; 24 | import org.fourthline.cling.support.contentdirectory.ContentDirectoryException; 25 | import org.fourthline.cling.support.contentdirectory.DIDLParser; 26 | import org.fourthline.cling.support.model.BrowseFlag; 27 | import org.fourthline.cling.support.model.BrowseResult; 28 | import org.fourthline.cling.support.model.DIDLContent; 29 | import org.fourthline.cling.support.model.DIDLObject; 30 | import org.fourthline.cling.support.model.SortCriterion; 31 | import org.fourthline.cling.support.model.container.Container; 32 | import org.fourthline.cling.support.model.item.Item; 33 | 34 | import java.io.File; 35 | import java.util.List; 36 | 37 | public class BeyondContentDirectoryService extends AbstractContentDirectoryService { 38 | 39 | public static final DIDLObject.Class AUDIO_CLASS = new DIDLObject.Class("object.container.audio"); 40 | public static final DIDLObject.Class VIDEO_CLASS = new DIDLObject.Class("object.container.video"); 41 | 42 | public final static String ROOT = "0"; 43 | public final static String AUDIO = ROOT + File.separator + "10"; 44 | public final static String VIDEO = ROOT + File.separator + "20"; 45 | public final static String IMAGE = ROOT + File.separator + "30"; 46 | 47 | @Override 48 | public BrowseResult browse(String objectID, BrowseFlag browseFlag, String filter, long firstResult, long maxResults, SortCriterion[] orderby) throws ContentDirectoryException { 49 | String address = Utils.getIPAddress(true); 50 | String serverUrl = "http://" + address + ":" + JettyResourceServer.JETTY_SERVER_PORT; 51 | 52 | //Create container by id 53 | Container resultBean = ContainerFactory.createContainer(objectID, serverUrl); 54 | DIDLContent content = new DIDLContent(); 55 | 56 | for (Container c : resultBean.getContainers()) 57 | content.addContainer(c); 58 | 59 | for (Item item : resultBean.getItems()) 60 | content.addItem(item); 61 | 62 | int count = resultBean.getChildCount(); 63 | String contentModel = ""; 64 | try { 65 | contentModel = new DIDLParser().generate(content); 66 | } catch (Exception e) { 67 | throw new ContentDirectoryException( 68 | ContentDirectoryErrorCode.CANNOT_PROCESS, e.toString()); 69 | } 70 | 71 | return new BrowseResult(contentModel, count, count); 72 | } 73 | 74 | static class ContainerFactory { 75 | 76 | static Container createContainer(String containerId, String serverUrl) { 77 | Container result = new Container(); 78 | result.setChildCount(0); 79 | 80 | if (ROOT.equals(containerId)) { 81 | Container audioContainer = new Container(); 82 | audioContainer.setId(AUDIO); 83 | audioContainer.setParentID(ROOT); 84 | audioContainer.setClazz(AUDIO_CLASS); 85 | audioContainer.setTitle("Audios"); 86 | 87 | result.addContainer(audioContainer); 88 | result.setChildCount(result.getChildCount() + 1); 89 | 90 | Container videoContainer = new Container(); 91 | videoContainer.setId(VIDEO); 92 | videoContainer.setParentID(ROOT); 93 | videoContainer.setClazz(VIDEO_CLASS); 94 | videoContainer.setTitle("Videos"); 95 | 96 | result.addContainer(videoContainer); 97 | result.setChildCount(result.getChildCount() + 1); 98 | } else if (AUDIO.equals(containerId)) { 99 | //Get audio items 100 | List items = MediaResourceDao.getAudioList(serverUrl, AUDIO); 101 | 102 | for (Item item : items) { 103 | result.addItem(item); 104 | result.setChildCount(result.getChildCount() + 1); 105 | } 106 | } else if (VIDEO.equals(containerId)) { 107 | //Get video items 108 | List items = MediaResourceDao.getVideoList(serverUrl, VIDEO); 109 | 110 | for (Item item : items) { 111 | result.addItem(item); 112 | result.setChildCount(result.getChildCount() + 1); 113 | } 114 | } 115 | return result; 116 | } 117 | 118 | 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/database/BeyondUpnpContract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.database; 17 | 18 | import android.net.Uri; 19 | import android.provider.BaseColumns; 20 | 21 | public class BeyondUpnpContract { 22 | 23 | interface PlaylistItemColumns { 24 | /** 25 | * The item title for this table. 26 | */ 27 | public static final String ITEM_TITLE = "item_title"; 28 | 29 | /** 30 | * The item uri for this table. 31 | */ 32 | public static final String ITEM_URI = "item_uri"; 33 | 34 | /** 35 | * The item thumb for this table. 36 | */ 37 | public static final String ITEM_THUMB = "item_thumb"; 38 | 39 | /** 40 | * The item metadata for this table. 41 | */ 42 | public static final String ITEM_METADATA = "item_metadata"; 43 | 44 | /** 45 | * The item join date for this table. 46 | */ 47 | public static final String ITEM_DATE = "item_date"; 48 | 49 | /** 50 | * The item verification code(md5) for this table. 51 | */ 52 | public static final String ITEM_VERIFICATION_CODE = "verification_code"; 53 | } 54 | 55 | public static class PlaylistItem implements PlaylistItemColumns,BaseColumns { 56 | public static String PLAYLIST_ITEM_TABLE = "playlist_item"; 57 | 58 | public static final Uri CONTENT_URI = 59 | BASE_CONTENT_URI.buildUpon().appendPath(PATH_PLAYLIST_ITEM).build(); 60 | 61 | public static final String CONTENT_TYPE = 62 | "vnd.android.cursor.dir/vnd.beyondupnp.playlist_item"; 63 | public static final String CONTENT_ITEM_TYPE = 64 | "vnd.android.cursor.item/vnd.beyondupnp.playlist_item"; 65 | 66 | } 67 | 68 | public static final String CONTENT_AUTHORITY = "com.kevinshen.beyondupnp"; 69 | 70 | public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY); 71 | 72 | private static final String PATH_PLAYLIST_ITEM = "playlist_item"; 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/database/MediaResourceDao.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.database; 17 | 18 | import android.database.Cursor; 19 | import android.os.Environment; 20 | import android.provider.MediaStore; 21 | 22 | import com.kevinshen.beyondupnp.BeyondApplication; 23 | 24 | import org.fourthline.cling.model.ModelUtil; 25 | import org.fourthline.cling.support.model.PersonWithRole; 26 | import org.fourthline.cling.support.model.Res; 27 | import org.fourthline.cling.support.model.item.Item; 28 | import org.fourthline.cling.support.model.item.Movie; 29 | import org.fourthline.cling.support.model.item.MusicTrack; 30 | 31 | import java.io.File; 32 | import java.util.ArrayList; 33 | import java.util.List; 34 | 35 | public class MediaResourceDao { 36 | private static String storageDir = ""; 37 | 38 | static { 39 | if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 40 | storageDir = Environment.getExternalStorageDirectory().getAbsolutePath(); 41 | } 42 | } 43 | 44 | public static List getAudioList(String serverUrl, String parentId) { 45 | List items = new ArrayList<>(); 46 | 47 | //Query all track,add to items 48 | Cursor c = BeyondApplication.getApplication().getContentResolver() 49 | .query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Audio.Media.TITLE); 50 | c.moveToFirst(); 51 | while (!c.isAfterLast()) { 52 | long id = c.getLong(c.getColumnIndex(MediaStore.Audio.Media._ID)); 53 | String title = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)); 54 | String creator = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)); 55 | String album = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM)); 56 | 57 | String data = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA)); 58 | //Remove SDCard path 59 | data = data.replaceFirst(storageDir, ""); 60 | //Replace file name by "id.ext" 61 | String fileName = data.substring(data.lastIndexOf(File.separator)); 62 | String ext = fileName.substring(fileName.lastIndexOf(".")); 63 | data = data.replace(fileName, File.separator + id + ext); 64 | 65 | String mimeType = c.getString(c.getColumnIndexOrThrow(MediaStore.Audio.Media.MIME_TYPE)); 66 | long size = c.getLong(c.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE)); 67 | long duration = c.getLong(c.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)); 68 | //Get duration string 69 | String durationStr = ModelUtil.toTimeString(duration); 70 | 71 | //Compose audio url 72 | String url = serverUrl + File.separator + "audio" + File.separator + data; 73 | Res res = new Res(mimeType, size, durationStr, null, url); 74 | 75 | items.add(new MusicTrack(String.valueOf(id), parentId, title, creator, album, new PersonWithRole(creator), res)); 76 | 77 | c.moveToNext(); 78 | } 79 | 80 | return items; 81 | } 82 | 83 | public static List getVideoList(String serverUrl, String parentId) { 84 | List items = new ArrayList<>(); 85 | 86 | Cursor c = BeyondApplication.getApplication().getContentResolver() 87 | .query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Video.Media.TITLE); 88 | c.moveToFirst(); 89 | while (!c.isAfterLast()) { 90 | long id = c.getLong(c.getColumnIndex(MediaStore.Audio.Media._ID)); 91 | String title = c.getString(c.getColumnIndexOrThrow(MediaStore.Video.Media.TITLE)); 92 | String creator = c.getString(c.getColumnIndexOrThrow(MediaStore.Video.Media.ARTIST)); 93 | 94 | String data = c.getString(c.getColumnIndexOrThrow(MediaStore.Video.Media.DATA)); 95 | //Remove SDCard path 96 | data = data.replaceFirst(storageDir, ""); 97 | //Replace file name by "id.ext" 98 | String fileName = data.substring(data.lastIndexOf(File.separator)); 99 | String ext = fileName.substring(fileName.lastIndexOf(".")); 100 | data = data.replace(fileName, File.separator + id + ext); 101 | 102 | String mimeType = c.getString(c.getColumnIndexOrThrow(MediaStore.Video.Media.MIME_TYPE)); 103 | long size = c.getLong(c.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE)); 104 | long duration = c.getLong(c.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION)); 105 | //Get duration string 106 | String durationStr = ModelUtil.toTimeString(duration); 107 | 108 | //Compose audio url 109 | String url = serverUrl + File.separator + "video" + File.separator + data; 110 | Res res = new Res(mimeType, size, durationStr, null, url); 111 | 112 | items.add(new Movie(String.valueOf(id), parentId, title, creator, res)); 113 | 114 | c.moveToNext(); 115 | } 116 | 117 | return items; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/database/SQLiteHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.database; 17 | 18 | import android.content.Context; 19 | import android.database.sqlite.SQLiteDatabase; 20 | import android.database.sqlite.SQLiteOpenHelper; 21 | import android.util.Log; 22 | 23 | public class SQLiteHelper extends SQLiteOpenHelper { 24 | public static final String TAG = SQLiteHelper.class.getSimpleName(); 25 | 26 | private static final String DATABASE_NAME = "beyondupnp.db"; 27 | private static final int DATABASE_VERSION = 1; 28 | 29 | public SQLiteHelper(Context context) { 30 | super(context, DATABASE_NAME, null, DATABASE_VERSION); 31 | } 32 | 33 | @Override 34 | public void onCreate(SQLiteDatabase db) { 35 | String playlistItemSql = "CREATE TABLE `" + BeyondUpnpContract.PlaylistItem.PLAYLIST_ITEM_TABLE + "` ( `" 36 | + BeyondUpnpContract.PlaylistItem._ID + "` INTEGER PRIMARY KEY AUTOINCREMENT, `" 37 | + BeyondUpnpContract.PlaylistItem.ITEM_TITLE + "` TEXT , `" 38 | + BeyondUpnpContract.PlaylistItem.ITEM_THUMB + "` TEXT , `" 39 | + BeyondUpnpContract.PlaylistItem.ITEM_METADATA + "` TEXT , `" 40 | + BeyondUpnpContract.PlaylistItem.ITEM_VERIFICATION_CODE + "` TEXT , `" 41 | + BeyondUpnpContract.PlaylistItem.ITEM_DATE + "` INTEGER , `" 42 | + BeyondUpnpContract.PlaylistItem.ITEM_URI + "` TEXT );"; 43 | db.execSQL(playlistItemSql); 44 | } 45 | 46 | @Override 47 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 48 | Log.d(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion); 49 | 50 | if (oldVersion < DATABASE_VERSION) { 51 | db.execSQL("DROP TABLE IF EXISTS `" + BeyondUpnpContract.PlaylistItem.PLAYLIST_ITEM_TABLE + "`;"); 52 | onCreate(db); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/database/SystemProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.database; 17 | 18 | import android.content.ContentProvider; 19 | import android.content.ContentUris; 20 | import android.content.ContentValues; 21 | import android.content.UriMatcher; 22 | import android.database.Cursor; 23 | import android.database.sqlite.SQLiteDatabase; 24 | import android.net.Uri; 25 | 26 | import com.kevinshen.beyondupnp.database.BeyondUpnpContract.PlaylistItem; 27 | 28 | public class SystemProvider extends ContentProvider { 29 | private static final String TAG = SystemProvider.class.getSimpleName(); 30 | 31 | private SQLiteHelper mOpenHelper; 32 | 33 | private static final UriMatcher sUriMatcher = buildUriMatcher(); 34 | 35 | private static final int PLAYLIST_ITEM = 101; 36 | private static final int PLAYLIST_ITEM_ID = 102; 37 | 38 | /** 39 | * Build and return a {@link UriMatcher} that catches all {@link Uri} 40 | * variations supported by this {@link ContentProvider}. 41 | */ 42 | private static UriMatcher buildUriMatcher() { 43 | final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); 44 | final String authority = BeyondUpnpContract.CONTENT_AUTHORITY; 45 | 46 | matcher.addURI(authority, "playlist_item", PLAYLIST_ITEM); 47 | matcher.addURI(authority, "playlist_item/*", PLAYLIST_ITEM_ID); 48 | 49 | return matcher; 50 | } 51 | 52 | @Override 53 | public boolean onCreate() { 54 | mOpenHelper = new SQLiteHelper(getContext()); 55 | return true; 56 | } 57 | 58 | @Override 59 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 60 | final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 61 | 62 | final int match = sUriMatcher.match(uri); 63 | 64 | switch (match) { 65 | case PLAYLIST_ITEM: { 66 | break; 67 | } 68 | case PLAYLIST_ITEM_ID: { 69 | long id = ContentUris.parseId(uri); 70 | selection = PlaylistItem._ID + " = ?"; 71 | selectionArgs = new String[]{String.valueOf(id)}; 72 | break; 73 | } 74 | default: { 75 | throw new IllegalArgumentException("Unknown URI: " + uri); 76 | } 77 | } 78 | 79 | return db.query(PlaylistItem.PLAYLIST_ITEM_TABLE, projection, selection, selectionArgs, null, null, sortOrder); 80 | } 81 | 82 | @Override 83 | public String getType(Uri uri) { 84 | int match = sUriMatcher.match(uri); 85 | switch (match) { 86 | case PLAYLIST_ITEM: 87 | return PlaylistItem.CONTENT_TYPE; 88 | case PLAYLIST_ITEM_ID: 89 | return PlaylistItem.CONTENT_ITEM_TYPE; 90 | default: 91 | throw new IllegalArgumentException("Unknown URI: " + uri); 92 | } 93 | } 94 | 95 | @Override 96 | public Uri insert(Uri uri, ContentValues values) { 97 | final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 98 | final int match = sUriMatcher.match(uri); 99 | switch (match) { 100 | case PLAYLIST_ITEM: { 101 | long id = db.insertOrThrow(PlaylistItem.PLAYLIST_ITEM_TABLE, null, values); 102 | notifyChange(uri); 103 | return ContentUris.withAppendedId(uri, id); 104 | } 105 | } 106 | 107 | return null; 108 | } 109 | 110 | @Override 111 | public int delete(Uri uri, String selection, String[] selectionArgs) { 112 | final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 113 | final int match = sUriMatcher.match(uri); 114 | int count = 0; 115 | switch (match) { 116 | case PLAYLIST_ITEM_ID: { 117 | long id = ContentUris.parseId(uri); 118 | selection = PlaylistItem._ID + " = ?"; 119 | selectionArgs = new String[]{String.valueOf(id)}; 120 | count = db.delete(PlaylistItem.PLAYLIST_ITEM_TABLE, selection, selectionArgs); 121 | break; 122 | } 123 | default: 124 | throw new IllegalArgumentException("Unknown URI: " + uri); 125 | } 126 | 127 | if (count > 0) { 128 | notifyChange(uri); 129 | } 130 | 131 | return count; 132 | } 133 | 134 | @Override 135 | public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 136 | final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 137 | final int match = sUriMatcher.match(uri); 138 | 139 | switch (match) { 140 | case PLAYLIST_ITEM_ID: 141 | long id = ContentUris.parseId(uri); 142 | selection = PlaylistItem._ID + " = ?"; 143 | selectionArgs = new String[]{String.valueOf(id)}; 144 | break; 145 | default: 146 | throw new IllegalArgumentException("Unknown URI: " + uri); 147 | } 148 | int count = db.update(PlaylistItem.PLAYLIST_ITEM_TABLE, values, selection, selectionArgs); 149 | if (count > 0) { 150 | notifyChange(uri); 151 | } 152 | return count; 153 | } 154 | 155 | private void notifyChange(Uri uri) { 156 | getContext().getContentResolver().notifyChange(uri, null); 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/service/BeyondUpnpService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.service; 17 | 18 | import android.content.Intent; 19 | import android.os.IBinder; 20 | 21 | import com.kevinshen.beyondupnp.BeyondApplication; 22 | import com.kevinshen.beyondupnp.R; 23 | import com.kevinshen.beyondupnp.core.upnp.AndroidJettyServletContainer; 24 | import com.kevinshen.beyondupnp.core.upnp.BeyondContentDirectoryService; 25 | import com.kevinshen.beyondupnp.util.Utils; 26 | 27 | import org.fourthline.cling.UpnpServiceConfiguration; 28 | import org.fourthline.cling.android.AndroidUpnpServiceConfiguration; 29 | import org.fourthline.cling.android.AndroidUpnpServiceImpl; 30 | import org.fourthline.cling.binding.annotations.AnnotationLocalServiceBinder; 31 | import org.fourthline.cling.controlpoint.ControlPoint; 32 | import org.fourthline.cling.model.DefaultServiceManager; 33 | import org.fourthline.cling.model.ValidationException; 34 | import org.fourthline.cling.model.meta.DeviceDetails; 35 | import org.fourthline.cling.model.meta.DeviceIdentity; 36 | import org.fourthline.cling.model.meta.LocalDevice; 37 | import org.fourthline.cling.model.meta.LocalService; 38 | import org.fourthline.cling.model.types.UDADeviceType; 39 | import org.fourthline.cling.model.types.UDN; 40 | import org.fourthline.cling.registry.Registry; 41 | import org.fourthline.cling.transport.impl.AsyncServletStreamServerConfigurationImpl; 42 | import org.fourthline.cling.transport.impl.AsyncServletStreamServerImpl; 43 | import org.fourthline.cling.transport.spi.NetworkAddressFactory; 44 | import org.fourthline.cling.transport.spi.StreamServer; 45 | 46 | import java.util.UUID; 47 | 48 | public class BeyondUpnpService extends AndroidUpnpServiceImpl { 49 | private static final String TAG = BeyondUpnpService.class.getSimpleName(); 50 | 51 | private LocalDevice mLocalDevice = null; 52 | 53 | @Override 54 | public void onCreate() { 55 | super.onCreate(); 56 | 57 | //Create LocalDevice 58 | LocalService localService = new AnnotationLocalServiceBinder().read(BeyondContentDirectoryService.class); 59 | localService.setManager(new DefaultServiceManager<>( 60 | localService, BeyondContentDirectoryService.class)); 61 | 62 | String macAddress = Utils.getMACAddress(Utils.WLAN0); 63 | //Generate UUID by MAC address 64 | UDN udn = UDN.valueOf(UUID.nameUUIDFromBytes(macAddress.getBytes()).toString()); 65 | 66 | try { 67 | mLocalDevice = new LocalDevice(new DeviceIdentity(udn), new UDADeviceType("MediaServer"), 68 | new DeviceDetails("Local Media Server"), new LocalService[]{localService}); 69 | } catch (ValidationException e) { 70 | e.printStackTrace(); 71 | } 72 | upnpService.getRegistry().addDevice(mLocalDevice); 73 | 74 | //LocalBinder instead of binder 75 | binder = new LocalBinder(); 76 | } 77 | 78 | @Override 79 | public void onDestroy() { 80 | super.onDestroy(); 81 | } 82 | 83 | @Override 84 | protected UpnpServiceConfiguration createConfiguration() { 85 | return new FixedAndroidUpnpServiceConfiguration(); 86 | } 87 | 88 | @Override 89 | public IBinder onBind(Intent intent) { 90 | return binder; 91 | } 92 | 93 | public LocalDevice getLocalDevice() { 94 | return mLocalDevice; 95 | } 96 | 97 | class FixedAndroidUpnpServiceConfiguration extends AndroidUpnpServiceConfiguration { 98 | @Override 99 | public StreamServer createStreamServer(NetworkAddressFactory networkAddressFactory) { 100 | // Use Jetty, start/stop a new shared instance of JettyServletContainer 101 | return new AsyncServletStreamServerImpl( 102 | new AsyncServletStreamServerConfigurationImpl( 103 | AndroidJettyServletContainer.INSTANCE, 104 | networkAddressFactory.getStreamListenPort() 105 | ) 106 | ); 107 | } 108 | } 109 | 110 | public UpnpServiceConfiguration getConfiguration() { 111 | return upnpService.getConfiguration(); 112 | } 113 | 114 | public Registry getRegistry() { 115 | return upnpService.getRegistry(); 116 | } 117 | 118 | public ControlPoint getControlPoint() { 119 | return upnpService.getControlPoint(); 120 | } 121 | 122 | public class LocalBinder extends Binder { 123 | public BeyondUpnpService getService() { 124 | return BeyondUpnpService.this; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/service/SystemService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.service; 17 | 18 | import android.app.Service; 19 | import android.content.Intent; 20 | import android.os.Binder; 21 | import android.os.IBinder; 22 | import android.util.Log; 23 | 24 | import com.kevinshen.beyondupnp.Intents; 25 | import com.kevinshen.beyondupnp.core.SystemManager; 26 | 27 | import org.fourthline.cling.android.AndroidUpnpService; 28 | import org.fourthline.cling.controlpoint.ControlPoint; 29 | import org.fourthline.cling.controlpoint.SubscriptionCallback; 30 | import org.fourthline.cling.model.gena.CancelReason; 31 | import org.fourthline.cling.model.gena.GENASubscription; 32 | import org.fourthline.cling.model.message.UpnpResponse; 33 | import org.fourthline.cling.model.meta.Device; 34 | import org.fourthline.cling.model.state.StateVariableValue; 35 | import org.fourthline.cling.support.avtransport.lastchange.AVTransportLastChangeParser; 36 | import org.fourthline.cling.support.avtransport.lastchange.AVTransportVariable; 37 | import org.fourthline.cling.support.contentdirectory.DIDLParser; 38 | import org.fourthline.cling.support.lastchange.EventedValueString; 39 | import org.fourthline.cling.support.lastchange.LastChange; 40 | import org.fourthline.cling.support.model.DIDLContent; 41 | import org.fourthline.cling.support.model.TransportState; 42 | import org.fourthline.cling.support.model.item.Item; 43 | 44 | import java.util.Map; 45 | 46 | /** 47 | * Application service,process background task. 48 | */ 49 | public class SystemService extends Service { 50 | private static final String TAG = SystemService.class.getSimpleName(); 51 | 52 | private Binder binder = new SystemServiceBinder(); 53 | private Device mSelectedDevice; 54 | private int mDeviceVolume; 55 | private AVTransportSubscriptionCallback mAVTransportSubscriptionCallback; 56 | 57 | @Override 58 | public void onCreate() { 59 | super.onCreate(); 60 | } 61 | 62 | @Override 63 | public void onDestroy() { 64 | //End all subscriptions 65 | if (mAVTransportSubscriptionCallback != null) 66 | mAVTransportSubscriptionCallback.end(); 67 | 68 | super.onDestroy(); 69 | } 70 | 71 | @Override 72 | public IBinder onBind(Intent intent) { 73 | return binder; 74 | } 75 | 76 | public class SystemServiceBinder extends Binder{ 77 | 78 | public Device getSelectedDevice() { 79 | return mSelectedDevice; 80 | } 81 | 82 | public void setSelectedDevice(Device selectedDevice,ControlPoint controlPoint) { 83 | if (selectedDevice == mSelectedDevice) return; 84 | 85 | Log.i(TAG,"Change selected device."); 86 | mSelectedDevice = selectedDevice; 87 | //End last device's subscriptions 88 | if (mAVTransportSubscriptionCallback != null) { 89 | mAVTransportSubscriptionCallback.end(); 90 | } 91 | //Init Subscriptions 92 | mAVTransportSubscriptionCallback = new AVTransportSubscriptionCallback(mSelectedDevice.findService(SystemManager.AV_TRANSPORT_SERVICE)); 93 | controlPoint.execute(mAVTransportSubscriptionCallback); 94 | 95 | Intent intent = new Intent(Intents.ACTION_CHANGE_DEVICE); 96 | sendBroadcast(intent); 97 | } 98 | 99 | public int getDeviceVolume(){ 100 | return mDeviceVolume; 101 | } 102 | 103 | public void setDeviceVolume(int currentVolume){ 104 | mDeviceVolume = currentVolume; 105 | } 106 | } 107 | 108 | private class AVTransportSubscriptionCallback extends SubscriptionCallback { 109 | 110 | protected AVTransportSubscriptionCallback(org.fourthline.cling.model.meta.Service service) { 111 | super(service); 112 | } 113 | 114 | @Override 115 | protected void failed(GENASubscription subscription, UpnpResponse responseStatus, Exception exception, String defaultMsg) { 116 | Log.e(TAG, "AVTransportSubscriptionCallback failed."); 117 | } 118 | 119 | @Override 120 | protected void established(GENASubscription subscription) { 121 | } 122 | 123 | @Override 124 | protected void ended(GENASubscription subscription, CancelReason reason, UpnpResponse responseStatus) { 125 | Log.i(TAG, "AVTransportSubscriptionCallback ended."); 126 | } 127 | 128 | @Override 129 | protected void eventReceived(GENASubscription subscription) { 130 | Map values = subscription.getCurrentValues(); 131 | if (values != null && values.containsKey("LastChange")) { 132 | String lastChangeValue = values.get("LastChange").toString(); 133 | Log.i(TAG, "LastChange:" + lastChangeValue); 134 | LastChange lastChange; 135 | try { 136 | lastChange = new LastChange(new AVTransportLastChangeParser(), lastChangeValue); 137 | } catch (Exception e) { 138 | e.printStackTrace(); 139 | return; 140 | } 141 | 142 | //Parse TransportState value. 143 | AVTransportVariable.TransportState transportState = lastChange.getEventedValue(0, AVTransportVariable.TransportState.class); 144 | if (transportState != null){ 145 | TransportState ts = transportState.getValue(); 146 | if (ts == TransportState.PLAYING){ 147 | Intent intent = new Intent(Intents.ACTION_PLAYING); 148 | sendBroadcast(intent); 149 | }else if (ts == TransportState.PAUSED_PLAYBACK){ 150 | Intent intent = new Intent(Intents.ACTION_PAUSED_PLAYBACK); 151 | sendBroadcast(intent); 152 | }else if (ts == TransportState.STOPPED){ 153 | Intent intent = new Intent(Intents.ACTION_STOPPED); 154 | sendBroadcast(intent); 155 | } 156 | } 157 | 158 | //Parse CurrentTrackMetaData value. 159 | EventedValueString currentTrackMetaData = lastChange.getEventedValue(0, AVTransportVariable.CurrentTrackMetaData.class); 160 | if (currentTrackMetaData != null && currentTrackMetaData.getValue() != null){ 161 | DIDLParser didlParser = new DIDLParser(); 162 | Intent lastChangeIntent; 163 | try { 164 | DIDLContent content = didlParser.parse(currentTrackMetaData.getValue()); 165 | Item item = content.getItems().get(0); 166 | String creator = item.getCreator(); 167 | String title = item.getTitle(); 168 | 169 | lastChangeIntent = new Intent(Intents.ACTION_UPDATE_LAST_CHANGE); 170 | lastChangeIntent.putExtra("creator",creator); 171 | lastChangeIntent.putExtra("title",title); 172 | }catch (Exception e){ 173 | Log.e(TAG,"Parse CurrentTrackMetaData error."); 174 | lastChangeIntent = null; 175 | } 176 | 177 | if (lastChangeIntent != null) 178 | sendBroadcast(lastChangeIntent); 179 | } 180 | } 181 | } 182 | 183 | @Override 184 | protected void eventsMissed(GENASubscription subscription, int numberOfMissedEvents) { 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/ui/DeviceListDialogFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.ui; 17 | 18 | import android.app.Activity; 19 | import android.app.DialogFragment; 20 | import android.content.Context; 21 | import android.os.AsyncTask; 22 | import android.os.Bundle; 23 | import android.util.Log; 24 | import android.view.LayoutInflater; 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | import android.widget.AdapterView; 28 | import android.widget.ArrayAdapter; 29 | import android.widget.ImageView; 30 | import android.widget.ListView; 31 | import android.widget.TextView; 32 | 33 | import com.kevinshen.beyondupnp.R; 34 | import com.kevinshen.beyondupnp.core.SystemManager; 35 | 36 | import org.fourthline.cling.model.meta.Device; 37 | 38 | import java.util.Collection; 39 | 40 | public class DeviceListDialogFragment extends DialogFragment { 41 | private static final String TAG = DeviceListDialogFragment.class.getSimpleName(); 42 | private ArrayAdapter mArrayAdapter; 43 | private AsyncTask mUpdateDeviceTask; 44 | 45 | /** 46 | * Use this factory method to create a new instance of 47 | * this fragment using the provided parameters. 48 | * 49 | * @return A new instance of fragment DeviceListDialogFragment. 50 | */ 51 | public static DeviceListDialogFragment newInstance() { 52 | DeviceListDialogFragment fragment = new DeviceListDialogFragment(); 53 | return fragment; 54 | } 55 | 56 | public DeviceListDialogFragment() { 57 | // Required empty public constructor 58 | } 59 | 60 | @Override 61 | public void onCreate(Bundle savedInstanceState) { 62 | super.onCreate(savedInstanceState); 63 | 64 | } 65 | 66 | @Override 67 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 68 | Bundle savedInstanceState) { 69 | // Inflate the layout for this fragment 70 | View view = inflater.inflate(R.layout.listview, container, false); 71 | // Set dialog title 72 | getDialog().setTitle("Select Active Device"); 73 | 74 | ListView listView = (ListView) view.findViewById(android.R.id.list); 75 | mArrayAdapter = new DeviceListAdapter(getActivity(), 0); 76 | listView.setAdapter(mArrayAdapter); 77 | listView.setOnItemClickListener(selectDeviceListener); 78 | 79 | return view; 80 | } 81 | 82 | private AdapterView.OnItemClickListener selectDeviceListener = new AdapterView.OnItemClickListener() { 83 | @Override 84 | public void onItemClick(AdapterView parent, View view, int position, long id) { 85 | Device device = mArrayAdapter.getItem(position); 86 | getActivity().setTitle(device.getDetails().getFriendlyName()); 87 | //Set selected device,there is only one device can be selected. 88 | SystemManager.getInstance().setSelectedDevice(device); 89 | //Close the dialog. 90 | dismiss(); 91 | } 92 | }; 93 | 94 | @Override 95 | public void onAttach(Activity activity) { 96 | super.onAttach(activity); 97 | } 98 | 99 | @Override 100 | public void onDetach() { 101 | super.onDetach(); 102 | } 103 | 104 | @Override 105 | public void onResume() { 106 | super.onResume(); 107 | //Create task 108 | mUpdateDeviceTask = new UpdateDeviceListTask().execute(); 109 | } 110 | 111 | @Override 112 | public void onPause() { 113 | super.onPause(); 114 | 115 | if (mUpdateDeviceTask != null) { 116 | mUpdateDeviceTask.cancel(true); 117 | mUpdateDeviceTask = null; 118 | } 119 | } 120 | 121 | @Override 122 | public void onDestroy() { 123 | //Cancel working task. 124 | if (mUpdateDeviceTask != null) 125 | mUpdateDeviceTask.cancel(true); 126 | 127 | super.onDestroy(); 128 | } 129 | 130 | private class DeviceListAdapter extends ArrayAdapter { 131 | private LayoutInflater mInflater; 132 | 133 | public DeviceListAdapter(Context context, int resource) { 134 | super(context, resource); 135 | mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 136 | } 137 | 138 | @Override 139 | public View getView(int position, View convertView, ViewGroup parent) { 140 | if (convertView == null) 141 | convertView = mInflater.inflate(R.layout.listview_items, null); 142 | 143 | Device item = getItem(position); 144 | if (item == null) { 145 | return convertView; 146 | } 147 | 148 | ImageView imageView = (ImageView)convertView.findViewById(R.id.listview_item_image); 149 | imageView.setBackgroundResource(R.drawable.ic_action_dock); 150 | 151 | TextView textView = (TextView) convertView.findViewById(R.id.listview_item_line_one); 152 | textView.setText(item.getDetails().getFriendlyName()); 153 | return convertView; 154 | } 155 | } 156 | 157 | private class UpdateDeviceListTask extends AsyncTask, Void> { 158 | 159 | @Override 160 | protected Void doInBackground(Void... params) { 161 | final SystemManager deviceManager = SystemManager.getInstance(); 162 | while (true) { 163 | Log.i(TAG, "Search devices"); 164 | //Send search command 165 | deviceManager.searchAllDevices(); 166 | //Update list values 167 | Collection devices = deviceManager.getDmrDevices(); 168 | publishProgress(devices); 169 | //Break immediately while task was cancelled. 170 | if (isCancelled()) break; 171 | 172 | try { 173 | //Sleep 3 second 174 | Thread.sleep(3000); 175 | } catch (InterruptedException e) { 176 | Log.e(TAG, "Interrupt update thread!"); 177 | break; 178 | } 179 | } 180 | return null; 181 | } 182 | 183 | @Override 184 | protected void onProgressUpdate(Collection... values) { 185 | Collection devices = values[0]; 186 | mArrayAdapter.clear(); 187 | mArrayAdapter.addAll(devices); 188 | Log.i(TAG, "Device list update."); 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/ui/LibraryFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.ui; 17 | 18 | import android.app.Activity; 19 | import android.app.Fragment; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.os.Bundle; 23 | import android.os.Handler; 24 | import android.os.Message; 25 | import android.support.v4.widget.SwipeRefreshLayout; 26 | import android.util.Log; 27 | import android.view.LayoutInflater; 28 | import android.view.View; 29 | import android.view.ViewGroup; 30 | import android.widget.AdapterView; 31 | import android.widget.ArrayAdapter; 32 | import android.widget.ImageView; 33 | import android.widget.ListView; 34 | import android.widget.TextView; 35 | 36 | import com.kevinshen.beyondupnp.R; 37 | import com.kevinshen.beyondupnp.core.SystemManager; 38 | 39 | import org.fourthline.cling.model.meta.Device; 40 | 41 | import java.util.Collection; 42 | 43 | public class LibraryFragment extends Fragment { 44 | private static final String TAG = LibraryFragment.class.getSimpleName(); 45 | 46 | private ArrayAdapter mArrayAdapter; 47 | private SwipeRefreshLayout mSwipeRefreshLayout; 48 | private ListView mCddListView; 49 | 50 | /** 51 | * Use this factory method to create a new instance of 52 | * this fragment using the provided parameters. 53 | * 54 | * @return A new instance of fragment LibraryFragment. 55 | */ 56 | public static LibraryFragment newInstance() { 57 | LibraryFragment fragment = new LibraryFragment(); 58 | return fragment; 59 | } 60 | 61 | public LibraryFragment() { 62 | // Required empty public constructor 63 | } 64 | 65 | @Override 66 | public void onCreate(Bundle savedInstanceState) { 67 | super.onCreate(savedInstanceState); 68 | } 69 | 70 | @Override 71 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 72 | Bundle savedInstanceState) { 73 | // Inflate the layout for this fragment 74 | View view = inflater.inflate(R.layout.fragment_library, container, false); 75 | //Init content directory device ListView 76 | mCddListView = (ListView)view.findViewById(R.id.content_directory_devices); 77 | mArrayAdapter = new LibraryAdapter(getActivity()); 78 | mCddListView.setAdapter(mArrayAdapter); 79 | mCddListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 80 | @Override 81 | public void onItemClick(AdapterView parent, View view, int position, long id) { 82 | Device device = mArrayAdapter.getItem(position); 83 | String identifierString = device.getIdentity().getUdn().getIdentifierString(); 84 | String objectId = "0"; 85 | //Create new activity 86 | Intent intent = new Intent(getActivity(),ContentContainerActivity.class); 87 | intent.putExtra(ContentContainerActivity.OBJECT_ID_TAG,objectId); 88 | intent.putExtra(ContentContainerActivity.IDENTIFIER_STRING_TAG,identifierString); 89 | intent.putExtra(ContentContainerActivity.CONTENT_CONTAINER_TITLE,device.getDetails().getFriendlyName()); 90 | getActivity().startActivity(intent); 91 | } 92 | }); 93 | 94 | mSwipeRefreshLayout = (SwipeRefreshLayout)view.findViewById(R.id.swipe_refresh_layout_library); 95 | mSwipeRefreshLayout.setOnRefreshListener(onRefreshListener); 96 | //SwipeRefresh style. 97 | mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright); 98 | 99 | return view; 100 | } 101 | 102 | @Override 103 | public void onAttach(Activity activity) { 104 | super.onAttach(activity); 105 | } 106 | 107 | @Override 108 | public void onDetach() { 109 | super.onDetach(); 110 | } 111 | 112 | @Override 113 | public void onResume() { 114 | super.onResume(); 115 | 116 | refreshDeviceList(); 117 | } 118 | 119 | public void refreshDeviceList(){ 120 | Log.i("Library","Refresh List."); 121 | Collection devices = SystemManager.getInstance().getDmcDevices(); 122 | mArrayAdapter.clear(); 123 | mArrayAdapter.addAll(devices); 124 | } 125 | 126 | private Handler handler = new Handler(){ 127 | @Override 128 | public void handleMessage(Message msg) { 129 | super.handleMessage(msg); 130 | } 131 | }; 132 | 133 | private SwipeRefreshLayout.OnRefreshListener onRefreshListener = new SwipeRefreshLayout.OnRefreshListener() { 134 | @Override 135 | public void onRefresh() { 136 | mSwipeRefreshLayout.setRefreshing(true); 137 | mCddListView.setEnabled(false); 138 | 139 | handler.post(new Runnable() { 140 | @Override 141 | public void run() { 142 | mSwipeRefreshLayout.setRefreshing(false); 143 | refreshDeviceList(); 144 | mCddListView.setEnabled(true); 145 | } 146 | }); 147 | } 148 | }; 149 | 150 | private class LibraryAdapter extends ArrayAdapter { 151 | private LayoutInflater mInflater; 152 | 153 | public LibraryAdapter(Context context) { 154 | super(context, 0); 155 | mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 156 | } 157 | 158 | @Override 159 | public View getView(int position, View convertView, ViewGroup parent) { 160 | if (convertView == null) 161 | convertView = mInflater.inflate(R.layout.listview_items, null); 162 | 163 | Device item = getItem(position); 164 | if (item == null) { 165 | return convertView; 166 | } 167 | 168 | ImageView imageView = (ImageView)convertView.findViewById(R.id.listview_item_image); 169 | imageView.setBackgroundResource(R.drawable.ic_action_dock); 170 | 171 | TextView textView = (TextView) convertView.findViewById(R.id.listview_item_line_one); 172 | textView.setText(item.getDetails().getFriendlyName()); 173 | return convertView; 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.ui; 17 | 18 | import android.app.Activity; 19 | import android.app.DialogFragment; 20 | import android.app.Fragment; 21 | import android.app.FragmentManager; 22 | import android.app.FragmentTransaction; 23 | import android.content.ComponentName; 24 | import android.content.Context; 25 | import android.content.Intent; 26 | import android.content.ServiceConnection; 27 | import android.os.Binder; 28 | import android.os.Bundle; 29 | import android.os.IBinder; 30 | import android.support.v13.app.FragmentPagerAdapter; 31 | import android.support.v4.view.ViewPager; 32 | import android.view.KeyEvent; 33 | import android.view.Menu; 34 | import android.view.MenuItem; 35 | 36 | import com.astuetz.PagerSlidingTabStrip; 37 | import com.kevinshen.beyondupnp.BeyondApplication; 38 | import com.kevinshen.beyondupnp.service.BeyondUpnpService; 39 | import com.kevinshen.beyondupnp.Intents; 40 | import com.kevinshen.beyondupnp.R; 41 | import com.kevinshen.beyondupnp.service.SystemService; 42 | import com.kevinshen.beyondupnp.core.SystemManager; 43 | 44 | import org.fourthline.cling.android.AndroidUpnpService; 45 | 46 | import java.util.HashMap; 47 | 48 | public class MainActivity extends Activity implements ViewPager.OnPageChangeListener { 49 | public static final String TAG = MainActivity.class.getSimpleName(); 50 | 51 | public static final String DIALOG_FRAGMENT_TAG = "dialog"; 52 | private PagerSlidingTabStrip mTabs; 53 | private ViewPager mPager; 54 | private PagerAdapter mPagerAdapter; 55 | 56 | private static final int NOWPLAYING_FRAGMENT_INDEX = 0; 57 | private static final int PLAYLIST_FRAGMENT_INDEX = 1; 58 | private static final int LIBRARY_FRAGMENT_INDEX = 2; 59 | 60 | private HashMap mFragmentArrayMap; 61 | private BeyondApplication mBeyondApplication; 62 | 63 | @Override 64 | protected void onCreate(Bundle savedInstanceState) { 65 | super.onCreate(savedInstanceState); 66 | setContentView(R.layout.activity_main); 67 | 68 | mBeyondApplication = (BeyondApplication)getApplication(); 69 | 70 | mTabs = (PagerSlidingTabStrip) findViewById(R.id.tabs); 71 | mTabs.setShouldExpand(true); 72 | 73 | mPager = (ViewPager) findViewById(R.id.pager); 74 | mPagerAdapter = new PagerAdapter(getFragmentManager()); 75 | 76 | mPager.setAdapter(mPagerAdapter); 77 | mPager.setOffscreenPageLimit(2); 78 | 79 | mTabs.setViewPager(mPager); 80 | mTabs.setOnPageChangeListener(this); 81 | //Init fragment map 82 | mFragmentArrayMap = new HashMap<>(3); 83 | // Bind UPnP service 84 | Intent upnpServiceIntent = new Intent(MainActivity.this, BeyondUpnpService.class); 85 | bindService(upnpServiceIntent, mUpnpServiceConnection, Context.BIND_AUTO_CREATE); 86 | // Bind System service 87 | Intent systemServiceIntent = new Intent(MainActivity.this, SystemService.class); 88 | bindService(systemServiceIntent, mSystemServiceConnection, Context.BIND_AUTO_CREATE); 89 | } 90 | 91 | @Override 92 | protected void onSaveInstanceState(Bundle outState) { 93 | //super.onSaveInstanceState(outState); 94 | } 95 | 96 | @Override 97 | public boolean onCreateOptionsMenu(Menu menu) { 98 | // Inflate the menu; this adds items to the action bar if it is present. 99 | getMenuInflater().inflate(R.menu.menu_main, menu); 100 | return true; 101 | } 102 | 103 | @Override 104 | public boolean onOptionsItemSelected(MenuItem item) { 105 | // Handle action bar item clicks here. The action bar will 106 | // automatically handle clicks on the Home/Up button, so long 107 | // as you specify a parent activity in AndroidManifest.xml. 108 | int id = item.getItemId(); 109 | 110 | //noinspection SimplifiableIfStatement 111 | if (id == R.id.action_select) { 112 | FragmentTransaction ft = getFragmentManager().beginTransaction(); 113 | Fragment prev = getFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG); 114 | if (prev != null) { 115 | ft.remove(prev); 116 | } 117 | ft.addToBackStack(null); 118 | 119 | // Create and show the dialog. 120 | DialogFragment newFragment = DeviceListDialogFragment.newInstance(); 121 | newFragment.show(ft, DIALOG_FRAGMENT_TAG); 122 | } 123 | 124 | return super.onOptionsItemSelected(item); 125 | } 126 | 127 | @Override 128 | protected void onDestroy() { 129 | super.onDestroy(); 130 | 131 | // Unbind UPnP service 132 | unbindService(mUpnpServiceConnection); 133 | // Unbind System service 134 | unbindService(mSystemServiceConnection); 135 | 136 | mFragmentArrayMap.clear(); 137 | mFragmentArrayMap = null; 138 | 139 | mBeyondApplication.stopServer(); 140 | } 141 | 142 | private ServiceConnection mUpnpServiceConnection = new ServiceConnection() { 143 | @Override 144 | public void onServiceConnected(ComponentName className, IBinder service) { 145 | BeyondUpnpService.LocalBinder binder = (BeyondUpnpService.LocalBinder) service; 146 | BeyondUpnpService beyondUpnpService = binder.getService(); 147 | 148 | SystemManager systemManager = SystemManager.getInstance(); 149 | systemManager.setUpnpService(beyondUpnpService); 150 | //Search on service created. 151 | systemManager.searchAllDevices(); 152 | } 153 | 154 | @Override 155 | public void onServiceDisconnected(ComponentName className) { 156 | SystemManager.getInstance().setUpnpService(null); 157 | } 158 | }; 159 | 160 | private ServiceConnection mSystemServiceConnection = new ServiceConnection() { 161 | @Override 162 | public void onServiceConnected(ComponentName className, IBinder service) { 163 | SystemService.SystemServiceBinder systemServiceBinder = (SystemService.SystemServiceBinder) service; 164 | //Set binder to SystemManager 165 | SystemManager systemManager = SystemManager.getInstance(); 166 | systemManager.setSystemServiceBinder(systemServiceBinder); 167 | } 168 | 169 | @Override 170 | public void onServiceDisconnected(ComponentName className) { 171 | SystemManager.getInstance().setUpnpService(null); 172 | } 173 | }; 174 | 175 | @Override 176 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 177 | 178 | } 179 | 180 | @Override 181 | public void onPageSelected(int position) { 182 | Fragment fragment = mPagerAdapter.getItem(position); 183 | //Refresh LibraryFragment when it selected 184 | if (position == LIBRARY_FRAGMENT_INDEX){ 185 | ((LibraryFragment)fragment).refreshDeviceList(); 186 | } 187 | } 188 | 189 | @Override 190 | public void onPageScrollStateChanged(int state) { 191 | 192 | } 193 | 194 | @Override 195 | public boolean onKeyDown(int keyCode, KeyEvent event) { 196 | SystemManager systemManager = SystemManager.getInstance(); 197 | if (keyCode == KeyEvent.KEYCODE_VOLUME_UP){ 198 | int volume = systemManager.getDeviceVolume(); 199 | volume += 5; 200 | if (volume > 100) 201 | volume = 100; 202 | sendBroadcast(new Intent(Intents.ACTION_SET_VOLUME).putExtra("currentVolume",volume)); 203 | return true; 204 | }else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN){ 205 | int volume = systemManager.getDeviceVolume(); 206 | volume -= 5; 207 | if (volume < 0) 208 | volume = 0; 209 | sendBroadcast(new Intent(Intents.ACTION_SET_VOLUME).putExtra("currentVolume",volume)); 210 | return true; 211 | }else { 212 | return super.onKeyDown(keyCode, event); 213 | } 214 | } 215 | 216 | private class PagerAdapter extends FragmentPagerAdapter { 217 | 218 | private final String[] TITLES = {"Playing", "Playlist", "Library"}; 219 | 220 | public PagerAdapter(FragmentManager fm) { 221 | super(fm); 222 | } 223 | 224 | @Override 225 | public CharSequence getPageTitle(int position) { 226 | return TITLES[position]; 227 | } 228 | 229 | @Override 230 | public int getCount() { 231 | return TITLES.length; 232 | } 233 | 234 | @Override 235 | public Fragment getItem(int position) { 236 | Fragment fragment = mFragmentArrayMap.get(position); 237 | if (fragment == null){ 238 | switch (position) { 239 | case NOWPLAYING_FRAGMENT_INDEX: 240 | fragment = NowplayingFragment.newInstance(); 241 | mFragmentArrayMap.put(NOWPLAYING_FRAGMENT_INDEX,fragment); 242 | break; 243 | case PLAYLIST_FRAGMENT_INDEX: 244 | fragment = PlaylistFragment.newInstance(); 245 | mFragmentArrayMap.put(PLAYLIST_FRAGMENT_INDEX,fragment); 246 | break; 247 | case LIBRARY_FRAGMENT_INDEX: 248 | fragment = LibraryFragment.newInstance(); 249 | mFragmentArrayMap.put(LIBRARY_FRAGMENT_INDEX,fragment); 250 | break; 251 | } 252 | } 253 | return fragment; 254 | } 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/ui/PlaylistFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.ui; 17 | 18 | import android.app.Activity; 19 | import android.app.Fragment; 20 | import android.app.LoaderManager; 21 | import android.content.ContentUris; 22 | import android.content.CursorLoader; 23 | import android.content.Loader; 24 | import android.database.ContentObserver; 25 | import android.database.Cursor; 26 | import android.net.Uri; 27 | import android.os.Bundle; 28 | import android.os.Handler; 29 | import android.view.ActionMode; 30 | import android.view.LayoutInflater; 31 | import android.view.Menu; 32 | import android.view.MenuInflater; 33 | import android.view.MenuItem; 34 | import android.view.View; 35 | import android.view.ViewGroup; 36 | import android.widget.AdapterView; 37 | import android.widget.ListView; 38 | import android.widget.SimpleCursorAdapter; 39 | 40 | import com.kevinshen.beyondupnp.R; 41 | import com.kevinshen.beyondupnp.core.PlaybackCommand; 42 | import com.kevinshen.beyondupnp.database.BeyondUpnpContract; 43 | import com.kevinshen.beyondupnp.database.BeyondUpnpContract.PlaylistItem; 44 | 45 | public class PlaylistFragment extends Fragment implements LoaderManager.LoaderCallbacks { 46 | private SimpleCursorAdapter mAdapter; 47 | private ListView mListView; 48 | 49 | public static PlaylistFragment newInstance() { 50 | PlaylistFragment fragment = new PlaylistFragment(); 51 | return fragment; 52 | } 53 | 54 | @Override 55 | public void onCreate(Bundle savedInstanceState) { 56 | super.onCreate(savedInstanceState); 57 | 58 | } 59 | 60 | @Override 61 | public View onCreateView(LayoutInflater inflater, final ViewGroup container, 62 | Bundle savedInstanceState) { 63 | // Inflate the layout for this fragment 64 | View view = inflater.inflate(R.layout.fragment_playlist, container, false); 65 | 66 | // Create an empty adapter we will use to display the loaded data. 67 | mAdapter = new SimpleCursorAdapter(getActivity(), 68 | android.R.layout.simple_list_item_activated_1, null, 69 | new String[]{PlaylistItem.ITEM_TITLE}, 70 | new int[]{android.R.id.text1}, 0); 71 | 72 | mListView = (ListView) view.findViewById(android.R.id.list); 73 | mListView.setAdapter(mAdapter); 74 | mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); 75 | mListView.setMultiChoiceModeListener(new MultipleModeCallback()); 76 | mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 77 | @Override 78 | public void onItemClick(AdapterView parent, View view, int position, long id) { 79 | Cursor cursor = (Cursor) mAdapter.getItem(position); 80 | String itemUri = cursor.getString(cursor.getColumnIndex(PlaylistItem.ITEM_URI)); 81 | String metadata = cursor.getString(cursor.getColumnIndex(PlaylistItem.ITEM_METADATA)); 82 | PlaybackCommand.playNewItem(itemUri, metadata); 83 | } 84 | }); 85 | 86 | return view; 87 | } 88 | 89 | @Override 90 | public void onActivityCreated(Bundle savedInstanceState) { 91 | super.onActivityCreated(savedInstanceState); 92 | 93 | // Prepare the loader. Either re-connect with an existing one, 94 | // or start a new one. 95 | getLoaderManager().initLoader(0, null, this); 96 | } 97 | 98 | @Override 99 | public void onAttach(Activity activity) { 100 | super.onAttach(activity); 101 | activity.getContentResolver().registerContentObserver(PlaylistItem.CONTENT_URI, true, mObserver); 102 | } 103 | 104 | @Override 105 | public void onDetach() { 106 | super.onDetach(); 107 | getActivity().getContentResolver().unregisterContentObserver(mObserver); 108 | } 109 | 110 | private class MultipleModeCallback implements ListView.MultiChoiceModeListener { 111 | 112 | public boolean onCreateActionMode(ActionMode mode, Menu menu) { 113 | MenuInflater inflater = getActivity().getMenuInflater(); 114 | inflater.inflate(R.menu.menu_list_multi_select, menu); 115 | mode.setTitle("Select Items"); 116 | setSubtitle(mode); 117 | return true; 118 | } 119 | 120 | public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 121 | return true; 122 | } 123 | 124 | @Override 125 | public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 126 | switch (item.getItemId()) { 127 | case R.id.menu_item_delete: 128 | //Delete selected items by id. 129 | for (long item_id : mListView.getCheckedItemIds()){ 130 | getActivity().getContentResolver(). 131 | delete(ContentUris.withAppendedId(PlaylistItem.CONTENT_URI,item_id),null,null); 132 | } 133 | //Finish 134 | mode.finish(); 135 | break; 136 | default: 137 | break; 138 | } 139 | return true; 140 | } 141 | 142 | public void onDestroyActionMode(ActionMode mode) { 143 | } 144 | 145 | public void onItemCheckedStateChanged(ActionMode mode, 146 | int position, long id, boolean checked) { 147 | setSubtitle(mode); 148 | } 149 | 150 | private void setSubtitle(ActionMode mode) { 151 | final int checkedCount = mListView.getCheckedItemCount(); 152 | switch (checkedCount) { 153 | case 0: 154 | mode.setSubtitle(null); 155 | break; 156 | case 1: 157 | mode.setSubtitle("One item selected"); 158 | break; 159 | default: 160 | mode.setSubtitle("" + checkedCount + " items selected"); 161 | break; 162 | } 163 | } 164 | } 165 | 166 | private final ContentObserver mObserver = new ContentObserver(new Handler()) { 167 | @Override 168 | public void onChange(boolean selfChange) { 169 | if (!isAdded()) { 170 | return; 171 | } 172 | 173 | //Reload data 174 | LoaderManager lm = getLoaderManager(); 175 | Loader loader = lm.getLoader(0); 176 | if (loader != null) { 177 | loader.forceLoad(); 178 | } 179 | } 180 | }; 181 | 182 | @Override 183 | public Loader onCreateLoader(int id, Bundle args) { 184 | Uri baseUri = PlaylistItem.CONTENT_URI; 185 | 186 | return new CursorLoader(getActivity(), baseUri, 187 | null, null, null, 188 | PlaylistItem.ITEM_DATE + " COLLATE LOCALIZED ASC"); 189 | } 190 | 191 | @Override 192 | public void onLoadFinished(Loader loader, Cursor data) { 193 | mAdapter.swapCursor(data); 194 | } 195 | 196 | @Override 197 | public void onLoaderReset(Loader loader) { 198 | mAdapter.swapCursor(null); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/util/MD5.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.util; 17 | 18 | import java.security.MessageDigest; 19 | import java.security.NoSuchAlgorithmException; 20 | 21 | public class MD5 { 22 | 23 | public static String createMD5(String val) throws NoSuchAlgorithmException { 24 | MessageDigest md5 = MessageDigest.getInstance("MD5"); 25 | md5.update(val.getBytes()); 26 | byte[] m = md5.digest(); 27 | 28 | StringBuilder sb = new StringBuilder(); 29 | for(byte b : m){ 30 | sb.append(b); 31 | } 32 | 33 | return sb.toString(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/kevinshen/beyondupnp/util/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Kevin Shen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.kevinshen.beyondupnp.util; 17 | 18 | import org.apache.http.conn.util.InetAddressUtils; 19 | 20 | import java.net.InetAddress; 21 | import java.net.NetworkInterface; 22 | import java.util.Collections; 23 | import java.util.List; 24 | 25 | public class Utils { 26 | public static final String WLAN0 = "wlan0"; 27 | 28 | /** 29 | * Get IP address from first non-localhost interface 30 | * 31 | * @param useIPv4 true=return ipv4, false=return ipv6 32 | * @return address or empty string 33 | */ 34 | public static String getIPAddress(boolean useIPv4) { 35 | try { 36 | List interfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); 37 | for (NetworkInterface intf : interfaces) { 38 | List addrs = Collections.list(intf.getInetAddresses()); 39 | for (InetAddress addr : addrs) { 40 | if (!addr.isLoopbackAddress()) { 41 | String sAddr = addr.getHostAddress().toUpperCase(); 42 | boolean isIPv4 = InetAddressUtils.isIPv4Address(sAddr); 43 | if (useIPv4) { 44 | if (isIPv4) 45 | return sAddr; 46 | } else { 47 | if (!isIPv4) { 48 | int delim = sAddr.indexOf('%'); // drop ip6 port suffix 49 | return delim < 0 ? sAddr : sAddr.substring(0, delim); 50 | } 51 | } 52 | } 53 | } 54 | } 55 | } catch (Exception ex) { 56 | } // for now eat exceptions 57 | return ""; 58 | } 59 | 60 | /** 61 | * Returns MAC address of the given interface name. 62 | * 63 | * @param interfaceName eth0, wlan0 or NULL=use first interface 64 | * @return mac address or empty string 65 | */ 66 | public static String getMACAddress(String interfaceName) { 67 | try { 68 | List interfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); 69 | for (NetworkInterface intf : interfaces) { 70 | if (interfaceName != null) { 71 | if (!intf.getName().equalsIgnoreCase(interfaceName)) continue; 72 | } 73 | byte[] mac = intf.getHardwareAddress(); 74 | if (mac == null) return ""; 75 | StringBuilder buf = new StringBuilder(); 76 | for (int idx = 0; idx < mac.length; idx++) 77 | buf.append(String.format("%02X:", mac[idx])); 78 | if (buf.length() > 0) buf.deleteCharAt(buf.length() - 1); 79 | return buf.toString(); 80 | } 81 | } catch (Exception ex) { 82 | } // for now eat exceptions 83 | return ""; 84 | } 85 | 86 | /** 87 | * Parse path url, obtain database id from file name. 88 | * 89 | * @param path 90 | * @return 91 | */ 92 | public static String parseResourceId(String path) { 93 | String result = null; 94 | if (path != null && path.length() > 0) { 95 | int index = path.lastIndexOf("/"); 96 | //File name like "id".mp3 97 | String fileName = path.substring(index + 1); 98 | result = fileName.substring(0, fileName.lastIndexOf(".")); 99 | } 100 | 101 | return result; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/res/color/listview_item_bg_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/color/tab_text_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/actionbar.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/actionbar.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/appwidget_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/appwidget_bg.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/appwidget_bg_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/appwidget_bg_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/bar_graph.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/bar_graph.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/beyond_holo_light_favorite_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/beyond_holo_light_favorite_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/beyond_holo_light_favorite_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/beyond_holo_light_favorite_selected.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/beyond_holo_light_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/beyond_holo_light_next.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/beyond_holo_light_overflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/beyond_holo_light_overflow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/beyond_holo_light_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/beyond_holo_light_pause.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/beyond_holo_light_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/beyond_holo_light_play.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/beyond_holo_light_previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/beyond_holo_light_previous.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/beyond_holo_light_repeat_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/beyond_holo_light_repeat_all.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/beyond_holo_light_repeat_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/beyond_holo_light_repeat_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/beyond_holo_light_repeat_one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/beyond_holo_light_repeat_one.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/beyond_holo_light_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/beyond_holo_light_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/beyond_holo_light_shuffle_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/beyond_holo_light_shuffle_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/beyond_holo_light_shuffle_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/beyond_holo_light_shuffle_on.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/beyond_settings_themes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/beyond_settings_themes.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/btn_drag_sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/btn_drag_sort.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/btn_switch_queue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/btn_switch_queue.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/btn_switch_queue_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/btn_switch_queue_active.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/dropdown_ic_arrow_normal_holo_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/dropdown_ic_arrow_normal_holo_light.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/list_section_divider_holo_custom.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/list_section_divider_holo_custom.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/notify_panel_notification_icon_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/notify_panel_notification_icon_bg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/queue_thumbnail_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/queue_thumbnail_bg.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/recents_thumbnail_bg_press.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/recents_thumbnail_bg_press.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/scrubber_primary_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/scrubber_primary_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/scrubber_secondary_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/scrubber_secondary_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/scrubber_track_holo_dark.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/scrubber_track_holo_dark.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/stat_notify_music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/stat_notify_music.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/tab_selected_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/tab_selected_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/tab_selected_pressed_focused_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/tab_selected_pressed_focused_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/tab_selected_pressed_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/tab_selected_pressed_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/tab_unselected_focused_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/tab_unselected_focused_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/tab_unselected_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/tab_unselected_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/tab_unselected_pressed_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/tab_unselected_pressed_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/title_bar_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/title_bar_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/view_more_album.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/view_more_album.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/view_more_song.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-hdpi/view_more_song.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/actionbar.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/actionbar.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/appwidget_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/appwidget_bg.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/beyond_holo_light_favorite_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/beyond_holo_light_favorite_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/beyond_holo_light_favorite_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/beyond_holo_light_favorite_selected.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/beyond_holo_light_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/beyond_holo_light_next.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/beyond_holo_light_overflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/beyond_holo_light_overflow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/beyond_holo_light_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/beyond_holo_light_pause.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/beyond_holo_light_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/beyond_holo_light_play.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/beyond_holo_light_previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/beyond_holo_light_previous.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/beyond_holo_light_repeat_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/beyond_holo_light_repeat_all.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/beyond_holo_light_repeat_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/beyond_holo_light_repeat_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/beyond_holo_light_repeat_one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/beyond_holo_light_repeat_one.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/beyond_holo_light_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/beyond_holo_light_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/beyond_holo_light_shuffle_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/beyond_holo_light_shuffle_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/beyond_holo_light_shuffle_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/beyond_holo_light_shuffle_on.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/beyond_settings_themes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/beyond_settings_themes.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/btn_drag_sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/btn_drag_sort.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/btn_switch_queue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/btn_switch_queue.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/btn_switch_queue_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/btn_switch_queue_active.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/dropdown_ic_arrow_normal_holo_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/dropdown_ic_arrow_normal_holo_light.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/list_section_divider_holo_custom.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/list_section_divider_holo_custom.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/notify_panel_notification_icon_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/notify_panel_notification_icon_bg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/queue_thumbnail_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/queue_thumbnail_bg.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/recents_thumbnail_bg_press.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/recents_thumbnail_bg_press.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/scrubber_primary_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/scrubber_primary_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/scrubber_secondary_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/scrubber_secondary_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/scrubber_track_holo_dark.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/scrubber_track_holo_dark.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/stat_notify_music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/stat_notify_music.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/tab_selected_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/tab_selected_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/tab_selected_pressed_focused_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/tab_selected_pressed_focused_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/tab_selected_pressed_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/tab_selected_pressed_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/tab_unselected_focused_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/tab_unselected_focused_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/tab_unselected_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/tab_unselected_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/tab_unselected_pressed_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/tab_unselected_pressed_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/title_bar_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/title_bar_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/view_more_album.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/view_more_album.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/view_more_song.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-mdpi/view_more_song.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/actionbar.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/actionbar.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/appwidget_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/appwidget_bg.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/beyond_holo_light_favorite_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/beyond_holo_light_favorite_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/beyond_holo_light_favorite_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/beyond_holo_light_favorite_selected.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/beyond_holo_light_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/beyond_holo_light_next.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/beyond_holo_light_overflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/beyond_holo_light_overflow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/beyond_holo_light_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/beyond_holo_light_pause.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/beyond_holo_light_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/beyond_holo_light_play.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/beyond_holo_light_previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/beyond_holo_light_previous.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/beyond_holo_light_repeat_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/beyond_holo_light_repeat_all.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/beyond_holo_light_repeat_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/beyond_holo_light_repeat_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/beyond_holo_light_repeat_one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/beyond_holo_light_repeat_one.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/beyond_holo_light_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/beyond_holo_light_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/beyond_holo_light_shuffle_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/beyond_holo_light_shuffle_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/beyond_holo_light_shuffle_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/beyond_holo_light_shuffle_on.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/beyond_settings_themes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/beyond_settings_themes.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/btn_drag_sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/btn_drag_sort.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/btn_switch_queue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/btn_switch_queue.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/btn_switch_queue_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/btn_switch_queue_active.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/dropdown_ic_arrow_normal_holo_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/dropdown_ic_arrow_normal_holo_light.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/ic_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/list_section_divider_holo_custom.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/list_section_divider_holo_custom.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/notify_panel_notification_icon_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/notify_panel_notification_icon_bg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/queue_thumbnail_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/queue_thumbnail_bg.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/recents_thumbnail_bg_press.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/recents_thumbnail_bg_press.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/scrubber_primary_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/scrubber_primary_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/scrubber_secondary_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/scrubber_secondary_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/scrubber_track_holo_dark.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/scrubber_track_holo_dark.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/stat_notify_music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/stat_notify_music.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/tab_selected_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/tab_selected_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/tab_selected_pressed_focused_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/tab_selected_pressed_focused_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/tab_selected_pressed_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/tab_selected_pressed_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/tab_unselected_focused_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/tab_unselected_focused_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/tab_unselected_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/tab_unselected_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/tab_unselected_pressed_holo.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/tab_unselected_pressed_holo.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/title_bar_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/title_bar_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/view_more_album.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/view_more_album.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/view_more_song.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable-xhdpi/view_more_song.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/audio_player_seekbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 22 | 23 | 26 | 27 | 28 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/colorstrip_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable/colorstrip_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/gridview_item_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/holo_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_dock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable/ic_action_dock.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/listview_item_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/no_art_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable/no_art_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/no_art_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable/no_art_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/promo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/app/src/main/res/drawable/promo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/queue_thumbnail_fg.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shuffle_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 13 | 14 | 17 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/status_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/tab.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/view_more.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/viewpager_margin.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_content_container.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/audio_controls.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 17 | 18 | 28 | 29 | 39 | 40 | 41 | 47 | 48 | 54 | 55 | 59 | 60 | 65 | 66 | 70 | 71 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /app/src/main/res/layout/context_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_device_list_dialog.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_library.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_nowplaying.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 16 | 17 | 21 | 22 | 23 | 29 | 30 | 35 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_playlist.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/list_separator.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/listview.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/listview_items.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | 32 | 33 | 45 | 46 | 56 | 57 | -------------------------------------------------------------------------------- /app/src/main/res/layout/listview_items_playlist.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/shadow.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_content_container.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_item_actions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_list_multi_select.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | #aa000000 6 | 7 | 8 | #bb000000 9 | 10 | 11 | #77000000 12 | 13 | 14 | #ff0099cc 15 | 16 | #ffdedede 17 | 18 | #ffdedede 19 | 20 | 21 | #00000000 22 | 23 | 24 | #ff000000 25 | 26 | 27 | #ffffffff 28 | 29 | 30 | #eeffffff 31 | 32 | 33 | #99ffffff 34 | 35 | 36 | #E2E2E2 37 | #FFFFFF 38 | #1E1E1E 39 | #AEAEAE 40 | #A8A8A8 41 | #3797C7 42 | #666666 43 | #ff53c1bd 44 | #99000000 45 | #f4f4f4 46 | #F9F9F9 47 | #E3E3E3 48 | #E8E8E8 49 | #6E6E6E 50 | #181818 51 | #282828 52 | #515252 53 | #E8E8E0 54 | #DBDBD3 55 | -------------------------------------------------------------------------------- /app/src/main/res/values/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 0 6 | 7 | 8 | 133 9 | 10 | 11 | 200 12 | 13 | 14 | 2 15 | 16 | 17 | 16 18 | 32 19 | 20 | 21 | 150 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10sp 6 | 12sp 7 | 14sp 8 | 16sp 9 | 18sp 10 | 11 | 12 | 6dp 13 | 40dp 14 | 40dp 15 | 16 | 17 | 68dp 18 | 12dp 19 | 36dp 20 | 6dp 21 | 45dp 22 | 2dp 23 | 12dp 24 | 25 | 26 | 0dp 27 | 28 | 29 | 32dp 30 | 31 | 32 | 8dp 33 | 32dp 34 | 16dp 35 | 36 | 37 | 54dp 38 | 9dp 39 | 4dp 40 | 16dp 41 | 50dp 42 | 5dp 43 | 10dp 44 | 80dp 45 | 70dp 46 | 44dp 47 | 4dp 48 | 49 | 50 | 47dp 51 | 9dp 52 | 4dp 53 | 16dp 54 | 5dp 55 | 10dp 56 | 57 | 58 | 4dp 59 | 30dp 60 | 1dp 61 | 5dp 62 | 63 | 64 | 48dp 65 | 64dp 66 | 11dp 67 | 128.0dip 68 | 48.0dip 69 | 8.0dip 70 | 71 | 72 | 4dp 73 | 74 | 75 | 5dp 76 | 250dp 77 | 3dp 78 | 70dp 79 | 4dp 80 | 81 | 82 | 5dp 83 | 15dp 84 | 85 | 86 | 152dp 87 | 132dp 88 | 168dp 89 | 7dp 90 | 6dp 91 | 4dp 92 | 10dp 93 | 64dp 94 | 12dp 95 | 12dp 96 | 15dp 97 | 5dp 98 | 10dp 99 | 8dp 100 | 101 | 102 | 16dp 103 | 20dp 104 | 56dp 105 | 10dp 106 | 2dp 107 | 108 | 109 | 110 | 20dp 111 | 112 | 88dp 113 | 114 | 0dp 115 | 116 | 0dp 117 | 118 | 0dp 119 | 8dp 120 | 121 | 122 | 48dp 123 | 124 | 48dp 125 | 126 | 127 | 164dp 128 | 145dp 129 | 130 | 131 | 14dp 132 | 133 | 14dp 134 | 135 | 20dp 136 | 137 | 138 | 62dp 139 | 72dp 140 | 141 | 142 | 90dp 143 | 144 | 145 | 180dp 146 | 55dp 147 | 135dp 148 | 149 | 150 | 3dp 151 | 6dp 152 | 10dp 153 | 26dp 154 | 30dp 155 | 10dp 156 | 20dp 157 | 158 | 159 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.0.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | ANDROID_BUILD_TARGET_SDK_VERSION=19 21 | ANDROID_BUILD_TOOLS_VERSION=19.1.0 22 | ANDROID_BUILD_SDK_VERSION=19 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkwtz/BeyondUPnP/1a82780ae499c37ac0a912f86735b1906ac731da/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Dec 17 12:38:00 CST 2014 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /import-summary.txt: -------------------------------------------------------------------------------- 1 | ECLIPSE ANDROID PROJECT IMPORT SUMMARY 2 | ====================================== 3 | 4 | Ignored Files: 5 | -------------- 6 | The following files were *not* copied into the new Gradle project; you 7 | should evaluate whether these are still needed in your project and if 8 | so manually move them: 9 | 10 | * LICENSE 11 | * pom.xml 12 | 13 | Moved Files: 14 | ------------ 15 | Android Gradle projects use a different directory structure than ADT 16 | Eclipse projects. Here's how the projects were restructured: 17 | 18 | * AndroidManifest.xml => PullToRefresh\src\main\AndroidManifest.xml 19 | * res\ => PullToRefresh\src\main\res\ 20 | * src\ => PullToRefresh\src\main\java\ 21 | 22 | Next Steps: 23 | ----------- 24 | You can now build the project. The Gradle project needs network 25 | connectivity to download dependencies. 26 | 27 | Bugs: 28 | ----- 29 | If for some reason your project does not build, and you determine that 30 | it is due to a bug or limitation of the Eclipse to Gradle importer, 31 | please file a bug at http://b.android.com with category 32 | Component-Tools. 33 | 34 | (This import summary is for your information only, and can be deleted 35 | after import once you are satisfied with the results.) 36 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':PagerSlidingTabStrip' 2 | --------------------------------------------------------------------------------