/*
* TV-Browser for Android
* Copyright (C) 2014 René Mach (rene@tvbrowser.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to use, copy, modify or merge the Software,
* furthermore to publish and distribute the Software free of charge without modifications and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.tvbrowser.devplugin;
import java.util.ArrayList;
import java.util.Arrays;
import org.tvbrowser.settings.SettingConstants;
import org.tvbrowser.tvbrowser.Logging;
import org.tvbrowser.tvbrowser.R;
import org.tvbrowser.utils.PrefUtils;
import org.tvbrowser.utils.UiUtils;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.BitmapDrawable;
import android.os.IBinder;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.text.style.ImageSpan;
/**
* A class with a service connection to a specific TV-Browser Plugin.
*
* @author René Mach
*/
public class PluginServiceConnection implements ServiceConnection, Comparable<PluginServiceConnection> {
private Plugin mPlugin;
private String mPackageId;
private String mPluginId;
private Context mContext;
private String mPluginName;
private String mPluginVersion;
private String mPluginDescription;
private String mPluginAuthor;
private String mPluginLicense;
private boolean mHasPreferences;
private ImageSpan mIcon;
private Runnable mBindCallback;
private ArrayList<Context> mBindContextList;
public PluginServiceConnection(String packageId, String id, Context context) {
mPackageId = packageId;
mPluginId = id;
mContext = context;
mHasPreferences = false;
mBindContextList = new ArrayList<Context>();
doLog(mContext, "Plugin connection created: " + packageId + " " + id);
}
public boolean bindPlugin(Context context, Runnable bindCallback) {
boolean bound = false;
mBindCallback = bindCallback;
doLog(mContext, "Plugin connection created: " + mPackageId + " " + mPluginId+"\nCONTEXT: " + context + " " + mBindContextList.contains(context) + " " + mBindContextList.size());
if(!mBindContextList.contains(context)) {
Intent intent = new Intent();
intent.setClassName(mPackageId, mPluginId);
try {
bound = context.bindService( intent, this, Context.BIND_AUTO_CREATE);
if(bound) {
mBindContextList.add(context);
}
}catch(Throwable t) {}
}
return bound;
}
public void unbindPlugin(Context context) {
if(mBindContextList.contains(context)) {
mBindContextList.remove(context);
context.unbindService(this);
}
}
public boolean isBound(Context context) {
return mBindContextList.contains(context);
}
public String getPluginName() {
return mPluginName;
}
public String getPluginVersion() {
return mPluginVersion;
}
public String getPluginDescription() {
return mPluginDescription;
}
public String getPluginAuthor() {
return mPluginAuthor;
}
public String getPluginLicense() {
return mPluginLicense;
}
public ImageSpan getPluginMarkIcon() {
return mIcon;
}
public boolean hasPreferences() {
return mHasPreferences;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mPlugin = Plugin.Stub.asInterface(service);
doLog(mContext, "Plugin connected: " + name);
if(isActivated()) {
callOnActivation();
}
readPluginMetaData();
}
public void callOnActivation() {
if(isConnected()) {
try {
if(PluginHandler.getPluginManager() != null) {
mPlugin.onActivation(PluginHandler.getPluginManager());
if(!PluginHandler.firstAndLastProgramIdAlreadyHandled()) {
try {
SharedPreferences pref = PrefUtils.getSharedPreferences(PrefUtils.TYPE_PREFERENCES_SHARED_GLOBAL, mContext);
long value = pref.getLong(mContext.getString(R.string.META_DATA_ID_FIRST_KNOWN), mContext.getResources().getInteger(R.integer.meta_data_id_default));
mPlugin.handleFirstKnownProgramId(value);
}catch(Throwable iae) {}
}
}
if(mBindCallback != null) {
mBindCallback.run();
mBindCallback = null;
}
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void loadIcon() {
mIcon = null;
if(isConnected() && isActivated()) {
try {
byte[] iconBytes = mPlugin.getMarkIcon();
if(iconBytes != null) {
Bitmap iconBitmap = UiUtils.createBitmapFromByteArray(iconBytes);
BitmapDrawable icon = new BitmapDrawable(mContext.getResources(),iconBitmap);
float zoom = 16f/iconBitmap.getHeight() * mContext.getResources().getDisplayMetrics().density;
icon.setBounds(0, 0, (int)(iconBitmap.getWidth() * zoom), (int)(iconBitmap.getHeight() * zoom));
if(!SettingConstants.IS_DARK_THEME) {
icon.setColorFilter(new PorterDuffColorFilter(Color.BLACK, PorterDuff.Mode.MULTIPLY));
}
mIcon = new ImageSpan(icon, ImageSpan.ALIGN_BASELINE);
}
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void readPluginMetaData() {
if(isConnected()) {
try {
mPluginName = mPlugin.getName();
mPluginVersion = mPlugin.getVersion();
mPluginDescription = mPlugin.getDescription();
mPluginAuthor = mPlugin.getAuthor();
mPluginLicense = mPlugin.getLicense();
mHasPreferences = mPlugin.hasPreferences();
loadIcon();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private boolean isConnected() {
return mPlugin != null && !mBindContextList.isEmpty();
}
public void callOnDeactivation() {
if(isConnected()) {
try {
mPlugin.onDeactivation();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mPlugin = null;
doLog(mContext, "Plugin disconnected: " + name);
for(Context context : mBindContextList) {
context.unbindService(this);
}
mBindContextList.clear();
PluginHandler.removePluginServiceConnection(this);
}
public boolean isActivated() {
return mPlugin != null && PreferenceManager.getDefaultSharedPreferences(mContext).getBoolean(mPluginId+"_ACTIVATED", true);
}
public Plugin getPlugin() {
return mPlugin;
}
public String getPackageId() {
return mPackageId;
}
public String getId() {
return mPluginId;
}
private String getCompareId() {
return mPackageId+"."+mPluginId;
}
@Override
public int hashCode() {
return Arrays.hashCode(getCompareId().getBytes());
}
@Override
public boolean equals(Object o) {
if(o instanceof PluginServiceConnection) {
return getCompareId().equals(((PluginServiceConnection) o).getCompareId());
}
return super.equals(o);
}
@Override
public int compareTo(PluginServiceConnection another) {
if(mPluginName != null && another.mPluginName != null) {
return mPluginName.compareToIgnoreCase(another.mPluginName);
}
return isConnected() ? -1 : 1;
}
private static void doLog(Context context, String message) {
Logging.log(null, message, Logging.TYPE_PLUGIN, context);
}
}