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 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_item_actions.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_list_multi_select.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
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 |
--------------------------------------------------------------------------------