/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Licensed 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.
*/
package com.example.google.tv.anymotelibrary.client;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import com.example.google.tv.anymotelibrary.connection.ConnectingTask;
import com.example.google.tv.anymotelibrary.connection.KeyStoreManager;
import com.example.google.tv.anymotelibrary.connection.PairingActivity;
import com.example.google.tv.anymotelibrary.connection.PairingPINDialogBuilder;
import com.example.google.tv.anymotelibrary.connection.TvDevice;
import com.example.google.tv.anymotelibrary.connection.TvDiscoveryService;
import com.example.google.tv.anymotelibrary.connection.ConnectingTask.ConnectionListener;
import com.example.google.tv.anymotelibrary.connection.PairingPINDialogBuilder.PinListener;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;
/**
* The central point to connect to Anymote serivce running on a Google TV device
* and send commands. The clients of this library should bind to this service
* and implement the ClientListener interface provided in this service.
*/
public class AnymoteClientService extends Service implements ConnectionListener {
private static final String LOG_TAG = "AnymoteConnectionService";
private List<ClientListener> clientListeners;
private List<PairingListener> pairingListeners;
private ConnectingTask connectingTask;
private Context context;
private TvDiscoveryService tvDiscovery;
private TvDevice target;
private KeyStoreManager mKeyStoreManager;
private static AnymoteSender anymoteSender;
/**
* All client applications should implement this listener. It provides
* callbacks when the state of connection to the Anymote service running on
* Google TV device changes.
*/
public interface ClientListener {
/**
* This callback method is called when connection to Anymote service has
* been established.
*
* @param anymoteSender The proxy to send Anymote messages.
*/
public void onConnected(AnymoteSender anymoteSender);
/**
* This callback method is called when connection to Anymote service is
* lost.
*/
public void onDisconnected();
/**
* This callback method is called when there was a error in establishing
* connection to the Anymote service.
*/
public void onConnectionFailed();
}
/**
* The Listener for Pairing stage.
*/
public interface PairingListener {
/**
* This callback method is called when the user is required to enter the
* secret paring code.
*
* @param pairingListener
*/
public void onPairingCodeRequired(PairingPINDialogBuilder.PinListener pairingListener);
}
@Override
public void onCreate() {
super.onCreate();
initialize();
}
/*
* (non-Javadoc)
* @see android.app.Service#onStartCommand(android.content.Intent, int, int)
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onDestroy() {
if (connectingTask != null) {
connectingTask.disconnect();
}
tvDiscovery = null;
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return new AnymoteClientServiceBinder();
}
/**
* @author mjoshi@google.com (Megha Joshi)
*/
public class AnymoteClientServiceBinder extends Binder {
/**
* Local binder to the service.
*
* @return binder to the service.
*/
public AnymoteClientService getService() {
return AnymoteClientService.this;
}
}
private void initialize() {
clientListeners = new ArrayList<ClientListener>();
pairingListeners = new ArrayList<PairingListener>();
try {
mKeyStoreManager = new KeyStoreManager();
mKeyStoreManager.initialize(this);
} catch (GeneralSecurityException e) {
Log.e(LOG_TAG, "Security exception during initialization! Aborting", e);
stopSelf();
return;
}
Intent intent2 = new Intent();
intent2.setComponent(new ComponentName(
getApplicationContext(),
"com.example.google.tv.anymotelibrary.connection.PairingActivity"));
intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplication().startActivity(intent2);
}
/**
* Service lost existing connection.
*/
@Override
public void onConnectionDisconnected() {
this.anymoteSender = null;
if (target != null) {
for (ClientListener listener : clientListeners) {
listener.onDisconnected();
}
target = null;
}
}
/**
* Initiate new connection to specified TV device.
*
* @param device the device to connect to.
* @param activity which uses the connection.
* @return {@code true} if already connected to the specified device.
*/
public boolean connect(TvDevice device, Context activity) {
if (target != null && target.equals(device)) {
return true;
}
this.context = activity;
if (connectingTask != null) {
connectingTask.cancel();
connectingTask = null;
}
target = null;
connectingTask = new ConnectingTask(device, mKeyStoreManager, activity);
connectingTask.setConnectionListener(this);
connectingTask.start();
return false;
}
/**
* Re-establish connection to current target.
*/
public void reconnect() {
TvDevice device = target;
if (device != null) {
connect(device, context);
}
}
/**
* The TV device that is connected.
*
* @return connected TV device.
*/
public TvDevice getCurrentDevice() {
return target;
}
/**
* Adds client listeners.
*
* @param listener client listener.
*/
public void attachClientListener(ClientListener listener) {
clientListeners.add(listener);
}
/**
* Removes client listener.
*
* @param listener client listener.
*/
public void detachClientListener(ClientListener listener) {
clientListeners.remove(listener);
}
/**
* Adds pairing listeners.
*
* @param listener pairing listener.
*/
public void attachPairingListener(PairingListener listener) {
pairingListeners.add(listener);
}
/**
* Removes pairing listeners.
*
* @param listener
*/
public void detachPairingListener(PairingListener listener) {
pairingListeners.remove(listener);
}
/**
* Called by anybody who wants to cancel pending connection.
*/
public void cancelConnection() {
if (connectingTask != null) {
connectingTask.cancel();
connectingTask = null;
}
}
/**
* Called by Pairing PIN Dialog when user provides secret.
*
* @param secret The secret entered by the user.
*/
public void setPairingSecret(String secret) {
if (connectingTask != null) {
connectingTask.setSecret(secret);
}
}
/**
* Called when connecting task successfully established connection.
*/
public void onConnected(TvDevice device, AnymoteSender anymoteSender) {
target = device;
this.anymoteSender = anymoteSender;
// Broadcast new connection.
for (ClientListener listener : clientListeners) {
listener.onConnected(anymoteSender);
}
}
public static AnymoteSender getAnymoteSender() {
return anymoteSender;
}
/**
* Returns instance of TV discovery service, creates new instance if one
* does not already exist.
*
* @return instance of TV discovery service.
*/
public synchronized TvDiscoveryService getTvDiscovery() {
if (tvDiscovery == null) {
tvDiscovery = new TvDiscoveryService(this);
}
return tvDiscovery;
}
@Override
public void onSecretRequired(PinListener pinListener) {
for (PairingListener listener : pairingListeners) {
listener.onPairingCodeRequired(pinListener);
}
}
@Override
public void onConnectionFailed() {
this.anymoteSender = null;
for (ClientListener listener : clientListeners) {
listener.onConnectionFailed();
}
}
@Override
public void onConnectionPairing() {
}
}