Last active
August 29, 2015 13:58
-
-
Save robUx4/9934836 to your computer and use it in GitHub Desktop.
Block the Play Store from opening unexpectedly (from dubious ad networks)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.levelup.touiteur; | |
import java.lang.reflect.Field; | |
import java.util.HashMap; | |
import android.annotation.SuppressLint; | |
import android.app.Activity; | |
import android.app.Instrumentation; | |
import android.content.ContentResolver; | |
import android.content.Context; | |
import android.content.Intent; | |
import android.content.IntentFilter; | |
import android.net.Uri; | |
import android.os.Build; | |
import android.os.Bundle; | |
import android.os.PatternMatcher; | |
import android.text.TextUtils; | |
import android.util.Log; | |
import android.view.MotionEvent; | |
import android.view.View; | |
import android.view.View.OnTouchListener; | |
import android.view.ViewGroup; | |
public abstract class ActivityWith_Ads extends Activity { | |
private final static String TAG = "AdSafe"; | |
private final static boolean HAS_ADS = true; | |
/** | |
* Extra field you should put on your external browser Intents to make sure they're not blocked | |
*/ | |
public static final String EXTRA_BYPASS_BLOCK = "com.levelup.touiteur.extra.urlbypass"; | |
private static final IntentFilter badIntentFilters[]; | |
/** | |
* The {@link android.app.Instrumentation Instrumentation} of the process where filters are added | |
*/ | |
private static Instrumentation mInstrumentation; | |
/** | |
* The list of {@link android.app.Instrumentation.ActivityMonitor ActivityMonitor} currently registered to block some intents | |
* for the whole process | |
*/ | |
private static Instrumentation.ActivityMonitor registeredMonitors[]; | |
/** | |
* Flag to indicate an ad was clicked so the resulting Intent launched is OK | |
*/ | |
private static boolean adClicked; | |
static { | |
// intents from the Play Store manifest | |
IntentFilter playStoreAssetBrowser = new IntentFilter(); | |
playStoreAssetBrowser.addAction(Intent.ACTION_MAIN); | |
playStoreAssetBrowser.addAction(Intent.ACTION_SEARCH); | |
playStoreAssetBrowser.addCategory(Intent.CATEGORY_LAUNCHER); | |
playStoreAssetBrowser.addCategory("android.intent.category.APP_MARKET"); // Intent.CATEGORY_APP_MARKET | |
playStoreAssetBrowser.addCategory(Intent.CATEGORY_DEFAULT); | |
IntentFilter playStoreMarket = new IntentFilter(); | |
playStoreMarket.addAction(Intent.ACTION_VIEW); | |
playStoreMarket.addAction("android.nfc.action.NDEF_DISCOVERED"); | |
playStoreMarket.addAction("com.google.android.finsky.NAVIGATIONAL_SUGGESTION"); | |
playStoreMarket.addCategory(Intent.CATEGORY_BROWSABLE); | |
playStoreMarket.addCategory(Intent.CATEGORY_DEFAULT); | |
playStoreMarket.addDataScheme("market"); | |
playStoreMarket.addDataAuthority("search", null); | |
playStoreMarket.addDataPath("/", PatternMatcher.PATTERN_LITERAL); | |
playStoreMarket.addDataScheme("http"); | |
playStoreMarket.addDataAuthority("market.android.com", null); | |
playStoreMarket.addDataPath("/", PatternMatcher.PATTERN_LITERAL); | |
playStoreMarket.addDataScheme("http"); | |
playStoreMarket.addDataAuthority("market.android.com", null); | |
playStoreMarket.addDataPath("/", PatternMatcher.PATTERN_PREFIX); | |
playStoreMarket.addDataScheme("https"); | |
playStoreMarket.addDataAuthority("market.android.com", null); | |
playStoreMarket.addDataPath("/", PatternMatcher.PATTERN_LITERAL); | |
playStoreMarket.addDataScheme("https"); | |
playStoreMarket.addDataAuthority("market.android.com", null); | |
playStoreMarket.addDataPath("/", PatternMatcher.PATTERN_PREFIX); | |
IntentFilter playStoreStore = new IntentFilter(); | |
playStoreStore.addAction(Intent.ACTION_VIEW); | |
playStoreStore.addAction("android.nfc.action.NDEF_DISCOVERED"); | |
playStoreStore.addCategory(Intent.CATEGORY_BROWSABLE); | |
playStoreStore.addCategory(Intent.CATEGORY_DEFAULT); | |
playStoreStore.addDataScheme("http"); | |
playStoreStore.addDataAuthority("play.google.com", null); | |
playStoreStore.addDataPath("/store", PatternMatcher.PATTERN_PREFIX); | |
playStoreStore.addDataScheme("https"); | |
playStoreStore.addDataAuthority("play.google.com", null); | |
playStoreStore.addDataPath("/store", PatternMatcher.PATTERN_PREFIX); | |
playStoreStore.addDataScheme("http"); | |
playStoreStore.addDataAuthority("play.google.com", null); | |
playStoreStore.addDataPath("/redeem", PatternMatcher.PATTERN_PREFIX); | |
playStoreStore.addDataScheme("https"); | |
playStoreStore.addDataAuthority("play.google.com", null); | |
playStoreStore.addDataPath("/redeem", PatternMatcher.PATTERN_PREFIX); | |
IntentFilter playStorePurchase = new IntentFilter(); | |
playStorePurchase.addAction("com.android.vending.billing.PURCHASE"); | |
playStorePurchase.addCategory(Intent.CATEGORY_DEFAULT); | |
badIntentFilters = new IntentFilter[] { playStoreAssetBrowser, playStoreMarket, playStoreStore, playStorePurchase, }; | |
} | |
private static boolean isBadIntent(Context context, Intent intent) { | |
if (null != intent.getComponent() && TextUtils.equals(intent.getComponent().getPackageName(), context.getPackageName())) { | |
// this intent is launching some of our activities | |
if (TextUtils.equals(intent.getComponent().getClassName(), "com.admarvel.android.ads.AdMarvelActivity")) { | |
Intent testIntent = new Intent(Intent.ACTION_VIEW); | |
testIntent.addCategory(Intent.CATEGORY_BROWSABLE); | |
testIntent.setData(Uri.parse(intent.getStringExtra("url"))); | |
intent = testIntent; // fall through to test the target that wasn't clicked | |
} else if (TextUtils.equals(intent.getComponent().getClassName(), "com.admarvel.android.ads.AdMarvelVideoActivity")) { | |
// this Intent was launched without the user clicking in the ad area | |
Log.e(TAG, "AdMarvelVideoActivity launched without click intent:" + intent + " extras:" + intent.getExtras()); | |
} else if (TextUtils.equals(intent.getComponent().getClassName(), "com.google.ads.AdActivity")) { | |
Bundle adData = intent.getBundleExtra("com.google.ads.AdOpener"); | |
if ("intent".equals(adData.get("action"))) { | |
Object params = adData.get("params"); | |
if (params instanceof HashMap) { | |
Intent testIntent = new Intent(Intent.ACTION_VIEW); | |
testIntent.addCategory(Intent.CATEGORY_BROWSABLE); | |
testIntent.setData(Uri.parse(((HashMap<String, String>) params).get("u"))); | |
intent = testIntent; // fall through to test the target that wasn't clicked | |
} | |
} | |
} else if (TextUtils.equals(intent.getComponent().getClassName(), "com.millennialmedia.android.MMActivity")) { | |
// this Intent was launched without the user clicking in the ad area | |
Log.e(TAG, "MMActivity launched without click intent:" + intent + " extras:" + intent.getExtras()); | |
} else if (TextUtils.equals(intent.getComponent().getClassName(), "com.millennialmedia.android.VideoPlayer")) { | |
// this Intent was launched without the user clicking in the ad area | |
Log.e(TAG, "VideoPlayer launched without click intent:" + intent + " extras:" + intent.getExtras()); | |
} else { | |
return false; // all other activities we know about should not be blocked | |
} | |
} | |
ContentResolver resolver = context.getContentResolver(); | |
for (IntentFilter monitor : badIntentFilters) { | |
if (monitor.match(resolver, intent, true, "IntentBlock") >= 0) { | |
Log.d(TAG, "bad intent detected intent:" + intent + " extras:" + intent.getExtras()); | |
return true; | |
} | |
} | |
return false; | |
} | |
public static boolean isIntentLegal(Context context, Intent intent) { | |
if (adClicked || intent.hasExtra(EXTRA_BYPASS_BLOCK)) | |
return true; | |
return !isBadIntent(context, intent); | |
} | |
@Override | |
public void startActivityForResult(Intent intent, int requestCode) { | |
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { | |
// it will be done in the other version otherwise | |
if (isIntentLegal(this, intent)) { | |
unregisterIntentBlockers(); | |
} | |
} | |
super.startActivityForResult(intent, requestCode); | |
} | |
@SuppressLint("NewApi") | |
@Override | |
public void startActivityForResult(Intent intent, int requestCode, Bundle options) { | |
if (isIntentLegal(this, intent)) { | |
unregisterIntentBlockers(); | |
} | |
super.startActivityForResult(intent, requestCode, options); | |
} | |
public static void registerIntentBlockers() { | |
if (null != mInstrumentation && null == registeredMonitors) { | |
Log.v(TAG, "register intent blockers in " + mInstrumentation); | |
registeredMonitors = new Instrumentation.ActivityMonitor[badIntentFilters.length]; | |
for (int i = 0; i < badIntentFilters.length; ++i) { | |
registeredMonitors[i] = new Instrumentation.ActivityMonitor(badIntentFilters[i], null, true); | |
mInstrumentation.addMonitor(registeredMonitors[i]); | |
} | |
} | |
} | |
public static void unregisterIntentBlockers() { | |
if (null != mInstrumentation && null != registeredMonitors) { | |
Log.v(TAG, "unregister intent blockers in " + mInstrumentation); | |
for (int i = 0; i < registeredMonitors.length; ++i) { | |
mInstrumentation.removeMonitor(registeredMonitors[i]); | |
} | |
registeredMonitors = null; | |
} | |
} | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.id.MainLayout); | |
ViewGroup mAdGroup = (ViewGroup) findViewById(R.id.AdItem); | |
if (!HAS_ADS) {// || !InvisiblePreferences.getInstance().getBoolean(InvisiblePreferences.Show_Ads)) { | |
((ViewGroup) mAdGroup.getParent()).removeView(mAdGroup); | |
mAdGroup = null; | |
} else { | |
mAdGroup.setVisibility(View.GONE); | |
View adClick = mAdGroup.findViewById(R.id.adClick); | |
adClick.setOnTouchListener(new OnTouchListener() { | |
@Override | |
public boolean onTouch(View v, MotionEvent event) { | |
// ad touched | |
adClicked = true; | |
return false; | |
} | |
}); | |
} | |
} | |
@Override | |
protected void onStart() { | |
adClicked = false; | |
if (HAS_ADS) { | |
try { | |
Field instrumentationField = Activity.class.getDeclaredField("mInstrumentation"); | |
instrumentationField.setAccessible(true); | |
Instrumentation instrumentation = (Instrumentation) instrumentationField.get(this); | |
Log.v(TAG, this + " Found instrumentation:" + instrumentation); | |
if (null == mInstrumentation) { | |
mInstrumentation = instrumentation; | |
} else if (mInstrumentation != instrumentation) { | |
Log.d(TAG, this + " different instrumentation:" + instrumentation + " was:" + mInstrumentation); | |
unregisterIntentBlockers(); | |
mInstrumentation = instrumentation; | |
} | |
registerIntentBlockers(); | |
if (null == mInstrumentation) { | |
Log.d(TAG, "Could not find instrumentation in " + this); | |
} | |
} catch (NoSuchFieldException e) { | |
Log.d(TAG, "Failed to get the activity instrumentation in " + this, e); | |
} catch (IllegalAccessException e) { | |
Log.d(TAG, "Failed to get the activity instrumentation in " + this, e); | |
} catch (IllegalArgumentException e) { | |
Log.d(TAG, "Failed to get the activity instrumentation in " + this, e); | |
} | |
} | |
super.onStart(); | |
} | |
@Override | |
public void setIntent(Intent newIntent) { | |
if (null == newIntent) { | |
Log.e(TAG, "Intent blockers are not compatible with an Activity that has no intent set"); | |
newIntent = new Intent(); | |
} | |
super.setIntent(newIntent); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Intent launchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); | |
launchIntent.putExtra(ActivityWithAds.EXTRA_BYPASS_BLOCK, true); // make sure we're not blocked | |
launchIntent.addCategory(Intent.CATEGORY_BROWSABLE); | |
if (null!=referrer) | |
launchIntent.putExtra("android.intent.extra.REFERRER", referrer); | |
launchIntent.putExtra(Browser.EXTRA_APPLICATION_ID, activity.getPackageName()); | |
activity.startActivity(launchIntent); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
android:layout_width="fill_parent" | |
android:layout_height="150dip" > | |
<com.admarvel.android.ads.AdMarvelView | |
android:id="@+id/adMarcel" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_gravity="center_horizontal" /> | |
<View | |
android:id="@+id/adClick" | |
android:layout_width="fill_parent" | |
android:layout_height="fill_parent" /> | |
</FrameLayout> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.levelup.touiteur; | |
import android.annotation.SuppressLint; | |
import android.app.Application; | |
import android.content.Intent; | |
import android.os.Bundle; | |
public class MyApp extends Application { | |
@Override | |
public void startActivity(Intent intent) { | |
if (ActivityWithAds.isIntentLegal(this, intent)) { | |
ActivityWithAds.unregisterIntentBlockers(); | |
super.startActivity(intent); | |
} | |
} | |
@SuppressLint("NewApi") | |
@Override | |
public void startActivity(Intent intent, Bundle options) { | |
if (ActivityWithAds.isIntentLegal(this, intent)) { | |
ActivityWithAds.unregisterIntentBlockers(); | |
super.startActivity(intent, options); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment