| Index: src/org/adblockplus/android/ABPEngine.java |
| diff --git a/src/org/adblockplus/android/ABPEngine.java b/src/org/adblockplus/android/ABPEngine.java |
| index 14d52152b36093130ee073bfdf469097497a16fa..faf1b4e09520bfd6eeabcfd7c49ddf8328691a7d 100644 |
| --- a/src/org/adblockplus/android/ABPEngine.java |
| +++ b/src/org/adblockplus/android/ABPEngine.java |
| @@ -17,129 +17,265 @@ |
| package org.adblockplus.android; |
| -import org.adblockplus.android.updater.UpdaterActivity; |
| +import java.util.List; |
| + |
| +import org.adblockplus.libadblockplus.AppInfo; |
| +import org.adblockplus.libadblockplus.EventCallback; |
| +import org.adblockplus.libadblockplus.Filter; |
| +import org.adblockplus.libadblockplus.FilterChangeCallback; |
| +import org.adblockplus.libadblockplus.FilterEngine; |
| +import org.adblockplus.libadblockplus.JsEngine; |
| +import org.adblockplus.libadblockplus.LogSystem; |
| +import org.adblockplus.libadblockplus.Subscription; |
| +import org.adblockplus.libadblockplus.UpdaterCallback; |
| +import org.adblockplus.libadblockplus.WebRequest; |
| -import android.app.Notification; |
| -import android.app.NotificationManager; |
| -import android.app.PendingIntent; |
| import android.content.Context; |
| -import android.content.Intent; |
| import android.content.pm.PackageInfo; |
| import android.content.pm.PackageManager.NameNotFoundException; |
| import android.os.Build.VERSION; |
| -import android.support.v4.app.NotificationCompat; |
| import android.util.Log; |
| -public class ABPEngine |
| +public final class ABPEngine |
| { |
| - private final static String TAG = "ABPEngine"; |
| - private static final int NOTIFICATION_ID = R.string.app_name + 1; |
| + private static final String TAG = Utils.getTag(ABPEngine.class); |
| - private Context context; |
| + private final Context context; |
| - public ABPEngine(Context context, String basePath) |
| + /** |
|
Felix Dahlke
2014/04/28 10:09:41
If this is a javadoc comment, it'll show up as the
René Jeschke
2014/04/28 10:18:34
Done.
|
| + * The fields below are volatile because: |
| + * |
| + * I encountered JNI related bugs/crashes caused by JNI backed Java objects. It seemed that under |
| + * certain conditions the objects were optimized away which resulted in crashes when trying to |
| + * release the object, sometimes even on access. |
| + * |
| + * The only solution that really worked was to declare the variables holding the references |
| + * volatile, this seems to prevent the JNI from 'optimizing away' those objects (as a volatile |
| + * variable might be changed at any time from any thread). |
| + */ |
| + private volatile JsEngine jsEngine; |
| + private volatile FilterEngine filterEngine; |
| + private volatile LogSystem logSystem; |
| + private volatile WebRequest webRequest; |
| + private volatile EventCallback updateCallback; |
| + private volatile UpdaterCallback updaterCallback; |
| + private volatile FilterChangeCallback filterChangeCallback; |
| + |
| + private ABPEngine(final Context context) |
| { |
| this.context = context; |
| - String version; |
| + } |
| + |
| + public static AppInfo generateAppInfo(final Context context) |
| + { |
| + String version = "0"; |
| try |
| { |
| final PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); |
| version = info.versionName + "." + info.versionCode; |
| - } catch (NameNotFoundException e) |
| + } |
| + catch (final NameNotFoundException e) |
| { |
| Log.e(TAG, "Failed to get the application version number", e); |
| - version = "0"; |
| } |
| final String sdkVersion = String.valueOf(VERSION.SDK_INT); |
| final String locale = context.getResources().getConfiguration().locale.toString(); |
| final boolean developmentBuild = !context.getResources().getBoolean(R.bool.def_release); |
| - initialize(basePath, version, sdkVersion, locale, developmentBuild); |
| - } |
| - public void onFilterChanged(String url, String status, long time) |
| - { |
| - context.sendBroadcast(new Intent(AdblockPlus.BROADCAST_SUBSCRIPTION_STATUS).putExtra("url", url).putExtra("status", status).putExtra("time", time)); |
| + return AppInfo.builder() |
| + .setVersion(version) |
| + .setApplicationVersion(sdkVersion) |
| + .setLocale(locale) |
| + .setDevelopmentBuild(developmentBuild) |
| + .build(); |
| } |
| - /** |
| - * Called when update event occurred. |
| - * @param url Update download address |
| - */ |
| - public void onUpdateEvent(String url, String error) |
| + public static ABPEngine create(final Context context, final AppInfo appInfo, final String basePath) |
| { |
| - Notification notification = getNotification(url, error); |
| - NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); |
| - notificationManager.notify(NOTIFICATION_ID, notification); |
| + final ABPEngine engine = new ABPEngine(context); |
| + |
| + engine.jsEngine = new JsEngine(appInfo); |
| + engine.jsEngine.setDefaultFileSystem(basePath); |
| + |
| + engine.logSystem = new AndroidLogSystem(); |
| + engine.jsEngine.setLogSystem(engine.logSystem); |
| + |
| + engine.webRequest = new AndroidWebRequest(); |
| + engine.jsEngine.setWebRequest(engine.webRequest); |
| + |
| + engine.updateCallback = new AndroidUpdateAvailableCallback(context); |
| + engine.jsEngine.setEventCallback("updateAvailable", engine.updateCallback); |
| + |
| + engine.filterEngine = new FilterEngine(engine.jsEngine); |
| + engine.filterChangeCallback = new AndroidFilterChangeCallback(context); |
| + engine.filterEngine.setFilterChangeCallback(engine.filterChangeCallback); |
| + |
| + engine.updaterCallback = new AndroidUpdaterCallback(context); |
| + |
| + return engine; |
| } |
| - private native void initialize(String basePath, String version, String sdkVersion, String locale, boolean developmentBuild); |
| + public void dispose() |
| + { |
| + // Safe disposing (just in case) |
| + if (this.filterEngine != null) |
| + { |
| + this.filterEngine.dispose(); |
| + this.filterEngine = null; |
| + } |
| - public native void release(); |
| + if (this.jsEngine != null) |
| + { |
| + this.jsEngine.dispose(); |
| + this.jsEngine = null; |
| + } |
| - public native boolean isFirstRun(); |
| + if (this.logSystem != null) |
| + { |
| + this.logSystem.dispose(); |
| + this.logSystem = null; |
| + } |
| - public native Subscription[] getListedSubscriptions(); |
| + if (this.webRequest != null) |
| + { |
| + this.webRequest.dispose(); |
| + this.webRequest = null; |
| + } |
| - public native Subscription[] getRecommendedSubscriptions(); |
| + if (this.updateCallback != null) |
| + { |
| + this.updateCallback.dispose(); |
| + this.updateCallback = null; |
| + } |
| - public native void addSubscription(String url); |
| + if (this.updaterCallback != null) |
| + { |
| + this.updaterCallback.dispose(); |
| + this.updaterCallback = null; |
| + } |
| - public native void removeSubscription(String url); |
| + if (this.filterChangeCallback != null) |
| + { |
| + this.filterChangeCallback.dispose(); |
| + this.filterChangeCallback = null; |
| + } |
| + } |
| - public native void refreshSubscription(String url); |
| + public boolean isFirstRun() |
| + { |
| + return this.filterEngine.isFirstRun(); |
| + } |
| - public native void actualizeSubscriptionStatus(String url); |
| + private static org.adblockplus.android.Subscription convertJsSubscription(final Subscription jsSubscription) |
| + { |
| + final org.adblockplus.android.Subscription subscription = new org.adblockplus.android.Subscription(); |
| - public native void setAcceptableAdsEnabled(boolean enabled); |
| + subscription.title = jsSubscription.getProperty("title").toString(); |
| + subscription.url = jsSubscription.getProperty("url").toString(); |
| - public native String getDocumentationLink(); |
| + return subscription; |
| + } |
| - public native boolean matches(String url, String contentType, String[] documentUrls); |
| + private static org.adblockplus.android.Subscription[] convertJsSubscriptions(final List<Subscription> jsSubscriptions) |
| + { |
| + final org.adblockplus.android.Subscription[] subscriptions = new org.adblockplus.android.Subscription[jsSubscriptions.size()]; |
| - public native String[] getSelectorsForDomain(String domain); |
| + for (int i = 0; i < subscriptions.length; i++) |
| + { |
| + subscriptions[i] = convertJsSubscription(jsSubscriptions.get(i)); |
| + } |
| - public native void checkUpdates(); |
| + return subscriptions; |
| + } |
| - private Notification getNotification(String url, String error) |
| + public org.adblockplus.android.Subscription[] getRecommendedSubscriptions() |
| { |
| - final PendingIntent emptyIntent = PendingIntent.getActivity(context, 0, new Intent(), 0); |
| + return convertJsSubscriptions(this.filterEngine.fetchAvailableSubscriptions()); |
| + } |
| - NotificationCompat.Builder builder = new NotificationCompat.Builder(context); |
| - builder.setContentTitle(context.getText(R.string.app_name)); |
| - builder.setSmallIcon(R.drawable.ic_stat_warning); |
| - builder.setWhen(System.currentTimeMillis()); |
| - builder.setAutoCancel(true); |
| - builder.setOnlyAlertOnce(true); |
| - builder.setContentIntent(emptyIntent); |
| + public org.adblockplus.android.Subscription[] getListedSubscriptions() |
| + { |
| + return convertJsSubscriptions(this.filterEngine.getListedSubscriptions()); |
| + } |
| - if (url != null) |
| + public void setSubscription(final String url) |
| + { |
| + Subscription sub = null; |
| + for (final Subscription s : this.filterEngine.getListedSubscriptions()) |
| + { |
| + if (url.equals(s.getProperty("url").toString())) |
| + { |
| + sub = s; |
| + } |
| + s.removeFromList(); |
| + } |
| + if (sub != null) |
| { |
| - builder.setSmallIcon(R.drawable.ic_stat_download); |
| + sub.addToList(); |
| + } |
| + } |
| + public void refreshSubscriptions() |
| + { |
| + for (final Subscription s : this.filterEngine.getListedSubscriptions()) |
| + { |
| + s.updateFilters(); |
| + } |
| + } |
| - Intent intent = new Intent(context, UpdaterActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| - intent.setAction("download"); |
| - intent.putExtra("url", url); |
| - PendingIntent updateIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); |
| - builder.setContentIntent(updateIntent); |
| - builder.setContentText(context.getString(R.string.msg_update_available)); |
| + public void setAcceptableAdsEnabled(final boolean enabled) |
| + { |
| + final String url = this.filterEngine.getPref("subscriptions_exceptionsurl").toString(); |
| + final Subscription sub = this.filterEngine.getSubscription(url); |
| + if (sub != null) |
| + { |
| + if (enabled) |
| + { |
| + sub.addToList(); |
| + } |
| + else |
| + { |
| + sub.removeFromList(); |
| + } |
| } |
| - else if (error != null) |
| + } |
| + |
| + public String getDocumentationLink() |
| + { |
| + return this.filterEngine.getPref("documentation_link").toString(); |
| + } |
| + |
| + public boolean matches(final String fullUrl, final String contentType, final String[] referrerChainArray) |
| + { |
| + final Filter filter = this.filterEngine.matches(fullUrl, contentType, referrerChainArray); |
| + |
| + if (filter == null) |
| { |
| - //TODO Should we show error message to the user? |
| - builder.setContentText(context.getString(R.string.msg_update_fail)); |
| + return false; |
| } |
| - else |
| + |
| + // hack: if there is no referrer, block only if filter is domain-specific |
| + // (to re-enable in-app ads blocking, proposed on 12.11.2012 Monday meeting) |
| + // (documentUrls contains the referrers on Android) |
| + if (referrerChainArray.length == 0 && (filter.getProperty("text").toString()).contains("||")) |
| { |
| - builder.setContentText(context.getString(R.string.msg_update_missing)); |
| + return false; |
| } |
| - Notification notification = builder.getNotification(); |
| - return notification; |
| + return filter.getType() != Filter.Type.EXCEPTION; |
| + } |
| + |
| + public void checkForUpdates() |
| + { |
| + this.filterEngine.forceUpdateCheck(this.updaterCallback); |
| } |
| - static |
| + public void updateSubscriptionStatus(final String url) |
| { |
| - System.loadLibrary("adblockplus-jni"); |
| + final Subscription sub = this.filterEngine.getSubscription(url); |
| + if (sub != null) |
| + { |
| + Utils.updateSubscriptionStatus(this.context, sub); |
| + } |
| } |
| } |