| OLD | NEW | 
|---|
| 1 /* | 1 /* | 
| 2  * This file is part of Adblock Plus <http://adblockplus.org/>, | 2  * This file is part of Adblock Plus <http://adblockplus.org/>, | 
| 3  * Copyright (C) 2006-2014 Eyeo GmbH | 3  * Copyright (C) 2006-2014 Eyeo GmbH | 
| 4  * | 4  * | 
| 5  * Adblock Plus is free software: you can redistribute it and/or modify | 5  * Adblock Plus is free software: you can redistribute it and/or modify | 
| 6  * it under the terms of the GNU General Public License version 3 as | 6  * it under the terms of the GNU General Public License version 3 as | 
| 7  * published by the Free Software Foundation. | 7  * published by the Free Software Foundation. | 
| 8  * | 8  * | 
| 9  * Adblock Plus is distributed in the hope that it will be useful, | 9  * Adblock Plus is distributed in the hope that it will be useful, | 
| 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| 12  * GNU General Public License for more details. | 12  * GNU General Public License for more details. | 
| 13  * | 13  * | 
| 14  * You should have received a copy of the GNU General Public License | 14  * You should have received a copy of the GNU General Public License | 
| 15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
| 16  */ | 16  */ | 
| 17 | 17 | 
| 18 package org.adblockplus.android; | 18 package org.adblockplus.android; | 
| 19 | 19 | 
| 20 import org.adblockplus.android.updater.UpdaterActivity; | 20 import java.util.List; | 
| 21 | 21 | 
| 22 import android.app.Notification; | 22 import org.adblockplus.libadblockplus.AppInfo; | 
| 23 import android.app.NotificationManager; | 23 import org.adblockplus.libadblockplus.EventCallback; | 
| 24 import android.app.PendingIntent; | 24 import org.adblockplus.libadblockplus.Filter; | 
|  | 25 import org.adblockplus.libadblockplus.FilterChangeCallback; | 
|  | 26 import org.adblockplus.libadblockplus.FilterEngine; | 
|  | 27 import org.adblockplus.libadblockplus.JsEngine; | 
|  | 28 import org.adblockplus.libadblockplus.LogSystem; | 
|  | 29 import org.adblockplus.libadblockplus.Subscription; | 
|  | 30 import org.adblockplus.libadblockplus.UpdaterCallback; | 
|  | 31 import org.adblockplus.libadblockplus.WebRequest; | 
|  | 32 | 
| 25 import android.content.Context; | 33 import android.content.Context; | 
| 26 import android.content.Intent; |  | 
| 27 import android.content.pm.PackageInfo; | 34 import android.content.pm.PackageInfo; | 
| 28 import android.content.pm.PackageManager.NameNotFoundException; | 35 import android.content.pm.PackageManager.NameNotFoundException; | 
| 29 import android.os.Build.VERSION; | 36 import android.os.Build.VERSION; | 
| 30 import android.support.v4.app.NotificationCompat; |  | 
| 31 import android.util.Log; | 37 import android.util.Log; | 
| 32 | 38 | 
| 33 public class ABPEngine | 39 public final class ABPEngine | 
| 34 { | 40 { | 
| 35   private final static String TAG = "ABPEngine"; | 41   private static final String TAG = Utils.getTag(ABPEngine.class); | 
| 36   private static final int NOTIFICATION_ID = R.string.app_name + 1; | 42 | 
| 37 | 43   private final Context context; | 
| 38   private Context context; | 44 | 
| 39 | 45   /* | 
| 40   public ABPEngine(Context context, String basePath) | 46    * The fields below are volatile because: | 
|  | 47    * | 
|  | 48    * I encountered JNI related bugs/crashes caused by JNI backed Java objects. I
     t seemed that under | 
|  | 49    * certain conditions the objects were optimized away which resulted in crashe
     s when trying to | 
|  | 50    * release the object, sometimes even on access. | 
|  | 51    * | 
|  | 52    * The only solution that really worked was to declare the variables holding t
     he references | 
|  | 53    * volatile, this seems to prevent the JNI from 'optimizing away' those object
     s (as a volatile | 
|  | 54    * variable might be changed at any time from any thread). | 
|  | 55    */ | 
|  | 56   private volatile JsEngine jsEngine; | 
|  | 57   private volatile FilterEngine filterEngine; | 
|  | 58   private volatile LogSystem logSystem; | 
|  | 59   private volatile WebRequest webRequest; | 
|  | 60   private volatile EventCallback updateCallback; | 
|  | 61   private volatile UpdaterCallback updaterCallback; | 
|  | 62   private volatile FilterChangeCallback filterChangeCallback; | 
|  | 63 | 
|  | 64   private ABPEngine(final Context context) | 
| 41   { | 65   { | 
| 42     this.context = context; | 66     this.context = context; | 
| 43     String version; | 67   } | 
|  | 68 | 
|  | 69   public static AppInfo generateAppInfo(final Context context) | 
|  | 70   { | 
|  | 71     String version = "0"; | 
| 44     try | 72     try | 
| 45     { | 73     { | 
| 46       final PackageInfo info = context.getPackageManager().getPackageInfo(contex
     t.getPackageName(), 0); | 74       final PackageInfo info = context.getPackageManager().getPackageInfo(contex
     t.getPackageName(), 0); | 
| 47       version = info.versionName + "." + info.versionCode; | 75       version = info.versionName + "." + info.versionCode; | 
| 48     } catch (NameNotFoundException e) | 76     } | 
|  | 77     catch (final NameNotFoundException e) | 
| 49     { | 78     { | 
| 50       Log.e(TAG, "Failed to get the application version number", e); | 79       Log.e(TAG, "Failed to get the application version number", e); | 
| 51       version = "0"; |  | 
| 52     } | 80     } | 
| 53     final String sdkVersion = String.valueOf(VERSION.SDK_INT); | 81     final String sdkVersion = String.valueOf(VERSION.SDK_INT); | 
| 54     final String locale = context.getResources().getConfiguration().locale.toStr
     ing(); | 82     final String locale = context.getResources().getConfiguration().locale.toStr
     ing(); | 
| 55     final boolean developmentBuild = !context.getResources().getBoolean(R.bool.d
     ef_release); | 83     final boolean developmentBuild = !context.getResources().getBoolean(R.bool.d
     ef_release); | 
| 56     initialize(basePath, version, sdkVersion, locale, developmentBuild); | 84 | 
| 57   } | 85     return AppInfo.builder() | 
| 58 | 86         .setVersion(version) | 
| 59   public void onFilterChanged(String url, String status, long time) | 87         .setApplicationVersion(sdkVersion) | 
| 60   { | 88         .setLocale(locale) | 
| 61     context.sendBroadcast(new Intent(AdblockPlus.BROADCAST_SUBSCRIPTION_STATUS).
     putExtra("url", url).putExtra("status", status).putExtra("time", time)); | 89         .setDevelopmentBuild(developmentBuild) | 
| 62   } | 90         .build(); | 
| 63 | 91   } | 
| 64   /** | 92 | 
| 65    * Called when update event occurred. | 93   public static ABPEngine create(final Context context, final AppInfo appInfo, f
     inal String basePath) | 
| 66    * @param url Update download address | 94   { | 
| 67    */ | 95     final ABPEngine engine = new ABPEngine(context); | 
| 68   public void onUpdateEvent(String url, String error) | 96 | 
| 69   { | 97     engine.jsEngine = new JsEngine(appInfo); | 
| 70     Notification notification = getNotification(url, error); | 98     engine.jsEngine.setDefaultFileSystem(basePath); | 
| 71     NotificationManager notificationManager = (NotificationManager) context.getS
     ystemService(Context.NOTIFICATION_SERVICE); | 99 | 
| 72     notificationManager.notify(NOTIFICATION_ID, notification); | 100     engine.logSystem = new AndroidLogSystem(); | 
| 73   } | 101     engine.jsEngine.setLogSystem(engine.logSystem); | 
| 74 | 102 | 
| 75   private native void initialize(String basePath, String version, String sdkVers
     ion, String locale, boolean developmentBuild); | 103     engine.webRequest = new AndroidWebRequest(); | 
| 76 | 104     engine.jsEngine.setWebRequest(engine.webRequest); | 
| 77   public native void release(); | 105 | 
| 78 | 106     engine.updateCallback = new AndroidUpdateAvailableCallback(context); | 
| 79   public native boolean isFirstRun(); | 107     engine.jsEngine.setEventCallback("updateAvailable", engine.updateCallback); | 
| 80 | 108 | 
| 81   public native Subscription[] getListedSubscriptions(); | 109     engine.filterEngine = new FilterEngine(engine.jsEngine); | 
| 82 | 110     engine.filterChangeCallback = new AndroidFilterChangeCallback(context); | 
| 83   public native Subscription[] getRecommendedSubscriptions(); | 111     engine.filterEngine.setFilterChangeCallback(engine.filterChangeCallback); | 
| 84 | 112 | 
| 85   public native void addSubscription(String url); | 113     engine.updaterCallback = new AndroidUpdaterCallback(context); | 
| 86 | 114 | 
| 87   public native void removeSubscription(String url); | 115     return engine; | 
| 88 | 116   } | 
| 89   public native void refreshSubscription(String url); | 117 | 
| 90 | 118   public void dispose() | 
| 91   public native void actualizeSubscriptionStatus(String url); | 119   { | 
| 92 | 120     // Safe disposing (just in case) | 
| 93   public native void setAcceptableAdsEnabled(boolean enabled); | 121     if (this.filterEngine != null) | 
| 94 | 122     { | 
| 95   public native String getDocumentationLink(); | 123       this.filterEngine.dispose(); | 
| 96 | 124       this.filterEngine = null; | 
| 97   public native boolean matches(String url, String contentType, String[] documen
     tUrls); | 125     } | 
| 98 | 126 | 
| 99   public native String[] getSelectorsForDomain(String domain); | 127     if (this.jsEngine != null) | 
| 100 | 128     { | 
| 101   public native void checkUpdates(); | 129       this.jsEngine.dispose(); | 
| 102 | 130       this.jsEngine = null; | 
| 103   private Notification getNotification(String url, String error) | 131     } | 
| 104   { | 132 | 
| 105     final PendingIntent emptyIntent = PendingIntent.getActivity(context, 0, new 
     Intent(), 0); | 133     if (this.logSystem != null) | 
| 106 | 134     { | 
| 107     NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
     ; | 135       this.logSystem.dispose(); | 
| 108     builder.setContentTitle(context.getText(R.string.app_name)); | 136       this.logSystem = null; | 
| 109     builder.setSmallIcon(R.drawable.ic_stat_warning); | 137     } | 
| 110     builder.setWhen(System.currentTimeMillis()); | 138 | 
| 111     builder.setAutoCancel(true); | 139     if (this.webRequest != null) | 
| 112     builder.setOnlyAlertOnce(true); | 140     { | 
| 113     builder.setContentIntent(emptyIntent); | 141       this.webRequest.dispose(); | 
| 114 | 142       this.webRequest = null; | 
| 115     if (url != null) | 143     } | 
| 116     { | 144 | 
| 117       builder.setSmallIcon(R.drawable.ic_stat_download); | 145     if (this.updateCallback != null) | 
| 118 | 146     { | 
| 119 | 147       this.updateCallback.dispose(); | 
| 120       Intent intent = new Intent(context, UpdaterActivity.class).addFlags(Intent
     .FLAG_ACTIVITY_NEW_TASK); | 148       this.updateCallback = null; | 
| 121       intent.setAction("download"); | 149     } | 
| 122       intent.putExtra("url", url); | 150 | 
| 123       PendingIntent updateIntent = PendingIntent.getActivity(context, 0, intent,
      PendingIntent.FLAG_UPDATE_CURRENT); | 151     if (this.updaterCallback != null) | 
| 124       builder.setContentIntent(updateIntent); | 152     { | 
| 125       builder.setContentText(context.getString(R.string.msg_update_available)); | 153       this.updaterCallback.dispose(); | 
| 126     } | 154       this.updaterCallback = null; | 
| 127     else if (error != null) | 155     } | 
| 128     { | 156 | 
| 129       //TODO Should we show error message to the user? | 157     if (this.filterChangeCallback != null) | 
| 130       builder.setContentText(context.getString(R.string.msg_update_fail)); | 158     { | 
| 131     } | 159       this.filterChangeCallback.dispose(); | 
| 132     else | 160       this.filterChangeCallback = null; | 
| 133     { | 161     } | 
| 134       builder.setContentText(context.getString(R.string.msg_update_missing)); | 162   } | 
| 135     } | 163 | 
| 136 | 164   public boolean isFirstRun() | 
| 137     Notification notification = builder.getNotification(); | 165   { | 
| 138     return notification; | 166     return this.filterEngine.isFirstRun(); | 
| 139   } | 167   } | 
| 140 | 168 | 
| 141   static | 169   private static org.adblockplus.android.Subscription convertJsSubscription(fina
     l Subscription jsSubscription) | 
| 142   { | 170   { | 
| 143     System.loadLibrary("adblockplus-jni"); | 171     final org.adblockplus.android.Subscription subscription = new org.adblockplu
     s.android.Subscription(); | 
|  | 172 | 
|  | 173     subscription.title = jsSubscription.getProperty("title").toString(); | 
|  | 174     subscription.url = jsSubscription.getProperty("url").toString(); | 
|  | 175 | 
|  | 176     return subscription; | 
|  | 177   } | 
|  | 178 | 
|  | 179   private static org.adblockplus.android.Subscription[] convertJsSubscriptions(f
     inal List<Subscription> jsSubscriptions) | 
|  | 180   { | 
|  | 181     final org.adblockplus.android.Subscription[] subscriptions = new org.adblock
     plus.android.Subscription[jsSubscriptions.size()]; | 
|  | 182 | 
|  | 183     for (int i = 0; i < subscriptions.length; i++) | 
|  | 184     { | 
|  | 185       subscriptions[i] = convertJsSubscription(jsSubscriptions.get(i)); | 
|  | 186     } | 
|  | 187 | 
|  | 188     return subscriptions; | 
|  | 189   } | 
|  | 190 | 
|  | 191   public org.adblockplus.android.Subscription[] getRecommendedSubscriptions() | 
|  | 192   { | 
|  | 193     return convertJsSubscriptions(this.filterEngine.fetchAvailableSubscriptions(
     )); | 
|  | 194   } | 
|  | 195 | 
|  | 196   public org.adblockplus.android.Subscription[] getListedSubscriptions() | 
|  | 197   { | 
|  | 198     return convertJsSubscriptions(this.filterEngine.getListedSubscriptions()); | 
|  | 199   } | 
|  | 200 | 
|  | 201   public void setSubscription(final String url) | 
|  | 202   { | 
|  | 203     Subscription sub = null; | 
|  | 204     for (final Subscription s : this.filterEngine.getListedSubscriptions()) | 
|  | 205     { | 
|  | 206       if (url.equals(s.getProperty("url").toString())) | 
|  | 207       { | 
|  | 208         sub = s; | 
|  | 209       } | 
|  | 210       s.removeFromList(); | 
|  | 211     } | 
|  | 212     if (sub != null) | 
|  | 213     { | 
|  | 214       sub.addToList(); | 
|  | 215     } | 
|  | 216   } | 
|  | 217 | 
|  | 218   public void refreshSubscriptions() | 
|  | 219   { | 
|  | 220     for (final Subscription s : this.filterEngine.getListedSubscriptions()) | 
|  | 221     { | 
|  | 222       s.updateFilters(); | 
|  | 223     } | 
|  | 224   } | 
|  | 225 | 
|  | 226   public void setAcceptableAdsEnabled(final boolean enabled) | 
|  | 227   { | 
|  | 228     final String url = this.filterEngine.getPref("subscriptions_exceptionsurl").
     toString(); | 
|  | 229     final Subscription sub = this.filterEngine.getSubscription(url); | 
|  | 230     if (sub != null) | 
|  | 231     { | 
|  | 232       if (enabled) | 
|  | 233       { | 
|  | 234         sub.addToList(); | 
|  | 235       } | 
|  | 236       else | 
|  | 237       { | 
|  | 238         sub.removeFromList(); | 
|  | 239       } | 
|  | 240     } | 
|  | 241   } | 
|  | 242 | 
|  | 243   public String getDocumentationLink() | 
|  | 244   { | 
|  | 245     return this.filterEngine.getPref("documentation_link").toString(); | 
|  | 246   } | 
|  | 247 | 
|  | 248   public boolean matches(final String fullUrl, final String contentType, final S
     tring[] referrerChainArray) | 
|  | 249   { | 
|  | 250     final Filter filter = this.filterEngine.matches(fullUrl, contentType, referr
     erChainArray); | 
|  | 251 | 
|  | 252     if (filter == null) | 
|  | 253     { | 
|  | 254       return false; | 
|  | 255     } | 
|  | 256 | 
|  | 257     // hack: if there is no referrer, block only if filter is domain-specific | 
|  | 258     // (to re-enable in-app ads blocking, proposed on 12.11.2012 Monday meeting) | 
|  | 259     // (documentUrls contains the referrers on Android) | 
|  | 260     if (referrerChainArray.length == 0 && (filter.getProperty("text").toString()
     ).contains("||")) | 
|  | 261     { | 
|  | 262       return false; | 
|  | 263     } | 
|  | 264 | 
|  | 265     return filter.getType() != Filter.Type.EXCEPTION; | 
|  | 266   } | 
|  | 267 | 
|  | 268   public void checkForUpdates() | 
|  | 269   { | 
|  | 270     this.filterEngine.forceUpdateCheck(this.updaterCallback); | 
|  | 271   } | 
|  | 272 | 
|  | 273   public void updateSubscriptionStatus(final String url) | 
|  | 274   { | 
|  | 275     final Subscription sub = this.filterEngine.getSubscription(url); | 
|  | 276     if (sub != null) | 
|  | 277     { | 
|  | 278       Utils.updateSubscriptionStatus(this.context, sub); | 
|  | 279     } | 
| 144   } | 280   } | 
| 145 } | 281 } | 
| OLD | NEW | 
|---|