keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore
- *
- *
- *
- *
- * Send the certificate request to the trusted Certificate Authority for signature.
- * One may choose to act as her own CA and sign the certificate request using a PKI
- * tool, such as OpenSSL.
- *
- *
- *
- *
- * Import the trusted CA root certificate
- *
keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore
- *
- *
- *
- *
- * Import the PKCS#7 file containg the complete certificate chain
- *
keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore
- *
- *
- *
- *
- * Verify the content the resultant keystore file
- *
keytool -list -v -keystore my.keystore
- *
- *
- *
- *
- * @since 4.0
- */
-public class SSLSocketFactory implements LayeredSocketFactory {
- private static final String TAG = SSLSocketFactory.class.getSimpleName();
- public static final String TLS = "TLS";
-
- public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER
- = new AllowAllHostnameVerifier();
-
- public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
- = new BrowserCompatHostnameVerifier();
-
- public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER
- = new StrictHostnameVerifier();
-
- /**
- * The default factory using the default JVM settings for secure connections.
- */
- private static final SSLSocketFactory DEFAULT_FACTORY = new SSLSocketFactory();
-
- /**
- * Gets the default factory, which uses the default JVM settings for secure
- * connections.
- *
- * @return the default factory
- */
- public static SSLSocketFactory getSocketFactory() {
- return DEFAULT_FACTORY;
- }
-
- private final javax.net.ssl.SSLSocketFactory socketfactory;
- private final HostNameResolver nameResolver;
- // TODO: make final
- private volatile X509HostnameVerifier hostnameVerifier;
-
- private static SSLContext createSSLContext(
- String algorithm,
- final KeyStore keystore,
- final String keystorePassword,
- final KeyStore truststore,
- final SecureRandom random,
- final TrustStrategy trustStrategy)
- throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, KeyManagementException {
- if (algorithm == null) {
- algorithm = TLS;
- }
- KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
- KeyManagerFactory.getDefaultAlgorithm());
- kmfactory.init(keystore, keystorePassword != null ? keystorePassword.toCharArray(): null);
- KeyManager[] keymanagers = kmfactory.getKeyManagers();
- TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
- TrustManagerFactory.getDefaultAlgorithm());
- tmfactory.init(keystore);
- TrustManager[] trustmanagers = tmfactory.getTrustManagers();
- if (trustmanagers != null && trustStrategy != null) {
- for (int i = 0; i < trustmanagers.length; i++) {
- TrustManager tm = trustmanagers[i];
- if (tm instanceof X509TrustManager) {
- trustmanagers[i] = new TrustManagerDecorator(
- (X509TrustManager) tm, trustStrategy);
- }
- }
- }
-
- SSLContext sslcontext = SSLContext.getInstance(algorithm);
- sslcontext.init(keymanagers, trustmanagers, random);
- return sslcontext;
- }
-
- /**
- * @deprecated Use {@link #SSLSocketFactory(String, KeyStore, String, KeyStore, SecureRandom, X509HostnameVerifier)}
- */
- @Deprecated
- public SSLSocketFactory(
- final String algorithm,
- final KeyStore keystore,
- final String keystorePassword,
- final KeyStore truststore,
- final SecureRandom random,
- final HostNameResolver nameResolver)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(createSSLContext(
- algorithm, keystore, keystorePassword, truststore, random, null),
- nameResolver);
- }
-
- /**
- * @since 4.1
- */
- public SSLSocketFactory(
- String algorithm,
- final KeyStore keystore,
- final String keystorePassword,
- final KeyStore truststore,
- final SecureRandom random,
- final X509HostnameVerifier hostnameVerifier)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(createSSLContext(
- algorithm, keystore, keystorePassword, truststore, random, null),
- hostnameVerifier);
- }
-
- /**
- * @since 4.1
- */
- public SSLSocketFactory(
- String algorithm,
- final KeyStore keystore,
- final String keystorePassword,
- final KeyStore truststore,
- final SecureRandom random,
- final TrustStrategy trustStrategy,
- final X509HostnameVerifier hostnameVerifier)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(createSSLContext(
- algorithm, keystore, keystorePassword, truststore, random, trustStrategy),
- hostnameVerifier);
- }
-
- public SSLSocketFactory(
- final KeyStore keystore,
- final String keystorePassword,
- final KeyStore truststore)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(TLS, keystore, keystorePassword, truststore, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- }
-
- public SSLSocketFactory(
- final KeyStore keystore,
- final String keystorePassword)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException{
- this(TLS, keystore, keystorePassword, null, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- }
-
- public SSLSocketFactory(
- final KeyStore truststore)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(TLS, null, null, truststore, null, null, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- }
-
- /**
- * @since 4.1
- */
- public SSLSocketFactory(
- final TrustStrategy trustStrategy,
- final X509HostnameVerifier hostnameVerifier)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(TLS, null, null, null, null, trustStrategy, hostnameVerifier);
- }
-
- /**
- * @since 4.1
- */
- public SSLSocketFactory(
- final TrustStrategy trustStrategy)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
- this(TLS, null, null, null, null, trustStrategy, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- }
-
- public SSLSocketFactory(final SSLContext sslContext) {
- this(sslContext, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- }
-
- /**
- * @deprecated Use {@link #SSLSocketFactory(SSLContext)}
- */
- @Deprecated
- public SSLSocketFactory(
- final SSLContext sslContext, final HostNameResolver nameResolver) {
- super();
- this.socketfactory = sslContext.getSocketFactory();
- this.hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
- this.nameResolver = nameResolver;
- }
-
- /**
- * @since 4.1
- */
- public SSLSocketFactory(
- final SSLContext sslContext, final X509HostnameVerifier hostnameVerifier) {
- super();
- this.socketfactory = sslContext.getSocketFactory();
- this.hostnameVerifier = hostnameVerifier;
- this.nameResolver = null;
- }
-
- private SSLSocketFactory() {
- super();
- this.socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory();
- this.hostnameVerifier = null;
- this.nameResolver = null;
- }
-
- /**
- * @param params Optional parameters. Parameters passed to this method will have no effect.
- * This method will create a unconnected instance of {@link Socket} class
- * using {@link javax.net.ssl.SSLSocketFactory#createSocket()} method.
- * @since 4.1
- */
- @SuppressWarnings("cast")
- public Socket createSocket(final HttpParams params) throws IOException {
- // the cast makes sure that the factory is working as expected
- SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket();
- sslSocket.setEnabledProtocols(getProtocols(sslSocket));
- sslSocket.setEnabledCipherSuites(getCiphers(sslSocket));
- return sslSocket;
- }
-
- @SuppressWarnings("cast")
- public Socket createSocket() throws IOException {
- // the cast makes sure that the factory is working as expected
- SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket();
- sslSocket.setEnabledProtocols(getProtocols(sslSocket));
- sslSocket.setEnabledCipherSuites(getCiphers(sslSocket));
- return sslSocket;
- }
-
- /**
- * @since 4.1
- */
- public Socket connectSocket(
- final Socket sock,
- final InetSocketAddress remoteAddress,
- final InetSocketAddress localAddress,
- final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
- if (remoteAddress == null) {
- throw new IllegalArgumentException("Remote address may not be null");
- }
- if (params == null) {
- throw new IllegalArgumentException("HTTP parameters may not be null");
- }
- SSLSocket sslsock = (SSLSocket) (sock != null ? sock : createSocket());
- if (localAddress != null) {
-// sslsock.setReuseAddress(HttpConnectionParams.getSoReuseaddr(params));
- sslsock.bind(localAddress);
- }
-
- setHostName(sslsock, remoteAddress.getHostName());
- int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
- int soTimeout = HttpConnectionParams.getSoTimeout(params);
-
- try {
- sslsock.connect(remoteAddress, connTimeout);
- } catch (SocketTimeoutException ex) {
- throw new ConnectTimeoutException("Connect to " + remoteAddress.getHostName() + "/"
- + remoteAddress.getAddress() + " timed out");
- }
- sslsock.setSoTimeout(soTimeout);
- if (this.hostnameVerifier != null) {
- try {
- this.hostnameVerifier.verify(remoteAddress.getHostName(), sslsock);
- // verifyHostName() didn't blowup - good!
- } catch (IOException iox) {
- // close the socket before re-throwing the exception
- try { sslsock.close(); } catch (Exception x) { /*ignore*/ }
- throw iox;
- }
- }
- return sslsock;
- }
-
-
- /**
- * Checks whether a socket connection is secure.
- * This factory creates TLS/SSL socket connections
- * which, by default, are considered secure.
- *
- * Derived classes may override this method to perform
- * runtime checks, for example based on the cypher suite.
- *
- * @param sock the connected socket
- *
- * @return true
- *
- * @throws IllegalArgumentException if the argument is invalid
- */
- public boolean isSecure(final Socket sock) throws IllegalArgumentException {
- if (sock == null) {
- throw new IllegalArgumentException("Socket may not be null");
- }
- // This instanceof check is in line with createSocket() above.
- if (!(sock instanceof SSLSocket)) {
- throw new IllegalArgumentException("Socket not created by this factory");
- }
- // This check is performed last since it calls the argument object.
- if (sock.isClosed()) {
- throw new IllegalArgumentException("Socket is closed");
- }
- return true;
- }
-
- /**
- * @since 4.1
- */
- public Socket createLayeredSocket(
- final Socket socket,
- final String host,
- final int port,
- final boolean autoClose) throws IOException, UnknownHostException {
- SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(
- socket,
- host,
- port,
- autoClose
- );
- sslSocket.setEnabledProtocols(getProtocols(sslSocket));
- sslSocket.setEnabledCipherSuites(getCiphers(sslSocket));
- if (this.hostnameVerifier != null) {
- this.hostnameVerifier.verify(host, sslSocket);
- }
- // verifyHostName() didn't blowup - good!
- return sslSocket;
- }
-
- @Deprecated
- public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) {
- if ( hostnameVerifier == null ) {
- throw new IllegalArgumentException("Hostname verifier may not be null");
- }
- this.hostnameVerifier = hostnameVerifier;
- }
-
- public X509HostnameVerifier getHostnameVerifier() {
- return this.hostnameVerifier;
- }
-
- /**
- * @deprecated Use {@link #connectSocket(Socket, InetSocketAddress, InetSocketAddress, HttpParams)}
- */
- @Deprecated
- public Socket connectSocket(
- final Socket socket,
- final String host, int port,
- final InetAddress localAddress, int localPort,
- final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
- InetSocketAddress local = null;
- if (localAddress != null || localPort > 0) {
- // we need to bind explicitly
- if (localPort < 0) {
- localPort = 0; // indicates "any"
- }
- local = new InetSocketAddress(localAddress, localPort);
- }
- InetAddress remoteAddress;
- if (this.nameResolver != null) {
- remoteAddress = this.nameResolver.resolve(host);
- } else {
- remoteAddress = InetAddress.getByName(host);
- }
- InetSocketAddress remote = new InetSocketAddress(remoteAddress, port);
- return connectSocket(socket, remote, local, params);
- }
-
- /**
- * @deprecated Use {@link #createLayeredSocket(Socket, String, int, boolean)}
- */
- @Deprecated
- public Socket createSocket(
- final Socket socket,
- final String host, int port,
- boolean autoClose) throws IOException, UnknownHostException {
- SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(socket, host, port, autoClose);
- sslSocket.setEnabledProtocols(getProtocols(sslSocket));
- sslSocket.setEnabledCipherSuites(getCiphers(sslSocket));
- setHostName(sslSocket, host);
- return sslSocket;
- }
-
- private void setHostName(SSLSocket sslsock, String hostname){
- try {
- java.lang.reflect.Method setHostnameMethod = sslsock.getClass().getMethod("setHostname", String.class);
- setHostnameMethod.invoke(sslsock, hostname);
- } catch (Exception e) {
- Log.w(TAG, "SNI not useable", e);
- }
- }
-
- private String[] getProtocols(SSLSocket sslSocket) {
- String[] protocols = sslSocket.getEnabledProtocols();
-
- // Remove SSLv3 if it is not the only option
- if(protocols.length > 1) {
- List protocolList = new ArrayList(Arrays.asList(protocols));
- protocolList.remove("SSLv3");
- protocols = protocolList.toArray(new String[protocolList.size()]);
- }
-
- return protocols;
- }
-
- private String[] getCiphers(SSLSocket sslSocket) {
- String[] ciphers = sslSocket.getEnabledCipherSuites();
-
- List enabledCiphers = new ArrayList(Arrays.asList(ciphers));
- // On Android 5.0 release, Jetty doesn't seem to play nice with these ciphers
- // Issue seems to have been fixed in M, and now won't work without them. Because Google
- if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
- enabledCiphers.remove("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
- enabledCiphers.remove("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA");
- }
-
- ciphers = enabledCiphers.toArray(new String[enabledCiphers.size()]);
- return ciphers;
- }
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustManagerDecorator.java b/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustManagerDecorator.java
deleted file mode 100644
index f23643684..000000000
--- a/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustManagerDecorator.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * .
- *
- */
-package github.daneren2005.dsub.service.ssl;
-
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-
-import javax.net.ssl.X509TrustManager;
-
-
-/**
- * @since 4.1
- */
-class TrustManagerDecorator implements X509TrustManager {
-
- private final X509TrustManager trustManager;
- private final TrustStrategy trustStrategy;
-
- TrustManagerDecorator(final X509TrustManager trustManager, final TrustStrategy trustStrategy) {
- super();
- this.trustManager = trustManager;
- this.trustStrategy = trustStrategy;
- }
-
- public void checkClientTrusted(
- final X509Certificate[] chain, final String authType) throws CertificateException {
- this.trustManager.checkClientTrusted(chain, authType);
- }
-
- public void checkServerTrusted(
- final X509Certificate[] chain, final String authType) throws CertificateException {
- if (!this.trustStrategy.isTrusted(chain, authType)) {
- this.trustManager.checkServerTrusted(chain, authType);
- }
- }
-
- public X509Certificate[] getAcceptedIssuers() {
- return this.trustManager.getAcceptedIssuers();
- }
-
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustSelfSignedStrategy.java b/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustSelfSignedStrategy.java
deleted file mode 100644
index 637a8931f..000000000
--- a/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustSelfSignedStrategy.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * .
- *
- */
-package github.daneren2005.dsub.service.ssl;
-
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-
-/**
- * A trust strategy that accepts self-signed certificates as trusted. Verification of all other
- * certificates is done by the trust manager configured in the SSL context.
- *
- * @since 4.1
- */
-public class TrustSelfSignedStrategy implements TrustStrategy {
-
- public boolean isTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
- return true;
- }
-
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustStrategy.java b/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustStrategy.java
deleted file mode 100644
index 334a97c56..000000000
--- a/app/src/main/java/github/daneren2005/dsub/service/ssl/TrustStrategy.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * .
- *
- */
-package github.daneren2005.dsub.service.ssl;
-
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-
-/**
- * A strategy to establish trustworthiness of certificates without consulting the trust manager
- * configured in the actual SSL context. This interface can be used to override the standard
- * JSSE certificate verification process.
- *
- * @since 4.1
- */
-public interface TrustStrategy {
-
- /**
- * Determines whether the certificate chain can be trusted without consulting the trust manager
- * configured in the actual SSL context. This method can be used to override the standard JSSE
- * certificate verification process.
- *
- * Please note that, if this method returns false, the trust manager configured
- * in the actual SSL context can still clear the certificate as trusted.
- *
- * @param chain the peer certificate chain
- * @param authType the authentication type based on the client certificate
- * @return true if the certificate can be trusted without verification by
- * the trust manager, false otherwise.
- * @throws CertificateException thrown if the certificate is not trusted or invalid.
- */
- boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException;
-
-}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/Util.java b/app/src/main/java/github/daneren2005/dsub/util/Util.java
index b69ea55e4..7f8a168d5 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/Util.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Util.java
@@ -19,7 +19,6 @@
import android.annotation.TargetApi;
import android.app.Activity;
-import android.graphics.Color;
import android.support.annotation.StringRes;
import android.support.v7.app.AlertDialog;
import android.content.ClipboardManager;
@@ -29,7 +28,6 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -50,15 +48,11 @@
import android.util.SparseArray;
import android.view.View;
import android.view.Gravity;
-import android.view.Window;
-import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import github.daneren2005.dsub.R;
-import github.daneren2005.dsub.activity.SettingsActivity;
-import github.daneren2005.dsub.activity.SubsonicFragmentActivity;
import github.daneren2005.dsub.adapter.DetailsAdapter;
import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.PlayerState;
@@ -67,8 +61,6 @@
import github.daneren2005.dsub.receiver.MediaButtonIntentReceiver;
import github.daneren2005.dsub.service.DownloadService;
-import org.apache.http.HttpEntity;
-
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
@@ -80,7 +72,6 @@
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.DecimalFormat;
import java.text.NumberFormat;
@@ -603,13 +594,6 @@ public static String parseOfflineIDSearch(String id) {
}
}
- public static String getContentType(HttpEntity entity) {
- if (entity == null || entity.getContentType() == null) {
- return null;
- }
- return entity.getContentType().getValue();
- }
-
public static int getRemainingTrialDays(Context context) {
SharedPreferences prefs = getPreferences(context);
long installTime = prefs.getLong(Constants.PREFERENCES_KEY_INSTALL_TIME, 0L);
From 3af3fc9495491c6263fb5794e10aa72a47b727c5 Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Thu, 6 Oct 2016 17:48:53 -0700
Subject: [PATCH 05/28] Move to using Google Play Services SSL by default where
available
---
.../daneren2005/dsub/service/RESTMusicService.java | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
index 72faa7e17..178b8d135 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
@@ -40,6 +40,8 @@
import android.net.NetworkInfo;
import android.util.Log;
+import com.google.android.gms.security.ProviderInstaller;
+
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.*;
import github.daneren2005.dsub.fragments.MainFragment;
@@ -105,6 +107,7 @@ public class RESTMusicService implements MusicService {
private String redirectFrom;
private String redirectTo;
private Integer instance;
+ private boolean hasInstalledGoogleSSL = false;
public RESTMusicService() {
selfSignedHostnameVerifier = new HostnameVerifier() {
@@ -1831,6 +1834,16 @@ private HttpURLConnection getConnectionDirect(Context context, String url, List<
}
private HttpURLConnection getConnectionDirect(Context context, String url, int minNetworkTimeout) throws Exception {
+ if(!hasInstalledGoogleSSL) {
+ try {
+ ProviderInstaller.installIfNeeded(context);
+ } catch(Exception e) {
+ // Just continue on anyways, doesn't really harm anything if this fails
+ Log.w(TAG, "Failed to update to use Google Play SSL", e);
+ }
+ hasInstalledGoogleSSL = true;
+ }
+
// Connect and add headers
URL urlObj = new URL(url);
HttpURLConnection connection = (HttpURLConnection) urlObj.openConnection();
From 1926158ac2cae28b0b1c40bc989537e539bddddd Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Fri, 7 Oct 2016 17:52:01 -0700
Subject: [PATCH 06/28] #564 Reimplement getInputStream range header,
increasing timeout, and fix retry logic
---
.../dsub/service/RESTMusicService.java | 72 +++++++++++--------
1 file changed, 42 insertions(+), 30 deletions(-)
diff --git a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
index 178b8d135..b05a569da 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
@@ -30,6 +30,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -80,6 +81,7 @@
import github.daneren2005.dsub.util.SongDBHandler;
import github.daneren2005.dsub.util.Util;
import java.io.*;
+import java.util.Map;
import java.util.zip.GZIPInputStream;
import javax.net.ssl.HostnameVerifier;
@@ -90,7 +92,6 @@ public class RESTMusicService implements MusicService {
private static final String TAG = RESTMusicService.class.getSimpleName();
- private static final int SOCKET_CONNECT_TIMEOUT = 10 * 1000;
private static final int SOCKET_READ_TIMEOUT_DEFAULT = 10 * 1000;
private static final int SOCKET_READ_TIMEOUT_DOWNLOAD = 30 * 1000;
private static final int SOCKET_READ_TIMEOUT_GET_PLAYLIST = 60 * 1000;
@@ -804,19 +805,17 @@ public HttpURLConnection getDownloadInputStream(Context context, MusicDirectory.
}
}
- // TODO: Use task
- // TODO: Add range header
- // Add "Range" header if offset is given.
- /*List headers = new ArrayList();
+ // Add "Range" header if offset is given
+ Map headers = new HashMap<>();
if (offset > 0) {
- headers.add(new BasicHeader("Range", "bytes=" + offset + "-"));
- }*/
+ headers.put("Range", "bytes=" + offset + "-");
+ }
// Set socket read timeout. Note: The timeout increases as the offset gets larger. This is
// to avoid the thrashing effect seen when offset is combined with transcoding/downsampling on the server.
// In that case, the server uses a long time before sending any data, causing the client to time out.
int timeout = (int) (SOCKET_READ_TIMEOUT_DOWNLOAD + offset * TIMEOUT_MILLIS_PER_OFFSET_BYTE);
- HttpURLConnection connection = getConnection(context, url, parameterNames, parameterValues, timeout);
+ HttpURLConnection connection = getConnection(context, url, parameterNames, parameterValues, headers, timeout);
// If content type is XML, an error occurred. Get it.
String contentType = connection.getContentType();
@@ -1769,29 +1768,34 @@ private InputStream getInputStreamFromConnection(HttpURLConnection connection) t
return in;
}
- private HttpURLConnection getConnection(Context context, String url, List parameterNames, List parameterValues) throws Exception {
- return getConnection(context, url, parameterNames, parameterValues, null, true);
- }
- private HttpURLConnection getConnection(Context context, String url, List parameterNames, List parameterValues, int minNetworkTimeout) throws Exception {
- return getConnection(context, url, parameterNames, parameterValues, minNetworkTimeout, null, true);
+ private HttpURLConnection getConnection(Context context, String url, List parameterNames, List parameterValues, Map headers, int minNetworkTimeout) throws Exception {
+ return getConnection(context, url, parameterNames, parameterValues, headers, minNetworkTimeout, null, true);
}
private HttpURLConnection getConnection(Context context, String url, List parameterNames, List parameterValues, ProgressListener progressListener, boolean throwErrors) throws Exception {
return getConnection(context, url, parameterNames, parameterValues, 0, progressListener, throwErrors);
}
private HttpURLConnection getConnection(Context context, String url, List parameterNames, List parameterValues, int minNetworkTimeout, ProgressListener progressListener, boolean throwErrors) throws Exception {
+ return getConnection(context, url, parameterNames, parameterValues, null, minNetworkTimeout, progressListener, throwErrors);
+ }
+ private HttpURLConnection getConnection(Context context, String url, List parameterNames, List parameterValues, Map headers, int minNetworkTimeout, ProgressListener progressListener, boolean throwErrors) throws Exception {
if(throwErrors) {
- return getConnectionDirect(context, url, parameterNames, parameterValues, minNetworkTimeout);
+ SharedPreferences prefs = Util.getPreferences(context);
+ int networkTimeout = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_NETWORK_TIMEOUT, SOCKET_READ_TIMEOUT_DEFAULT + ""));
+ return getConnectionDirect(context, url, parameterNames, parameterValues, headers, Math.max(minNetworkTimeout, networkTimeout));
} else {
- return getConnection(context, url, parameterNames, parameterValues, minNetworkTimeout, progressListener, HTTP_REQUEST_MAX_ATTEMPTS, 0);
+ return getConnection(context, url, parameterNames, parameterValues, headers, minNetworkTimeout, progressListener, HTTP_REQUEST_MAX_ATTEMPTS, 0);
}
}
- private HttpURLConnection getConnection(Context context, String url, List parameterNames, List parameterValues, ProgressListener progressListener, int retriesLeft, int attempts) throws Exception {
- return getConnection(context, url, parameterNames, parameterValues, 0, progressListener, retriesLeft, attempts);
- }
- private HttpURLConnection getConnection(Context context, String url, List parameterNames, List parameterValues, int minNetworkTimeout, ProgressListener progressListener, int retriesLeft, int attempts) throws Exception {
+ private HttpURLConnection getConnection(Context context, String url, List parameterNames, List parameterValues, Map headers, int minNetworkTimeout, ProgressListener progressListener, int retriesLeft, int attempts) throws Exception {
+ SharedPreferences prefs = Util.getPreferences(context);
+ int networkTimeout = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_NETWORK_TIMEOUT, SOCKET_READ_TIMEOUT_DEFAULT + ""));
+ minNetworkTimeout = Math.max(minNetworkTimeout, networkTimeout);
+ attempts++;
+ retriesLeft--;
+
try {
- return getConnectionDirect(context, url, parameterNames, parameterValues, minNetworkTimeout);
+ return getConnectionDirect(context, url, parameterNames, parameterValues, headers, minNetworkTimeout);
} catch (IOException x) {
if(retriesLeft > 0) {
if (progressListener != null) {
@@ -1802,16 +1806,15 @@ private HttpURLConnection getConnection(Context context, String url, List parameterNames, List parameterValues, int minNetworkTimeout) throws Exception {
+ private HttpURLConnection getConnectionDirect(Context context, String url, List parameterNames, List parameterValues, Map headers, int minNetworkTimeout) throws Exception {
// Add params to query
if (parameterNames != null) {
StringBuilder builder = new StringBuilder(url);
@@ -1830,10 +1833,10 @@ private HttpURLConnection getConnectionDirect(Context context, String url, List<
Log.i(TAG, stripUrlInfo(rewrittenUrl));
}
- return getConnectionDirect(context, rewrittenUrl, minNetworkTimeout);
+ return getConnectionDirect(context, rewrittenUrl, headers, minNetworkTimeout);
}
- private HttpURLConnection getConnectionDirect(Context context, String url, int minNetworkTimeout) throws Exception {
+ private HttpURLConnection getConnectionDirect(Context context, String url, Map headers, int minNetworkTimeout) throws Exception {
if(!hasInstalledGoogleSSL) {
try {
ProviderInstaller.installIfNeeded(context);
@@ -1853,16 +1856,25 @@ private HttpURLConnection getConnectionDirect(Context context, String url, int m
connection.addRequestProperty("User-Agent", Constants.REST_CLIENT_ID);
// Set timeout
- SharedPreferences prefs = Util.getPreferences(context);
- int networkTimeout = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_NETWORK_TIMEOUT, SOCKET_CONNECT_TIMEOUT + ""));
- connection.setConnectTimeout(Math.max(minNetworkTimeout, networkTimeout));
- connection.setReadTimeout(SOCKET_READ_TIMEOUT_DEFAULT);
+ connection.setConnectTimeout(minNetworkTimeout);
+ connection.setReadTimeout(minNetworkTimeout);
+
+ // Add headers
+ if(headers != null) {
+ for(Map.Entry header: headers.entrySet()) {
+ connection.setRequestProperty(header.getKey(), header.getValue());
+ }
+ }
if(connection instanceof HttpsURLConnection) {
HttpsURLConnection sslConnection = (HttpsURLConnection) connection;
sslConnection.setHostnameVerifier(selfSignedHostnameVerifier);
}
+ // Force the connection to initiate
+ if(connection.getResponseCode() >= 500) {
+ throw new IOException("Error code: " + connection.getResponseCode());
+ }
detectRedirect(context, urlObj, connection);
return connection;
}
From e98dea91a05b1deca4428e56a869fa4cc9d2dcf8 Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Sat, 8 Oct 2016 09:53:34 -0700
Subject: [PATCH 07/28] Remove legacy reference to DragSortListView
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index e7d83ca4f..7583175b1 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ git submodule init
git submodule update
```
-Go to DragSortListView/library and ServerProxy and build project files
+Go to ServerProxy and build project files
```
android update project --path ./
```
From e32c49329b7c665e96604ac047ce772e1155e468 Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Mon, 10 Oct 2016 17:37:24 -0700
Subject: [PATCH 08/28] Remove extra xlms definitions on layouts
---
app/src/main/res/layout-land/download.xml | 2 +-
app/src/main/res/layout/abstract_fragment_activity.xml | 2 +-
app/src/main/res/layout/change_email.xml | 2 +-
app/src/main/res/layout/confirm_password.xml | 2 +-
app/src/main/res/layout/create_bookmark.xml | 2 +-
app/src/main/res/layout/create_podcast.xml | 2 +-
app/src/main/res/layout/create_user.xml | 6 +++---
app/src/main/res/layout/notification.xml | 1 -
app/src/main/res/layout/notification_expanded.xml | 1 -
app/src/main/res/layout/select_album_header.xml | 2 +-
app/src/main/res/layout/shuffle_dialog.xml | 6 +++---
app/src/main/res/layout/update_playlist.xml | 6 +++---
app/src/main/res/layout/update_share.xml | 6 +++---
13 files changed, 19 insertions(+), 21 deletions(-)
diff --git a/app/src/main/res/layout-land/download.xml b/app/src/main/res/layout-land/download.xml
index 894ae62e8..855bf2a90 100644
--- a/app/src/main/res/layout-land/download.xml
+++ b/app/src/main/res/layout-land/download.xml
@@ -37,7 +37,7 @@
android:layout_centerHorizontal="true"
android:layout_above="@+id/download_song_title">
-
-
-
diff --git a/app/src/main/res/layout/confirm_password.xml b/app/src/main/res/layout/confirm_password.xml
index 9ec61c0ab..d74eecfde 100644
--- a/app/src/main/res/layout/confirm_password.xml
+++ b/app/src/main/res/layout/confirm_password.xml
@@ -4,7 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
diff --git a/app/src/main/res/layout/create_bookmark.xml b/app/src/main/res/layout/create_bookmark.xml
index d6f077c3d..22d962277 100644
--- a/app/src/main/res/layout/create_bookmark.xml
+++ b/app/src/main/res/layout/create_bookmark.xml
@@ -3,7 +3,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
-
diff --git a/app/src/main/res/layout/create_podcast.xml b/app/src/main/res/layout/create_podcast.xml
index 04e74ec38..a5e667926 100644
--- a/app/src/main/res/layout/create_podcast.xml
+++ b/app/src/main/res/layout/create_podcast.xml
@@ -3,7 +3,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
-
diff --git a/app/src/main/res/layout/create_user.xml b/app/src/main/res/layout/create_user.xml
index b2d8f6e00..7d77ade9b 100644
--- a/app/src/main/res/layout/create_user.xml
+++ b/app/src/main/res/layout/create_user.xml
@@ -4,7 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
@@ -26,7 +26,7 @@
android:textColor="?android:textColorPrimary"/>
-
@@ -48,7 +48,7 @@
android:textColor="?android:textColorPrimary"/>
-
diff --git a/app/src/main/res/layout/notification.xml b/app/src/main/res/layout/notification.xml
index 4a89db49c..0ab5a8847 100644
--- a/app/src/main/res/layout/notification.xml
+++ b/app/src/main/res/layout/notification.xml
@@ -13,7 +13,6 @@
android:gravity="center" />
-
-
@@ -26,7 +26,7 @@
android:hint="@string/shuffle.startYear" />
-
@@ -49,7 +49,7 @@
android:hint="@string/shuffle.endYear" />
-
diff --git a/app/src/main/res/layout/update_playlist.xml b/app/src/main/res/layout/update_playlist.xml
index cc7e5ee63..f9cc6a909 100644
--- a/app/src/main/res/layout/update_playlist.xml
+++ b/app/src/main/res/layout/update_playlist.xml
@@ -3,7 +3,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
-
@@ -26,7 +26,7 @@
android:textColor="?android:textColorPrimary"/>
-
@@ -49,7 +49,7 @@
android:hint="@string/common.comment" />
-
diff --git a/app/src/main/res/layout/update_share.xml b/app/src/main/res/layout/update_share.xml
index ef44e304e..0d06e00d0 100644
--- a/app/src/main/res/layout/update_share.xml
+++ b/app/src/main/res/layout/update_share.xml
@@ -3,7 +3,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
-
@@ -26,7 +26,7 @@
android:hint="@string/common.name" />
-
@@ -48,7 +48,7 @@
android:calendarViewShown="false"/>
-
From a09b7f8b2bac877655e10f5f7f8f37779d119595 Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Mon, 10 Oct 2016 17:47:05 -0700
Subject: [PATCH 09/28] Fix a couple of minor warnings
---
.../github/daneren2005/dsub/fragments/SubsonicFragment.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
index 818324ed4..e138f4dd4 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
@@ -660,7 +660,7 @@ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCoun
}
});
- refreshLayout.setColorScheme(
+ refreshLayout.setColorSchemeResources(
R.color.holo_blue_light,
R.color.holo_orange_light,
R.color.holo_green_light,
@@ -683,7 +683,7 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
}
});
- refreshLayout.setColorScheme(
+ refreshLayout.setColorSchemeResources(
R.color.holo_blue_light,
R.color.holo_orange_light,
R.color.holo_green_light,
@@ -1113,7 +1113,7 @@ protected Void doInBackground() throws Throwable {
@Override
protected void done(Void result) {
- Util.toast(context, context.getResources().getString(R.string.updated_playlist, songs.size(), playlist.getName()));
+ Util.toast(context, context.getResources().getString(R.string.updated_playlist, String.valueOf(songs.size()), playlist.getName()));
}
@Override
From da04e2c1d00217fc55943574bc318a2b52ad5823 Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Tue, 11 Oct 2016 17:01:50 -0700
Subject: [PATCH 10/28] Update gradle
---
ServerProxy | 2 +-
build.gradle | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/ServerProxy b/ServerProxy
index 8e9308c21..ed3ff58dc 160000
--- a/ServerProxy
+++ b/ServerProxy
@@ -1 +1 @@
-Subproject commit 8e9308c21d34d2d8c2b315c7d93f036b824706eb
+Subproject commit ed3ff58dc83f40ad3437fc30e63d7b437939c2bc
diff --git a/build.gradle b/build.gradle
index af9a87abe..3dfb085aa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.2.0'
+ classpath 'com.android.tools.build:gradle:2.2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
From 1796bf6a4c007b39e22696b6ae3c38aac62454e8 Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Tue, 11 Oct 2016 17:02:33 -0700
Subject: [PATCH 11/28] Fixes #747: Add support for Internet Radio while
casting
---
.../SelectInternetRadioStationFragment.java | 2 +-
.../dsub/service/ChromeCastController.java | 51 +--------------
.../dsub/service/DLNAController.java | 54 +---------------
.../dsub/service/JukeboxController.java | 2 +-
.../dsub/service/RemoteController.java | 64 +++++++++++++++++++
5 files changed, 71 insertions(+), 102 deletions(-)
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectInternetRadioStationFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectInternetRadioStationFragment.java
index c39e9f610..74c4b269b 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectInternetRadioStationFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectInternetRadioStationFragment.java
@@ -133,7 +133,7 @@ private void getStreamFromPlaylist(InternetRadioStation internetRadioStation) {
connection.disconnect();
}
} catch (Exception e) {
- Log.e(TAG, "Failed to get stream data from playlist");
+ Log.e(TAG, "Failed to get stream data from playlist", e);
}
}
diff --git a/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java b/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java
index 2df290cf3..566ed773a 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java
@@ -66,17 +66,12 @@ public class ChromeCastController extends RemoteController {
private boolean isStopping = false;
private Runnable afterUpdateComplete = null;
- private ServerProxy proxy;
- private String rootLocation;
private RemoteMediaPlayer mediaPlayer;
private double gain = 0.5;
public ChromeCastController(DownloadService downloadService, CastDevice castDevice) {
- this.downloadService = downloadService;
+ super(downloadService);
this.castDevice = castDevice;
-
- SharedPreferences prefs = Util.getPreferences(downloadService);
- rootLocation = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
}
@Override
@@ -287,49 +282,7 @@ public void run() {
try {
MusicService musicService = MusicServiceFactory.getMusicService(downloadService);
- String url;
- // Offline, use file proxy
- if(Util.isOffline(downloadService) || song.getId().indexOf(rootLocation) != -1) {
- if(proxy == null) {
- proxy = new FileProxy(downloadService);
- proxy.start();
- }
-
- // Offline song
- if(song.getId().indexOf(rootLocation) != -1) {
- url = proxy.getPublicAddress(song.getId());
- } else {
- // Playing online song in offline mode
- url = proxy.getPublicAddress(currentPlaying.getCompleteFile().getPath());
- }
- } else {
- // Check if we want a proxy going still
- if(Util.isCastProxy(downloadService)) {
- if(proxy instanceof FileProxy) {
- proxy.stop();
- proxy = null;
- }
-
- if(proxy == null) {
- proxy = createWebProxy();
- proxy.start();
- }
- } else if(proxy != null) {
- proxy.stop();
- proxy = null;
- }
-
- if(song.isVideo()) {
- url = musicService.getHlsUrl(song.getId(), currentPlaying.getBitRate(), downloadService);
- } else {
- url = musicService.getMusicUrl(downloadService, song, currentPlaying.getBitRate());
- }
-
- // If proxy is going, it is a WebProxy
- if(proxy != null) {
- url = proxy.getPublicAddress(url);
- }
- }
+ String url = getStreamUrl(musicService, currentPlaying);
// Setup song/video information
MediaMetadata meta = new MediaMetadata(song.isVideo() ? MediaMetadata.MEDIA_TYPE_MOVIE : MediaMetadata.MEDIA_TYPE_MUSIC_TRACK);
diff --git a/app/src/main/java/github/daneren2005/dsub/service/DLNAController.java b/app/src/main/java/github/daneren2005/dsub/service/DLNAController.java
index 248900578..143be330f 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/DLNAController.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/DLNAController.java
@@ -83,9 +83,6 @@ public class DLNAController extends RemoteController {
SubscriptionCallback callback;
boolean supportsSeek = false;
boolean supportsSetupNext = false;
-
- private ServerProxy proxy;
- String rootLocation = "";
boolean error = false;
final AtomicLong lastUpdate = new AtomicLong();
@@ -108,12 +105,9 @@ public void run() {
};
public DLNAController(DownloadService downloadService, ControlPoint controlPoint, DLNADevice device) {
- this.downloadService = downloadService;
+ super(downloadService);
this.controlPoint = controlPoint;
this.device = device;
-
- SharedPreferences prefs = Util.getPreferences(downloadService);
- rootLocation = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
nextSupported = true;
}
@@ -473,49 +467,7 @@ Pair getSongInfo(final DownloadFile downloadFile) throws Excepti
// Get url for entry
MusicService musicService = MusicServiceFactory.getMusicService(downloadService);
- String url;
- // In offline mode or playing offline song
- if(Util.isOffline(downloadService) || song.getId().indexOf(rootLocation) != -1) {
- if(proxy == null) {
- proxy = new FileProxy(downloadService);
- proxy.start();
- }
-
- // Offline song
- if(song.getId().indexOf(rootLocation) != -1) {
- url = proxy.getPublicAddress(song.getId());
- } else {
- // Playing online song in offline mode
- url = proxy.getPublicAddress(downloadFile.getCompleteFile().getPath());
- }
- } else {
- // Check if we want a proxy going still
- if(Util.isCastProxy(downloadService)) {
- if(proxy instanceof FileProxy) {
- proxy.stop();
- proxy = null;
- }
-
- if(proxy == null) {
- proxy = createWebProxy();
- proxy.start();
- }
- } else if(proxy != null) {
- proxy.stop();
- proxy = null;
- }
-
- if(song.isVideo()) {
- url = musicService.getHlsUrl(song.getId(), downloadFile.getBitRate(), downloadService);
- } else {
- url = musicService.getMusicUrl(downloadService, song, downloadFile.getBitRate());
- }
-
- // If proxy is going, it is a WebProxy
- if(proxy != null) {
- url = proxy.getPublicAddress(url);
- }
- }
+ String url = getStreamUrl(musicService, downloadFile);
// Create metadata for entry
Item track;
@@ -585,7 +537,7 @@ Pair getSongInfo(final DownloadFile downloadFile) throws Excepti
Log.w(TAG, "Metadata generation failed", e);
}
- return new Pair(url, metadata);
+ return new Pair<>(url, metadata);
}
private void failedLoad() {
diff --git a/app/src/main/java/github/daneren2005/dsub/service/JukeboxController.java b/app/src/main/java/github/daneren2005/dsub/service/JukeboxController.java
index 82ef45e1a..b9f40f320 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/JukeboxController.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/JukeboxController.java
@@ -48,7 +48,7 @@ public class JukeboxController extends RemoteController {
private float gain = 0.5f;
public JukeboxController(DownloadService downloadService, Handler handler) {
- this.downloadService = downloadService;
+ super(downloadService);
this.handler = handler;
}
diff --git a/app/src/main/java/github/daneren2005/dsub/service/RemoteController.java b/app/src/main/java/github/daneren2005/dsub/service/RemoteController.java
index 2815bcc9f..c6702e3ec 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/RemoteController.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/RemoteController.java
@@ -19,20 +19,32 @@
package github.daneren2005.dsub.service;
+import android.content.SharedPreferences;
import android.util.Log;
import java.util.Iterator;
import java.util.concurrent.LinkedBlockingQueue;
+import github.daneren2005.dsub.domain.MusicDirectory;
import github.daneren2005.dsub.domain.RemoteStatus;
import github.daneren2005.dsub.util.Constants;
import github.daneren2005.dsub.util.Util;
+import github.daneren2005.serverproxy.FileProxy;
+import github.daneren2005.serverproxy.ServerProxy;
import github.daneren2005.serverproxy.WebProxy;
public abstract class RemoteController {
private static final String TAG = RemoteController.class.getSimpleName();
protected DownloadService downloadService;
protected boolean nextSupported = false;
+ protected ServerProxy proxy;
+ protected String rootLocation = "";
+
+ public RemoteController(DownloadService downloadService) {
+ this.downloadService = downloadService;
+ SharedPreferences prefs = Util.getPreferences(downloadService);
+ rootLocation = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);
+ }
public abstract void create(boolean playing, int seconds);
public abstract void start();
@@ -110,4 +122,56 @@ protected WebProxy createWebProxy() {
return new WebProxy(downloadService);
}
}
+
+ protected String getStreamUrl(MusicService musicService, DownloadFile downloadFile) throws Exception {
+ MusicDirectory.Entry song = downloadFile.getSong();
+
+ String url;
+ // In offline mode or playing offline song
+ if(downloadFile.isStream()) {
+ url = downloadFile.getStream();
+ } else if(Util.isOffline(downloadService) || song.getId().indexOf(rootLocation) != -1) {
+ if(proxy == null) {
+ proxy = new FileProxy(downloadService);
+ proxy.start();
+ }
+
+ // Offline song
+ if(song.getId().indexOf(rootLocation) != -1) {
+ url = proxy.getPublicAddress(song.getId());
+ } else {
+ // Playing online song in offline mode
+ url = proxy.getPublicAddress(downloadFile.getCompleteFile().getPath());
+ }
+ } else {
+ // Check if we want a proxy going still
+ if(Util.isCastProxy(downloadService)) {
+ if(proxy instanceof FileProxy) {
+ proxy.stop();
+ proxy = null;
+ }
+
+ if(proxy == null) {
+ proxy = createWebProxy();
+ proxy.start();
+ }
+ } else if(proxy != null) {
+ proxy.stop();
+ proxy = null;
+ }
+
+ if(song.isVideo()) {
+ url = musicService.getHlsUrl(song.getId(), downloadFile.getBitRate(), downloadService);
+ } else {
+ url = musicService.getMusicUrl(downloadService, song, downloadFile.getBitRate());
+ }
+
+ // If proxy is going, it is a WebProxy
+ if(proxy != null) {
+ url = proxy.getPublicAddress(url);
+ }
+ }
+
+ return url;
+ }
}
From c69779ca1c1ba11da80765f6c5545e5a30f6fd4f Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Tue, 11 Oct 2016 19:46:01 -0700
Subject: [PATCH 12/28] Fixes #746: Add support for Play Title by Artist voice
commands
---
.../activity/SubsonicFragmentActivity.java | 6 +-
.../activity/VoiceQueryReceiverActivity.java | 16 ++++
.../dsub/fragments/SearchFragment.java | 85 +++++++++++++++----
3 files changed, 90 insertions(+), 17 deletions(-)
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
index c71900463..e37c6d49f 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
@@ -30,6 +30,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
+import android.provider.MediaStore;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AlertDialog;
@@ -402,9 +403,12 @@ public void onNewIntent(Intent intent) {
if(currentFragment instanceof SearchFragment) {
String query = intent.getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY);
boolean autoplay = intent.getBooleanExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false);
+ String artist = intent.getStringExtra(MediaStore.EXTRA_MEDIA_ARTIST);
+ String album = intent.getStringExtra(MediaStore.EXTRA_MEDIA_ALBUM);
+ String title = intent.getStringExtra(MediaStore.EXTRA_MEDIA_TITLE);
if (query != null) {
- ((SearchFragment)currentFragment).search(query, autoplay);
+ ((SearchFragment)currentFragment).search(query, autoplay, artist, album, title);
}
getIntent().removeExtra(Constants.INTENT_EXTRA_NAME_QUERY);
} else {
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/VoiceQueryReceiverActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/VoiceQueryReceiverActivity.java
index c0effe270..641b118f4 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/VoiceQueryReceiverActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/VoiceQueryReceiverActivity.java
@@ -55,6 +55,22 @@ public void onCreate(Bundle savedInstanceState) {
if(!GMS_SEARCH_ACTION.equals(getIntent().getAction())) {
intent.putExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
}
+
+ String artist = getIntent().getStringExtra(MediaStore.EXTRA_MEDIA_ARTIST);
+ if(artist != null) {
+ intent.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, artist);
+ }
+
+ String album = getIntent().getStringExtra(MediaStore.EXTRA_MEDIA_ALBUM);
+ if(album != null) {
+ intent.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, album);
+ }
+
+ String title = getIntent().getStringExtra(MediaStore.EXTRA_MEDIA_TITLE);
+ if(title != null) {
+ intent.putExtra(MediaStore.EXTRA_MEDIA_TITLE, title);
+ }
+
intent.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, getIntent().getStringExtra(MediaStore.EXTRA_MEDIA_FOCUS));
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Util.startActivityWithoutTransition(VoiceQueryReceiverActivity.this, intent);
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
index eed714afa..dfff45cd2 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SearchFragment.java
@@ -3,7 +3,10 @@
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
import android.content.Intent;
import android.os.Bundle;
@@ -26,6 +29,7 @@
import github.daneren2005.dsub.adapter.SectionAdapter;
import github.daneren2005.dsub.domain.Artist;
import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.domain.MusicDirectory.Entry;
import github.daneren2005.dsub.domain.SearchCritera;
import github.daneren2005.dsub.domain.SearchResult;
import github.daneren2005.dsub.service.MusicService;
@@ -132,7 +136,7 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
@Override
public void onCreateContextMenu(Menu menu, MenuInflater menuInflater, UpdateView updateView, Serializable item) {
onCreateContextMenuSupport(menu, menuInflater, updateView, item);
- if(item instanceof MusicDirectory.Entry && !((MusicDirectory.Entry) item).isVideo() && !Util.isOffline(context)) {
+ if(item instanceof Entry && !((Entry) item).isVideo() && !Util.isOffline(context)) {
menu.removeItem(R.id.song_menu_remove_playlist);
}
recreateContextMenu(menu);
@@ -152,8 +156,8 @@ public void refresh(boolean refresh) {
public void onItemClicked(UpdateView updateView, Serializable item) {
if (item instanceof Artist) {
onArtistSelected((Artist) item, false);
- } else if (item instanceof MusicDirectory.Entry) {
- MusicDirectory.Entry entry = (MusicDirectory.Entry) item;
+ } else if (item instanceof Entry) {
+ Entry entry = (Entry) item;
if (entry.isDirectory()) {
onAlbumSelected(entry, false);
} else if (entry.isVideo()) {
@@ -165,12 +169,12 @@ public void onItemClicked(UpdateView updateView, Serializable item
}
@Override
- protected List getSelectedEntries() {
+ protected List getSelectedEntries() {
List selected = adapter.getSelected();
- List selectedMedia = new ArrayList<>();
+ List selectedMedia = new ArrayList<>();
for(Serializable ser: selected) {
- if(ser instanceof MusicDirectory.Entry) {
- selectedMedia.add((MusicDirectory.Entry) ser);
+ if(ser instanceof Entry) {
+ selectedMedia.add((Entry) ser);
}
}
@@ -182,7 +186,7 @@ protected boolean isShowArtistEnabled() {
return true;
}
- public void search(final String query, final boolean autoplay) {
+ public void search(final String query, final boolean autoplay, final String artist, final String album, final String title) {
if(skipSearch) {
skipSearch = false;
return;
@@ -202,7 +206,7 @@ protected void done(SearchResult result) {
searchResult = result;
recyclerView.setAdapter(adapter = new SearchAdapter(context, searchResult, getImageLoader(), largeAlbums, SearchFragment.this));
if (autoplay) {
- autoplay(query);
+ autoplay(query, artist, album, title);
}
}
@@ -232,7 +236,7 @@ private void onArtistSelected(Artist artist, boolean autoplay) {
replaceFragment(fragment);
}
- private void onAlbumSelected(MusicDirectory.Entry album, boolean autoplay) {
+ private void onAlbumSelected(Entry album, boolean autoplay) {
SubsonicFragment fragment = new SelectDirectoryFragment();
Bundle args = new Bundle();
args.putString(Constants.INTENT_EXTRA_NAME_ID, album.getId());
@@ -245,7 +249,7 @@ private void onAlbumSelected(MusicDirectory.Entry album, boolean autoplay) {
replaceFragment(fragment);
}
- private void onSongSelected(MusicDirectory.Entry song, boolean save, boolean append, boolean autoplay, boolean playNext) {
+ private void onSongSelected(Entry song, boolean save, boolean append, boolean autoplay, boolean playNext) {
DownloadService downloadService = getDownloadService();
if (downloadService != null) {
if (!append) {
@@ -260,7 +264,7 @@ private void onSongSelected(MusicDirectory.Entry song, boolean save, boolean app
}
}
- private void onVideoSelected(MusicDirectory.Entry entry) {
+ private void onVideoSelected(Entry entry) {
int maxBitrate = Util.getMaxVideoBitrate(context);
Intent intent = new Intent(Intent.ACTION_VIEW);
@@ -268,6 +272,55 @@ private void onVideoSelected(MusicDirectory.Entry entry) {
startActivity(intent);
}
+ private void autoplay(String query, String artistQuery, String albumQuery, String titleQuery) {
+ Log.i(TAG, "Query: '" + query + "' ( Artist: '" + artistQuery + "', Album: '" + albumQuery + "', Title: '" + titleQuery + "')");
+
+ if(titleQuery != null && !searchResult.getSongs().isEmpty()) {
+ titleQuery = titleQuery.toLowerCase();
+
+ TreeMap tree = new TreeMap<>();
+ for(Entry song: searchResult.getSongs()) {
+ tree.put(Util.getStringDistance(song.getTitle().toLowerCase(), titleQuery), song);
+ }
+
+ Map.Entry entry = tree.firstEntry();
+ if(entry.getKey() <= MIN_CLOSENESS) {
+ onSongSelected(entry.getValue(), false, false, true, false);
+ } else {
+ autoplay(query);
+ }
+ } else if(albumQuery != null && !searchResult.getAlbums().isEmpty()) {
+ albumQuery = albumQuery.toLowerCase();
+
+ TreeMap tree = new TreeMap<>();
+ for(Entry album: searchResult.getAlbums()) {
+ tree.put(Util.getStringDistance(album.getTitle().toLowerCase(), albumQuery), album);
+ }
+
+ Map.Entry entry = tree.firstEntry();
+ if(entry.getKey() <= MIN_CLOSENESS) {
+ onAlbumSelected(entry.getValue(), true);
+ } else {
+ autoplay(query);
+ }
+ } else if(artistQuery != null && !searchResult.getArtists().isEmpty()) {
+ artistQuery = artistQuery.toLowerCase();
+
+ TreeMap tree = new TreeMap<>();
+ for(Artist artist: searchResult.getArtists()) {
+ tree.put(Util.getStringDistance(artist.getName().toLowerCase(), artistQuery), artist);
+ }
+ Map.Entry entry = tree.firstEntry();
+ if(entry.getKey() <= MIN_CLOSENESS) {
+ onArtistSelected(entry.getValue(), true);
+ } else {
+ autoplay(query);
+ }
+ } else {
+ autoplay(query);
+ }
+ }
+
private void autoplay(String query) {
query = query.toLowerCase();
@@ -276,12 +329,12 @@ private void autoplay(String query) {
artist = searchResult.getArtists().get(0);
artist.setCloseness(Util.getStringDistance(artist.getName().toLowerCase(), query));
}
- MusicDirectory.Entry album = null;
+ Entry album = null;
if(!searchResult.getAlbums().isEmpty()) {
album = searchResult.getAlbums().get(0);
album.setCloseness(Util.getStringDistance(album.getTitle().toLowerCase(), query));
}
- MusicDirectory.Entry song = null;
+ Entry song = null;
if(!searchResult.getSongs().isEmpty()) {
song = searchResult.getSongs().get(0);
song.setCloseness(Util.getStringDistance(song.getTitle().toLowerCase(), query));
@@ -289,10 +342,10 @@ private void autoplay(String query) {
if(artist != null && (artist.getCloseness() <= MIN_CLOSENESS ||
(album == null || artist.getCloseness() <= album.getCloseness()) &&
- (song == null || artist.getCloseness() <= song.getCloseness()))) {
+ (song == null || artist.getCloseness() <= song.getCloseness()))) {
onArtistSelected(artist, true);
} else if(album != null && (album.getCloseness() <= MIN_CLOSENESS ||
- song == null || album.getCloseness() <= song.getCloseness())) {
+ song == null || album.getCloseness() <= song.getCloseness())) {
onAlbumSelected(album, true);
} else if(song != null) {
onSongSelected(song, false, false, true, false);
From 406b996e0cbb2a708606ddee4a0a12d90136dcf0 Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Thu, 13 Oct 2016 16:05:46 -0700
Subject: [PATCH 13/28] DSub released to Beta to test out connection logic
---
app/build.gradle | 4 ++--
app/src/main/res/xml/changelog.xml | 6 ++++++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 50bbb69b1..92dfa7313 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,8 +8,8 @@ android {
applicationId "github.daneren2005.dsub"
minSdkVersion 14
targetSdkVersion 23
- versionCode 187
- versionName '5.3.1'
+ versionCode 188
+ versionName '5.3.2 BETA'
setProperty("archivesBaseName", "DSub $versionName")
resConfigs "de", "es", "fr", "hu", "nl", "pt-rPT", "ru", "sv"
}
diff --git a/app/src/main/res/xml/changelog.xml b/app/src/main/res/xml/changelog.xml
index 169edaa09..7abec09c2 100644
--- a/app/src/main/res/xml/changelog.xml
+++ b/app/src/main/res/xml/changelog.xml
@@ -1,5 +1,11 @@
+
+ Add support for casting Internet Radio to ChromeCast/DLNA
+ Add support for Play Title by Artist from Google Search
+ Use Google Play SSL
+ Move to more modern connection framework
+ Fix Internet Radio streams which point to playlistsDon't show playback speed button below Android 6.0
From 3789eba4794eed03d1bf703ed648f08b9e55b90a Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Thu, 13 Oct 2016 16:06:22 -0700
Subject: [PATCH 14/28] Fixes #749: Fix background cache refresh screwing up
Show All
---
.../dsub/fragments/SelectDirectoryFragment.java | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
index 0ac968b7f..a10c0ed50 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
@@ -459,8 +459,12 @@ protected MusicDirectory load(MusicService service) throws Exception {
}
List songs = new ArrayList();
getSongsRecursively(root, songs);
- root.replaceChildren(songs);
- return root;
+
+ // CachedMusicService is refreshing this data in the background, so will wipe out the songs list from root
+ MusicDirectory clonedRoot = new MusicDirectory(songs);
+ clonedRoot.setId(root.getId());
+ clonedRoot.setName(root.getName());
+ return clonedRoot;
}
private void getSongsRecursively(MusicDirectory parent, List songs) throws Exception {
@@ -916,7 +920,7 @@ protected void done(Void result) {
for(Integer index: indexes) {
entryGridAdapter.removeAt(index);
}
- Util.toast(context, context.getResources().getString(R.string.removed_playlist, indexes.size(), name));
+ Util.toast(context, context.getResources().getString(R.string.removed_playlist, String.valueOf(indexes.size()), name));
}
@Override
From 811f2a397549ee8d819c39a75809f363d576d44e Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Thu, 13 Oct 2016 16:49:30 -0700
Subject: [PATCH 15/28] Fixes #748: Show albums for Show All
---
.../daneren2005/dsub/fragments/SelectDirectoryFragment.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
index a10c0ed50..d3a0bfe84 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java
@@ -737,7 +737,7 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if(!artist) {
entryGridAdapter.setShowArtist(true);
}
- if(topTracks) {
+ if(topTracks || showAll) {
entryGridAdapter.setShowAlbum(true);
}
From b53cff3acfb12980001c30cf262d8f74f4740e68 Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Mon, 17 Oct 2016 16:59:35 -0700
Subject: [PATCH 16/28] Add some better error handling to saving playlists +
log errors
---
.../dsub/fragments/SubsonicFragment.java | 24 +++++++++++++------
1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
index e138f4dd4..de2303097 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java
@@ -1135,16 +1135,24 @@ protected void createNewPlaylist(final List songs, final boolean getSugge
final EditText playlistNameView = (EditText) layout.findViewById(R.id.save_playlist_name);
final CheckBox overwriteCheckBox = (CheckBox) layout.findViewById(R.id.save_playlist_overwrite);
if(getSuggestion) {
- String playlistName = (getDownloadService() != null) ? getDownloadService().getSuggestedPlaylistName() : null;
+ DownloadService downloadService = getDownloadService();
+ String playlistName = null;
+ String playlistId = null;
+ if(downloadService != null) {
+ playlistName = downloadService.getSuggestedPlaylistName();
+ playlistId = downloadService.getSuggestedPlaylistId();
+ }
if (playlistName != null) {
playlistNameView.setText(playlistName);
- try {
- if(ServerInfo.checkServerVersion(context, "1.8.0") && Integer.parseInt(getDownloadService().getSuggestedPlaylistId()) != -1) {
- overwriteCheckBox.setChecked(true);
- overwriteCheckBox.setVisibility(View.VISIBLE);
+ if(playlistId != null) {
+ try {
+ if (ServerInfo.checkServerVersion(context, "1.8.0") && Integer.parseInt(playlistId) != -1) {
+ overwriteCheckBox.setChecked(true);
+ overwriteCheckBox.setVisibility(View.VISIBLE);
+ }
+ } catch (Exception e) {
+ Log.i(TAG, "Playlist id isn't a integer, probably MusicCabinet", e);
}
- } catch(Exception e) {
- Log.i(TAG, "Playlist id isn't a integer, probably MusicCabinet");
}
} else {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
@@ -1205,6 +1213,7 @@ protected void done(Void result) {
@Override
protected void error(Throwable error) {
String msg = context.getResources().getString(R.string.download_playlist_error) + " " + getErrorMessage(error);
+ Log.e(TAG, "Failed to create playlist", error);
Util.toast(context, msg);
}
}.execute();
@@ -1234,6 +1243,7 @@ protected void error(Throwable error) {
msg = context.getResources().getString(R.string.download_playlist_error) + " " + getErrorMessage(error);
}
+ Log.e(TAG, "Failed to overwrite playlist", error);
Util.toast(context, msg, false);
}
}.execute();
From 4f2655b59a323f1936a72b2622e693c589f18fbb Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Fri, 14 Oct 2016 13:30:01 -0700
Subject: [PATCH 17/28] HttpUrlConnection doesn't auto follow http -> https
redirects
---
.../dsub/service/RESTMusicService.java | 21 +++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
index b05a569da..cb8871a08 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
@@ -1875,12 +1875,29 @@ private HttpURLConnection getConnectionDirect(Context context, String url, Map= 500) {
throw new IOException("Error code: " + connection.getResponseCode());
}
- detectRedirect(context, urlObj, connection);
+ if(detectRedirect(context, urlObj, connection)) {
+ String rewrittenUrl = rewriteUrlWithRedirect(context, url);
+ if(!rewrittenUrl.equals(url)) {
+ connection.disconnect();
+ return getConnectionDirect(context, rewrittenUrl, headers, minNetworkTimeout);
+ }
+ }
+
return connection;
}
- private void detectRedirect(Context context, URL originalUrl, HttpURLConnection connection) throws Exception {
+ // Returns true when we should immediately retry with the redirect
+ private boolean detectRedirect(Context context, URL originalUrl, HttpURLConnection connection) throws Exception {
+ if(connection.getResponseCode() == HttpURLConnection.HTTP_MOVED_TEMP || connection.getResponseCode() == HttpURLConnection.HTTP_MOVED_PERM) {
+ String redirectLocation = connection.getHeaderField("Location");
+ if(redirectLocation != null) {
+ detectRedirect(context, originalUrl.toExternalForm(), redirectLocation);
+ return true;
+ }
+ }
+
detectRedirect(context, originalUrl, connection.getURL());
+ return false;
}
private void detectRedirect(Context context, URL originalUrl, URL redirectedUrl) throws Exception {
detectRedirect(context, originalUrl.toExternalForm(), redirectedUrl.toExternalForm());
From a76a2884f0457c32f4fd2284ff80b9ba9cb5ce3f Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Mon, 17 Oct 2016 17:07:43 -0700
Subject: [PATCH 18/28] Fix accepting self signed certificates
---
ServerProxy | 2 +-
.../dsub/service/RESTMusicService.java | 29 +++++++++++++++++++
.../dsub/service/RemoteController.java | 3 +-
3 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/ServerProxy b/ServerProxy
index ed3ff58dc..a8a756261 160000
--- a/ServerProxy
+++ b/ServerProxy
@@ -1 +1 @@
-Subproject commit ed3ff58dc83f40ad3437fc30e63d7b437939c2bc
+Subproject commit a8a756261fa3c38b1c7df443eacff77ae4aaf137
diff --git a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
index cb8871a08..913e30bf6 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/RESTMusicService.java
@@ -86,7 +86,11 @@
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
public class RESTMusicService implements MusicService {
@@ -102,6 +106,7 @@ public class RESTMusicService implements MusicService {
private static final int HTTP_REQUEST_MAX_ATTEMPTS = 5;
private static final long REDIRECTION_CHECK_INTERVAL_MILLIS = 60L * 60L * 1000L;
+ private SSLSocketFactory sslSocketFactory;
private HostnameVerifier selfSignedHostnameVerifier;
private long redirectionLastChecked;
private int redirectionNetworkType = -1;
@@ -111,6 +116,26 @@ public class RESTMusicService implements MusicService {
private boolean hasInstalledGoogleSSL = false;
public RESTMusicService() {
+ TrustManager[] trustAllCerts = new TrustManager[]{
+ new X509TrustManager() {
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ public void checkClientTrusted(
+ java.security.cert.X509Certificate[] certs, String authType) {
+ }
+ public void checkServerTrusted(
+ java.security.cert.X509Certificate[] certs, String authType) {
+ }
+ }
+ };
+ try {
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
+ sslSocketFactory = sslContext.getSocketFactory();
+ } catch (Exception e) {
+ }
+
selfSignedHostnameVerifier = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
@@ -1868,6 +1893,7 @@ private HttpURLConnection getConnectionDirect(Context context, String url, Map
Date: Mon, 17 Oct 2016 17:08:17 -0700
Subject: [PATCH 19/28] Change Show all media -> Show all songs
---
app/src/main/res/values/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9ac609c37..0d9c2aa1e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -103,7 +103,7 @@
Add ChannelKeep SyncedStop syncing
- Show all media
+ Show all songsShow ArtistShareDelete Cache
From 862e767d938c1a3e574a8ecf58d9e7f0c83786f9 Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Mon, 17 Oct 2016 17:08:34 -0700
Subject: [PATCH 20/28] Second beta released with self-signed cert fixes
---
app/build.gradle | 4 ++--
app/src/main/res/xml/changelog.xml | 6 ++++++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 92dfa7313..47b3df01e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,8 +8,8 @@ android {
applicationId "github.daneren2005.dsub"
minSdkVersion 14
targetSdkVersion 23
- versionCode 188
- versionName '5.3.2 BETA'
+ versionCode 189
+ versionName '5.3.2 BETA 2'
setProperty("archivesBaseName", "DSub $versionName")
resConfigs "de", "es", "fr", "hu", "nl", "pt-rPT", "ru", "sv"
}
diff --git a/app/src/main/res/xml/changelog.xml b/app/src/main/res/xml/changelog.xml
index 7abec09c2..b1dd59303 100644
--- a/app/src/main/res/xml/changelog.xml
+++ b/app/src/main/res/xml/changelog.xml
@@ -1,5 +1,11 @@
+
+ Fix errors with Self Signed Certificates
+ Fix error following redirect from http to https
+ Fix Show all media sometimes failing
+ Show album instead of artist for Show all media
+ Add support for casting Internet Radio to ChromeCast/DLNAAdd support for Play Title by Artist from Google Search
From 7e27f21daed8d79b0f582d26d1380e3967b1f7ac Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Wed, 19 Oct 2016 17:15:14 -0700
Subject: [PATCH 21/28] Clear tint cache on exit so we can fix if bad colors
get stuck
---
.../daneren2005/dsub/activity/SubsonicActivity.java | 2 +-
.../dsub/activity/SubsonicFragmentActivity.java | 8 ++------
.../java/github/daneren2005/dsub/util/DrawableTint.java | 2 +-
3 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
index 9b14f4f65..dcf966a8d 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
@@ -245,7 +245,7 @@ protected void onResume() {
if (theme != null && !theme.equals(ThemeUtil.getTheme(this)) || fullScreen != prefs.getBoolean(Constants.PREFERENCES_KEY_FULL_SCREEN, false) || actionbarColored != prefs.getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true)) {
restart();
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
- DrawableTint.wipeTintCache();
+ DrawableTint.clearCache();
}
populateTabs();
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
index e37c6d49f..fb8221c80 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java
@@ -28,7 +28,6 @@
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.os.Bundle;
-import android.os.Handler;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.support.v4.app.FragmentManager;
@@ -38,8 +37,6 @@
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CheckBox;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
@@ -49,9 +46,6 @@
import java.io.File;
import java.util.Date;
import java.util.List;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
import github.daneren2005.dsub.R;
import github.daneren2005.dsub.domain.MusicDirectory;
@@ -78,6 +72,7 @@
import github.daneren2005.dsub.service.MusicServiceFactory;
import github.daneren2005.dsub.updates.Updater;
import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.DrawableTint;
import github.daneren2005.dsub.util.FileUtil;
import github.daneren2005.dsub.util.SilentBackgroundTask;
import github.daneren2005.dsub.util.UserUtil;
@@ -139,6 +134,7 @@ public void onCreate(Bundle savedInstanceState) {
stopService(new Intent(this, DownloadService.class));
finish();
getImageLoader().clearCache();
+ DrawableTint.clearCache();
} else if(getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW)) {
getIntent().putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, "Download");
lastSelectedPosition = R.id.drawer_downloading;
diff --git a/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java b/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java
index cc8e241d0..f03906a8a 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/DrawableTint.java
@@ -95,7 +95,7 @@ public static Drawable getTintedAttrDrawable(Context context, @AttrRes int drawa
return getTintedDrawable(context, drawableRes, colorAttr);
}
- public static void wipeTintCache() {
+ public static void clearCache() {
attrMap.clear();
tintedDrawables.clear();
}
From f9894f5ff28cdd166f64ba6d1fe6bee59b6dcc6d Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Wed, 19 Oct 2016 17:22:22 -0700
Subject: [PATCH 22/28] Remove debug timeout
---
.../main/java/github/daneren2005/dsub/util/SongDBHandler.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/app/src/main/java/github/daneren2005/dsub/util/SongDBHandler.java b/app/src/main/java/github/daneren2005/dsub/util/SongDBHandler.java
index 90c885f6d..1309ee697 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/SongDBHandler.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/SongDBHandler.java
@@ -120,7 +120,6 @@ protected synchronized void addSongsImpl(SQLiteDatabase db, int serverKey, List<
values.put(SONGS_SERVER_KEY, serverKey);
values.put(SONGS_SERVER_ID, entry.getFirst());
values.put(SONGS_COMPLETE_PATH, entry.getSecond());
- // Util.sleepQuietly(10000);
db.insertWithOnConflict(TABLE_SONGS, null, values, SQLiteDatabase.CONFLICT_IGNORE);
}
From 862f70536f672ff91bea830e42d4a906f8a466ae Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Fri, 21 Oct 2016 18:13:14 -0700
Subject: [PATCH 23/28] Upgrade to support library v24
---
app/build.gradle | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 47b3df01e..e65817969 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,7 +1,7 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 23
+ compileSdkVersion 24
buildToolsVersion "23.0.3"
defaultConfig {
@@ -46,11 +46,11 @@ android {
dependencies {
compile project(':Server Proxy')
compile fileTree(include: ['*.jar'], dir: 'libs')
- compile 'com.android.support:support-v4:23.4.+'
- compile 'com.android.support:appcompat-v7:23.4.+'
- compile 'com.android.support:mediarouter-v7:23.4.+'
- compile 'com.android.support:recyclerview-v7:23.4.+'
- compile 'com.android.support:design:23.4.+'
+ compile 'com.android.support:support-v4:24.2.+'
+ compile 'com.android.support:appcompat-v7:24.2.+'
+ compile 'com.android.support:mediarouter-v7:24.2.+'
+ compile 'com.android.support:recyclerview-v7:24.2.+'
+ compile 'com.android.support:design:24.2.+'
compile 'com.google.android.gms:play-services-cast:8.1.0'
compile 'com.sothree.slidinguppanel:library:3.0.0'
compile 'de.hdodenhof:circleimageview:1.2.1'
From b7e728dc0788a23af2eecb9f5870c3c612aa50a7 Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Fri, 21 Oct 2016 18:13:59 -0700
Subject: [PATCH 24/28] Fix for Chromecast issues. Cast API appears to have
changed so starting a new song triggers an additional COMPLETED broadcast
while it is getting the new song ready
---
.../daneren2005/dsub/service/ChromeCastController.java | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java b/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java
index 566ed773a..f9e2bfb1b 100644
--- a/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java
+++ b/app/src/main/java/github/daneren2005/dsub/service/ChromeCastController.java
@@ -23,6 +23,7 @@
import com.google.android.gms.cast.ApplicationMetadata;
import com.google.android.gms.cast.Cast;
import com.google.android.gms.cast.CastDevice;
+import com.google.android.gms.cast.CastStatusCodes;
import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.cast.MediaStatus;
@@ -343,6 +344,8 @@ else if(song.getTranscodedContentType() != null) {
public void onResult(RemoteMediaPlayer.MediaChannelResult result) {
if (result.getStatus().isSuccess()) {
// Handled in other handler
+ } else if(result.getStatus().getStatusCode() == CastStatusCodes.REPLACED) {
+ Log.w(TAG, "Request was replaced: " + currentPlaying.toString());
} else {
Log.e(TAG, "Failed to load: " + result.getStatus().toString());
failedLoad();
@@ -459,7 +462,9 @@ public void onStatusUpdated() {
break;
case MediaStatus.PLAYER_STATE_IDLE:
if (mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_FINISHED) {
- downloadService.onSongCompleted();
+ if(downloadService.getPlayerState() != PlayerState.PREPARING) {
+ downloadService.onSongCompleted();
+ }
} else if (mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_INTERRUPTED) {
if (downloadService.getPlayerState() != PlayerState.PREPARING) {
downloadService.setPlayerState(PlayerState.PREPARING);
From bb248548d25a14d695b48051d8df79422c398878 Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Fri, 21 Oct 2016 18:16:12 -0700
Subject: [PATCH 25/28] Ask for location permissions in support for the
day/night theme
---
app/src/main/AndroidManifest.xml | 1 +
.../daneren2005/dsub/activity/SubsonicActivity.java | 3 ++-
.../daneren2005/dsub/fragments/SettingsFragment.java | 12 ++++++++++++
3 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 65305cbd9..2cb24272b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -23,6 +23,7 @@
+
diff --git a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
index dcf966a8d..3b533fae6 100644
--- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
+++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicActivity.java
@@ -95,7 +95,8 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
protected static boolean actionbarColored;
private static final int MENU_GROUP_SERVER = 10;
private static final int MENU_ITEM_SERVER_BASE = 100;
- private static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
+ public static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
+ public static final int PERMISSIONS_REQUEST_LOCATION = 2;
private final List afterServiceAvailable = new ArrayList<>();
private boolean drawerIdle = true;
diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SettingsFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SettingsFragment.java
index d5ba25f5f..584a205a8 100644
--- a/app/src/main/java/github/daneren2005/dsub/fragments/SettingsFragment.java
+++ b/app/src/main/java/github/daneren2005/dsub/fragments/SettingsFragment.java
@@ -15,12 +15,14 @@
package github.daneren2005.dsub.fragments;
+import android.Manifest;
import android.accounts.Account;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
@@ -29,6 +31,8 @@
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
import android.text.InputType;
import android.util.Log;
import android.view.LayoutInflater;
@@ -47,6 +51,7 @@
import java.util.Map;
import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.activity.SubsonicActivity;
import github.daneren2005.dsub.service.DownloadService;
import github.daneren2005.dsub.service.HeadphoneListenerService;
import github.daneren2005.dsub.service.MusicService;
@@ -188,6 +193,13 @@ else if(Constants.PREFERENCES_KEY_SYNC_MOST_RECENT.equals(key)) {
} else {
context.stopService(serviceIntent);
}
+ } else if(Constants.PREFERENCES_KEY_THEME.equals(key)) {
+ String value = sharedPreferences.getString(key, null);
+ if("day/night".equals(value) || "day/black".equals(value)) {
+ if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(context, new String[]{ Manifest.permission.ACCESS_COARSE_LOCATION }, SubsonicActivity.PERMISSIONS_REQUEST_LOCATION);
+ }
+ }
}
scheduleBackup();
From 5be695799a506cb885d4a14abda3a52dbf8d3a4c Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Sat, 22 Oct 2016 14:13:12 -0700
Subject: [PATCH 26/28] Update gradle
---
ServerProxy | 2 +-
build.gradle | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/ServerProxy b/ServerProxy
index a8a756261..a4d957353 160000
--- a/ServerProxy
+++ b/ServerProxy
@@ -1 +1 @@
-Subproject commit a8a756261fa3c38b1c7df443eacff77ae4aaf137
+Subproject commit a4d957353db2634906e0d5099d7a078a111bfab9
diff --git a/build.gradle b/build.gradle
index 3dfb085aa..5b7e9efd3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.2.1'
+ classpath 'com.android.tools.build:gradle:2.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
From 2ed855cd6d14eacd77a578f4d8ac8c80ebe516de Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Sat, 22 Oct 2016 14:13:25 -0700
Subject: [PATCH 27/28] DSub 5.3.2 released
---
app/build.gradle | 4 ++--
app/src/main/res/xml/changelog.xml | 14 ++++++--------
2 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index e65817969..5f46aab81 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,8 +8,8 @@ android {
applicationId "github.daneren2005.dsub"
minSdkVersion 14
targetSdkVersion 23
- versionCode 189
- versionName '5.3.2 BETA 2'
+ versionCode 190
+ versionName '5.3.2'
setProperty("archivesBaseName", "DSub $versionName")
resConfigs "de", "es", "fr", "hu", "nl", "pt-rPT", "ru", "sv"
}
diff --git a/app/src/main/res/xml/changelog.xml b/app/src/main/res/xml/changelog.xml
index b1dd59303..dc77a5a32 100644
--- a/app/src/main/res/xml/changelog.xml
+++ b/app/src/main/res/xml/changelog.xml
@@ -1,16 +1,14 @@
-
- Fix errors with Self Signed Certificates
- Fix error following redirect from http to https
- Fix Show all media sometimes failing
- Show album instead of artist for Show all media
-
-
+ Add support for casting Internet Radio to ChromeCast/DLNAAdd support for Play Title by Artist from Google Search
- Use Google Play SSLMove to more modern connection framework
+ Use Google Play SSL
+ Show album instead of artist for Show all media
+ Ask for location permissions for Day/Night themes
+ Fix a change to the ChromeCast API
+ Fix Show all media sometimes failingFix Internet Radio streams which point to playlists
From 2b4eb81c25e6170e881c8074672d4f2ae0226f4f Mon Sep 17 00:00:00 2001
From: Scott Jackson
Date: Sat, 22 Oct 2016 14:37:07 -0700
Subject: [PATCH 28/28] Fix location permission blocking install on devices
without GPS
---
app/src/main/AndroidManifest.xml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2cb24272b..37643a216 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -30,6 +30,8 @@
+
+