[androidtv] Adds additional i18n support to ConnectionManagers (#15184)

Signed-off-by: Ben Rosenblum <rosenblumb@gmail.com>
This commit is contained in:
morph166955 2023-07-15 02:39:45 -05:00 committed by GitHub
parent 4edad54e83
commit cad86bc83b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 138 additions and 53 deletions

View File

@ -61,13 +61,15 @@ public class AndroidTVHandler extends BaseThingHandler {
private static final int THING_STATUS_FREQUENCY = 250;
private final AndroidTVDynamicCommandDescriptionProvider commandDescriptionProvider;
private final AndroidTVTranslationProvider translationProvider;
private final ThingTypeUID thingTypeUID;
private final String thingID;
public AndroidTVHandler(Thing thing, AndroidTVDynamicCommandDescriptionProvider commandDescriptionProvider,
ThingTypeUID thingTypeUID) {
AndroidTVTranslationProvider translationProvider, ThingTypeUID thingTypeUID) {
super(thing);
this.commandDescriptionProvider = commandDescriptionProvider;
this.translationProvider = translationProvider;
this.thingTypeUID = thingTypeUID;
this.thingID = this.getThing().getUID().getId();
}
@ -76,6 +78,10 @@ public class AndroidTVHandler extends BaseThingHandler {
thing.setProperty(property, value);
}
public AndroidTVTranslationProvider getTranslationProvider() {
return translationProvider;
}
public String getThingID() {
return this.thingID;
}
@ -113,21 +119,17 @@ public class AndroidTVHandler extends BaseThingHandler {
if (googletvConnectionManager != null) {
if (!googletvConnectionManager.getLoggedIn()) {
statusMessage = "GoogleTV: " + googletvConnectionManager.getStatusMessage();
failed = true;
} else {
statusMessage = "GoogleTV: ONLINE";
}
statusMessage = "GoogleTV: " + googletvConnectionManager.getStatusMessage();
}
if (THING_TYPE_SHIELDTV.equals(thingTypeUID)) {
if (shieldtvConnectionManager != null) {
if (!shieldtvConnectionManager.getLoggedIn()) {
statusMessage = statusMessage + " | ShieldTV: " + shieldtvConnectionManager.getStatusMessage();
failed = true;
} else {
statusMessage = statusMessage + " | ShieldTV: ONLINE";
}
statusMessage = statusMessage + " | ShieldTV: " + shieldtvConnectionManager.getStatusMessage();
}
}

View File

@ -18,6 +18,8 @@ import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
@ -41,11 +43,14 @@ public class AndroidTVHandlerFactory extends BaseThingHandlerFactory {
THING_TYPE_SHIELDTV);
private final AndroidTVDynamicCommandDescriptionProvider commandDescriptionProvider;
private final AndroidTVTranslationProvider translationProvider;
@Activate
public AndroidTVHandlerFactory(
final @Reference AndroidTVDynamicCommandDescriptionProvider commandDescriptionProvider) {
final @Reference AndroidTVDynamicCommandDescriptionProvider commandDescriptionProvider,
final @Reference TranslationProvider i18nProvider, final @Reference LocaleProvider localeProvider) {
this.commandDescriptionProvider = commandDescriptionProvider;
this.translationProvider = new AndroidTVTranslationProvider(i18nProvider, localeProvider);
}
@Override
@ -56,6 +61,6 @@ public class AndroidTVHandlerFactory extends BaseThingHandlerFactory {
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
return new AndroidTVHandler(thing, commandDescriptionProvider, thingTypeUID);
return new AndroidTVHandler(thing, commandDescriptionProvider, translationProvider, thingTypeUID);
}
}

View File

@ -0,0 +1,54 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.androidtv.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link AndroidTVTranslationProvider} provides i18n message lookup.
*
* @author Ben Rosenblum - Initial contribution
*/
@NonNullByDefault
public class AndroidTVTranslationProvider {
private final Bundle bundle;
private final TranslationProvider i18nProvider;
private final LocaleProvider localeProvider;
private final Logger logger = LoggerFactory.getLogger(AndroidTVTranslationProvider.class);
public AndroidTVTranslationProvider(TranslationProvider i18nProvider, LocaleProvider localeProvider) {
this.bundle = FrameworkUtil.getBundle(this.getClass());
this.i18nProvider = i18nProvider;
this.localeProvider = localeProvider;
}
public String getText(String key, @Nullable Object... arguments) {
@Nullable
String text = i18nProvider.getText(bundle, key, null, localeProvider.getLocale(), arguments);
if (text != null) {
logger.trace("Translated: {} as {}", key, text);
return text;
} else {
logger.trace("Failed to translate: {}", key);
return key;
}
}
}

View File

@ -60,6 +60,7 @@ import javax.net.ssl.X509TrustManager;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.androidtv.internal.AndroidTVHandler;
import org.openhab.binding.androidtv.internal.AndroidTVTranslationProvider;
import org.openhab.binding.androidtv.internal.utils.AndroidTVPKI;
import org.openhab.core.OpenHAB;
import org.openhab.core.library.types.NextPreviousType;
@ -97,6 +98,7 @@ public class GoogleTVConnectionManager {
private final AndroidTVHandler handler;
private GoogleTVConfiguration config;
private final AndroidTVTranslationProvider translationProvider;
private @NonNullByDefault({}) SSLSocketFactory sslSocketFactory;
private @Nullable SSLSocket sslSocket;
@ -166,6 +168,7 @@ public class GoogleTVConnectionManager {
messageParser = new GoogleTVMessageParser(this);
this.config = config;
this.handler = handler;
this.translationProvider = handler.getTranslationProvider();
this.connectionManager = this;
this.scheduler = handler.getScheduler();
this.encryptionKey = androidtvPKI.generateEncryptionKey();
@ -177,6 +180,7 @@ public class GoogleTVConnectionManager {
messageParser = new GoogleTVMessageParser(this);
this.config = config;
this.handler = handler;
this.translationProvider = handler.getTranslationProvider();
this.connectionManager = connectionManager;
this.scheduler = handler.getScheduler();
this.encryptionKey = androidtvPKI.generateEncryptionKey();
@ -298,16 +302,17 @@ public class GoogleTVConnectionManager {
private void setStatus(boolean isLoggedIn) {
if (isLoggedIn) {
setStatus(isLoggedIn, "ONLINE");
setStatus(isLoggedIn, "online.online");
} else {
setStatus(isLoggedIn, "UNKNOWN");
setStatus(isLoggedIn, "offline.unknown");
}
}
private void setStatus(boolean isLoggedIn, String statusMessage) {
if ((this.isLoggedIn != isLoggedIn) || (!this.statusMessage.equals(statusMessage))) {
String translatedMessage = translationProvider.getText(statusMessage);
if ((this.isLoggedIn != isLoggedIn) || (!this.statusMessage.equals(translatedMessage))) {
this.isLoggedIn = isLoggedIn;
this.statusMessage = statusMessage;
this.statusMessage = translatedMessage;
handler.checkThingStatus();
}
}
@ -501,10 +506,10 @@ public class GoogleTVConnectionManager {
shimAsyncInitializeTask = scheduler.submit(this::shimInitialize);
}
} catch (NoSuchAlgorithmException | IOException e) {
setStatus(false, "Error initializing keystore");
setStatus(false, "offline.error-initalizing-keystore");
logger.debug("Error initializing keystore", e);
} catch (UnrecoverableKeyException e) {
setStatus(false, "Key unrecoverable with supplied password");
setStatus(false, "offline.key-unrecoverable-with-supplied-password");
} catch (GeneralSecurityException e) {
logger.debug("General security exception", e);
} catch (Exception e) {
@ -530,12 +535,12 @@ public class GoogleTVConnectionManager {
logger.debug("{} - Connection to {}:{} {} successful", handler.getThingID(), config.ipAddress,
config.port, config.mode);
} catch (UnknownHostException e) {
setStatus(false, "Unknown host");
setStatus(false, "offline.unknown-host");
logger.debug("{} - Unknown host {}", handler.getThingID(), config.ipAddress);
return;
} catch (IllegalArgumentException e) {
// port out of valid range
setStatus(false, "Invalid port number");
setStatus(false, "offline.invalid-port-number");
logger.debug("{} - Invalid port number {}:{}", handler.getThingID(), config.ipAddress, config.port);
return;
} catch (InterruptedIOException e) {
@ -546,7 +551,7 @@ public class GoogleTVConnectionManager {
String message = e.getMessage();
if ((message != null) && (message.contains("certificate_unknown"))
&& (!config.mode.equals(PIN_MODE)) && (!config.shim)) {
setStatus(false, "PIN Process Incomplete");
setStatus(false, "offline.pin-process-incomplete");
logger.debug("{} - GoogleTV PIN Process Incomplete", handler.getThingID());
reconnectTaskCancel(true);
startChildConnectionManager(this.config.port + 1, PIN_MODE);
@ -562,8 +567,8 @@ public class GoogleTVConnectionManager {
this.shimServerSocket = null;
}
} else {
setStatus(false, "Error opening GoogleTV SSL connection. Check log.");
logger.info("{} - Error opening GoogleTV SSL connection to {}:{} {}", handler.getThingID(),
setStatus(false, "offline.error-opening-ssl-connection-check-log");
logger.info("{} - Error opening SSL connection to {}:{} {}", handler.getThingID(),
config.ipAddress, config.port, e.getMessage());
disconnect(false);
scheduleConnectRetry(config.reconnect); // Possibly a temporary problem. Try again later.
@ -571,7 +576,7 @@ public class GoogleTVConnectionManager {
return;
}
setStatus(false, "Initializing");
setStatus(false, "offline.initializing");
logger.trace("{} - Starting Reader Thread for {}:{}", handler.getThingID(), config.ipAddress,
config.port);
@ -826,7 +831,7 @@ public class GoogleTVConnectionManager {
synchronized (connectionLock) {
if (!this.disposing) {
logger.debug("{} - Attempting to reconnect to the GoogleTV", handler.getThingID());
setStatus(false, "reconnecting");
setStatus(false, "offline.reconnecting");
disconnect(false);
connect();
}
@ -852,12 +857,12 @@ public class GoogleTVConnectionManager {
}
} catch (InterruptedIOException e) {
logger.debug("Interrupted while sending to GoogleTV");
setStatus(false, "Interrupted");
setStatus(false, "offline.interrupted");
break; // exit loop and terminate thread
} catch (IOException e) {
logger.warn("{} - Communication error, will try to reconnect GoogleTV. Error: {}",
handler.getThingID(), e.getMessage());
setStatus(false, "Communication error, will try to reconnect");
setStatus(false, "offline.communication-error-will-try-to-reconnect");
sendQueue.add(command); // Requeue command
this.isLoggedIn = false;
reconnect();
@ -944,12 +949,12 @@ public class GoogleTVConnectionManager {
}
} catch (InterruptedIOException e) {
logger.debug("Interrupted while reading");
setStatus(false, "Interrupted");
setStatus(false, "offline.interrupted");
} catch (IOException e) {
String message = e.getMessage();
if ((message != null) && (message.contains("certificate_unknown")) && (!config.mode.equals(PIN_MODE))
&& (!config.shim)) {
setStatus(false, "PIN Process Incomplete");
setStatus(false, "offline.pin-process-incomplete");
logger.debug("{} - GoogleTV PIN Process Incomplete", handler.getThingID());
reconnectTaskCancel(true);
startChildConnectionManager(this.config.port + 1, PIN_MODE);
@ -966,11 +971,11 @@ public class GoogleTVConnectionManager {
}
} else {
logger.debug("I/O error while reading from stream: {}", e.getMessage());
setStatus(false, "I/O Error");
setStatus(false, "offline.io-error");
}
} catch (RuntimeException e) {
logger.warn("Runtime exception in reader thread", e);
setStatus(false, "Runtime exception");
setStatus(false, "offline.runtime-exception");
} finally {
logger.debug("{} - Message reader thread exiting {}", handler.getThingID(), config.port);
}
@ -1038,13 +1043,13 @@ public class GoogleTVConnectionManager {
}
} catch (InterruptedIOException e) {
logger.debug("Interrupted while reading");
setStatus(false, "Interrupted");
setStatus(false, "offline.interrupted");
} catch (IOException e) {
logger.debug("I/O error while reading from stream: {}", e.getMessage());
setStatus(false, "I/O Error");
setStatus(false, "offline.io-error");
} catch (RuntimeException e) {
logger.warn("Runtime exception in reader thread", e);
setStatus(false, "Runtime exception");
setStatus(false, "offline.runtime-exception");
} finally {
logger.debug("Shim message reader thread exiting {}", config.port);
}
@ -1263,7 +1268,7 @@ public class GoogleTVConnectionManager {
if (config.mode.equals(DEFAULT_MODE)) {
if ((!isLoggedIn) && (command.toString().equals("REQUEST"))
&& (childConnectionManager == null)) {
setStatus(false, "User Forced PIN Process");
setStatus(false, "offline.user-forced-pin-process");
logger.debug("{} - User Forced PIN Process", handler.getThingID());
disconnect(true);
startChildConnectionManager(config.port + 1, PIN_MODE);

View File

@ -58,6 +58,7 @@ import javax.net.ssl.X509TrustManager;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.androidtv.internal.AndroidTVHandler;
import org.openhab.binding.androidtv.internal.AndroidTVTranslationProvider;
import org.openhab.binding.androidtv.internal.utils.AndroidTVPKI;
import org.openhab.core.OpenHAB;
import org.openhab.core.library.types.StringType;
@ -87,6 +88,7 @@ public class ShieldTVConnectionManager {
private final AndroidTVHandler handler;
private ShieldTVConfiguration config;
private final AndroidTVTranslationProvider translationProvider;
private @NonNullByDefault({}) SSLSocketFactory sslSocketFactory;
private @Nullable SSLSocket sslSocket;
@ -148,6 +150,7 @@ public class ShieldTVConnectionManager {
messageParser = new ShieldTVMessageParser(this);
this.config = config;
this.handler = handler;
this.translationProvider = handler.getTranslationProvider();
this.scheduler = handler.getScheduler();
this.encryptionKey = androidtvPKI.generateEncryptionKey();
initialize();
@ -215,16 +218,17 @@ public class ShieldTVConnectionManager {
private void setStatus(boolean isLoggedIn) {
if (isLoggedIn) {
setStatus(isLoggedIn, "ONLINE");
setStatus(isLoggedIn, "online.online");
} else {
setStatus(isLoggedIn, "UNKNOWN");
setStatus(isLoggedIn, "offline.unknown");
}
}
private void setStatus(boolean isLoggedIn, String statusMessage) {
if ((this.isLoggedIn != isLoggedIn) || (!this.statusMessage.equals(statusMessage))) {
String translatedMessage = translationProvider.getText(statusMessage);
if ((this.isLoggedIn != isLoggedIn) || (!this.statusMessage.equals(translatedMessage))) {
this.isLoggedIn = isLoggedIn;
this.statusMessage = statusMessage;
this.statusMessage = translatedMessage;
handler.checkThingStatus();
}
}
@ -399,10 +403,10 @@ public class ShieldTVConnectionManager {
shimAsyncInitializeTask = scheduler.submit(this::shimInitialize);
}
} catch (NoSuchAlgorithmException | IOException e) {
setStatus(false, "Error initializing keystore");
setStatus(false, "offline.error-initalizing-keystore");
logger.debug("Error initializing keystore", e);
} catch (UnrecoverableKeyException e) {
setStatus(false, "Key unrecoverable with supplied password");
setStatus(false, "offline.key-unrecoverable-with-supplied-password");
} catch (GeneralSecurityException e) {
logger.debug("General security exception", e);
} catch (Exception e) {
@ -424,26 +428,26 @@ public class ShieldTVConnectionManager {
new InputStreamReader(sslSocket.getInputStream(), StandardCharsets.ISO_8859_1));
this.sslSocket = sslSocket;
} catch (UnknownHostException e) {
setStatus(false, "Unknown host");
setStatus(false, "offline.unknown-host");
return;
} catch (IllegalArgumentException e) {
// port out of valid range
setStatus(false, "Invalid port number");
setStatus(false, "offline.invalid-port-number");
return;
} catch (InterruptedIOException e) {
logger.debug("Interrupted while establishing ShieldTV connection");
Thread.currentThread().interrupt();
return;
} catch (IOException e) {
setStatus(false, "Error opening ShieldTV SSL connection. Check log.");
logger.info("{} - Error opening ShieldTV SSL connection to {}:{} {}", handler.getThingID(),
config.ipAddress, config.port, e.getMessage());
setStatus(false, "offline.error-opening-ssl-connection-check-log");
logger.info("{} - Error opening SSL connection to {}:{} {}", handler.getThingID(), config.ipAddress,
config.port, e.getMessage());
disconnect(false);
scheduleConnectRetry(config.reconnect); // Possibly a temporary problem. Try again later.
return;
}
setStatus(false, "Initializing");
setStatus(false, "offline.initializing");
Thread readerThread = new Thread(this::readerThreadJob, "ShieldTV reader " + handler.getThingID());
readerThread.setDaemon(true);
@ -640,7 +644,7 @@ public class ShieldTVConnectionManager {
synchronized (connectionLock) {
if (!this.disposing) {
logger.debug("{} - Attempting to reconnect to the ShieldTV", handler.getThingID());
setStatus(false, "reconnecting");
setStatus(false, "offline.reconnecting");
disconnect(false);
connect();
}
@ -666,12 +670,12 @@ public class ShieldTVConnectionManager {
}
} catch (InterruptedIOException e) {
logger.debug("Interrupted while sending to ShieldTV");
setStatus(false, "Interrupted");
setStatus(false, "offline.interrupted");
break; // exit loop and terminate thread
} catch (IOException e) {
logger.warn("{} - Communication error, will try to reconnect ShieldTV. Error: {}",
handler.getThingID(), e.getMessage());
setStatus(false, "Communication error, will try to reconnect");
setStatus(false, "offline.communication-error-will-try-to-reconnect");
sendQueue.add(command); // Requeue command
this.isLoggedIn = false;
reconnect();
@ -785,13 +789,13 @@ public class ShieldTVConnectionManager {
}
} catch (InterruptedIOException e) {
logger.debug("Interrupted while reading");
setStatus(false, "Interrupted");
setStatus(false, "offline.interrupted");
} catch (IOException e) {
logger.debug("I/O error while reading from stream: {}", e.getMessage());
setStatus(false, "I/O Error");
setStatus(false, "offline.io-error");
} catch (RuntimeException e) {
logger.warn("Runtime exception in reader thread", e);
setStatus(false, "Runtime exception");
setStatus(false, "offline.runtime-exception");
} finally {
logger.debug("{} - Message reader thread exiting", handler.getThingID());
}
@ -886,13 +890,13 @@ public class ShieldTVConnectionManager {
}
} catch (InterruptedIOException e) {
logger.debug("Interrupted while reading");
setStatus(false, "Interrupted");
setStatus(false, "offline.interrupted");
} catch (IOException e) {
logger.debug("I/O error while reading from stream: {}", e.getMessage());
setStatus(false, "I/O Error");
setStatus(false, "offline.io-error");
} catch (RuntimeException e) {
logger.warn("Runtime exception in reader thread", e);
setStatus(false, "Runtime exception");
setStatus(false, "offline.runtime-exception");
} finally {
logger.debug("Message reader thread exiting");
}

View File

@ -66,4 +66,19 @@ channel-type.androidtv.player.description = Player Control
offline.protocols-starting = Protocols Starting
offline.googletv-address-not-specified = googletv address not specified
offline.shieldtv-address-not-specified = shieldtv address not specified
offline.error-initalizing-keystore = Error initializing keystore
offline.key-unrecoverable-with-supplied-password = Key unrecoverable with supplied password
offline.unknown-host = Unknown host
offline.invalid-port-number = Invalid port number
offline.pin-process-incomplete = PIN Process Incomplete
offline.error-opening-ssl-connection-check-log = Error opening SSL connection. Check log.
offline.initializing = Initializing
offline.reconnecting = Reconnecting
offline.communication-error-will-try-to-reconnect = Communication error, will try to reconnect
offline.interrupted = Interrupted
offline.io-error = I/O Error
offline.runtime-exception = Runtime exception
offline.user-forced-pin-process = User Forced PIN Process
offline.unknown = Unknown
online.online = Online