Bug 1361664 - Part2. Create MVP Triggers and Deeplinks for Mobile Marketing Automatin. r=maliu

This patch do 6 things. They are related so I put them in the same patch.
1. Extract MmaEvent Name
2. If MMA is diabled, don't send event.
3. Add check before sending 'Set Default Borwser' deep link
4. Add user attribute for delay messaging focus install status.
5. If the user pref off at runtime, we ask Leanplum to stop and prevent our app sending event to Leanplum.
6. Fix proguard. Only keep necceary files.

MozReview-Commit-ID: APEmr1JXBLH

--HG--
extra : rebase_source : 35b54c11004905c950c1ace84507554a2e1b4f39
This commit is contained in:
Nevin Chen 2017-05-17 01:25:45 +08:00
Родитель 41b293dfc2
Коммит 0f2aff9431
11 изменённых файлов: 175 добавлений и 19 удалений

Просмотреть файл

@ -106,6 +106,7 @@ def _defines():
if CONFIG['MOZ_ANDROID_MMA']:
DEFINES['MOZ_LEANPLUM_SDK_KEY'] = CONFIG['MOZ_LEANPLUM_SDK_KEY']
DEFINES['MOZ_LEANPLUM_SDK_CLIENTID'] = CONFIG['MOZ_LEANPLUM_SDK_CLIENTID']
DEFINES['MOZ_BUILDID'] = open(os.path.join(buildconfig.topobjdir, 'buildid.h')).readline().split()[2]

Просмотреть файл

@ -20,6 +20,7 @@ import org.mozilla.gecko.home.HomeConfig.PanelType;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.menu.GeckoMenuInflater;
import org.mozilla.gecko.menu.MenuPanel;
import org.mozilla.gecko.mma.MmaDelegate;
import org.mozilla.gecko.notifications.NotificationHelper;
import org.mozilla.gecko.util.IntentUtils;
import org.mozilla.gecko.mozglue.SafeIntent;
@ -121,6 +122,8 @@ import java.util.concurrent.TimeUnit;
import static org.mozilla.gecko.Tabs.INTENT_EXTRA_SESSION_UUID;
import static org.mozilla.gecko.Tabs.INTENT_EXTRA_TAB_ID;
import static org.mozilla.gecko.Tabs.INVALID_TAB_ID;
import static org.mozilla.gecko.mma.MmaDelegate.DOWNLOAD_VIDEOS_OR_ANY_OTHER_MEDIA;
import static org.mozilla.gecko.mma.MmaDelegate.LOADS_ARTICLES;
public abstract class GeckoApp extends GeckoActivity
implements AnchoredPopup.OnVisibilityChangeListener,
@ -870,7 +873,15 @@ public abstract class GeckoApp extends GeckoActivity
final SharedPreferences prefs = getSharedPreferences();
int count = prefs.getInt(PREFS_FLASH_USAGE, 0);
prefs.edit().putInt(PREFS_FLASH_USAGE, ++count).apply();
} else if ("Mma:reader_available".equals(event)) {
MmaDelegate.track(LOADS_ARTICLES);
} else if ("Mma:web_save_media".equals(event) || "Mma:web_save_image".equals(event)) {
MmaDelegate.track(DOWNLOAD_VIDEOS_OR_ANY_OTHER_MEDIA);
}
}
/**
@ -1396,6 +1407,9 @@ public abstract class GeckoApp extends GeckoActivity
"DevToolsAuth:Scan",
"DOMFullScreen:Start",
"DOMFullScreen:Stop",
"Mma:reader_available",
"Mma:web_save_image",
"Mma:web_save_media",
"Permissions:Data",
"PrivateBrowsing:Data",
"RuntimePermissions:Check",
@ -2444,6 +2458,9 @@ public abstract class GeckoApp extends GeckoActivity
"DevToolsAuth:Scan",
"DOMFullScreen:Start",
"DOMFullScreen:Stop",
"Mma:reader_available",
"Mma:web_save_image",
"Mma:web_save_media",
"Permissions:Data",
"PrivateBrowsing:Data",
"RuntimePermissions:Check",

Просмотреть файл

@ -21,9 +21,11 @@ import org.mozilla.gecko.annotation.RobocopTarget;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.distribution.PartnerBrowserCustomizationsClient;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.mma.MmaDelegate;
import org.mozilla.gecko.mozglue.SafeIntent;
import org.mozilla.gecko.notifications.WhatsNewReceiver;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.promotion.AddToHomeScreenPromotion;
import org.mozilla.gecko.reader.ReaderModeUtils;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
@ -49,6 +51,7 @@ import android.text.TextUtils;
import android.util.Log;
import static org.mozilla.gecko.Tab.TabType;
import static org.mozilla.gecko.mma.MmaDelegate.VISITING_A_WEBSITE_WITH_MATCH_TO_PAST_HISTORY;
public class Tabs implements BundleEventListener {
private static final String LOGTAG = "GeckoTabs";
@ -1088,6 +1091,7 @@ public class Tabs implements BundleEventListener {
if (!delayLoad && !background) {
selectTab(tabToSelect.getId());
tracking(url);
}
// Load favicon instantly for about:home page because it's already cached
@ -1095,9 +1099,17 @@ public class Tabs implements BundleEventListener {
tabToSelect.loadFavicon();
}
return tabToSelect;
}
private void tracking(String url) {
AddToHomeScreenPromotion.URLHistory history = AddToHomeScreenPromotion.getHistoryForURL(mAppContext, url);
if (history != null && history.visits > 0) {
MmaDelegate.track(VISITING_A_WEBSITE_WITH_MATCH_TO_PAST_HISTORY, history.visits);
}
}
/**
* Opens a new tab and loads either about:home or, if PREFS_HOMEPAGE_FOR_EVERY_NEW_TAB is set,
* the user's homepage.

Просмотреть файл

@ -11,10 +11,18 @@ import org.mozilla.gecko.TelemetryContract.Event;
import org.mozilla.gecko.TelemetryContract.Method;
import org.mozilla.gecko.TelemetryContract.Reason;
import org.mozilla.gecko.TelemetryContract.Session;
import org.mozilla.gecko.mma.MmaDelegate;
import android.os.SystemClock;
import android.util.Log;
import static org.mozilla.gecko.mma.MmaDelegate.INTERACT_WITH_SEARCH_URL_AREA;
import static org.mozilla.gecko.mma.MmaDelegate.LOAD_BOOKMARK;
import static org.mozilla.gecko.mma.MmaDelegate.SAVE_BOOKMARK;
import static org.mozilla.gecko.mma.MmaDelegate.SAVE_PASSWORD;
import static org.mozilla.gecko.mma.MmaDelegate.WHEN_USER_TAKE_A_SCREENSHOT;
/**
* All telemetry times are relative to one of two clocks:
*
@ -208,6 +216,24 @@ public class Telemetry {
String.class, eventName, String.class, method.toString(),
timestamp, String.class, extras);
}
mappingMmaTracking(eventName, method, extras);
}
private static void mappingMmaTracking(String eventName, Method method, String extras) {
if (eventName == null || method == null || extras == null) {
return;
}
if (eventName.equalsIgnoreCase(Event.SAVE.toString()) && method == Method.MENU && extras.equals("bookmark")) {
MmaDelegate.track(SAVE_BOOKMARK);
} else if (eventName.equalsIgnoreCase(Event.LOAD_URL.toString()) && method == Method.LIST_ITEM && extras.equals("bookmarks")) {
MmaDelegate.track(LOAD_BOOKMARK);
} else if (eventName.equalsIgnoreCase(Event.SHOW.toString()) && method == Method.ACTIONBAR && extras.equals("urlbar-url")) {
MmaDelegate.track(INTERACT_WITH_SEARCH_URL_AREA);
} else if (eventName.equalsIgnoreCase(Event.SHARE.toString()) && method == Method.BUTTON && extras.equals("screenshot")) {
MmaDelegate.track(WHEN_USER_TAKE_A_SCREENSHOT);
} else if (eventName.equalsIgnoreCase(Event.ACTION.toString()) && method == Method.DOORHANGER && extras.equals("login-positive")) {
MmaDelegate.track(SAVE_PASSWORD);
}
}
public static void sendUIEvent(final Event event, final Method method, final long timestamp,

Просмотреть файл

@ -7,14 +7,37 @@
package org.mozilla.gecko.mma;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import org.mozilla.gecko.Experiments;
import org.mozilla.gecko.MmaConstants;
import org.mozilla.gecko.PrefsHelper;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.switchboard.SwitchBoard;
import java.lang.ref.WeakReference;
public class MmaDelegate {
public static final String LOADS_ARTICLES = "Loads articles";
public static final String DOWNLOAD_VIDEOS_OR_ANY_OTHER_MEDIA = "Download videos or any other media";
public static final String CLEAR_PRIVATE_DATA = "Clear Private Data";
public static final String SAVE_BOOKMARK = "SaveBookmark";
public static final String LOAD_BOOKMARK = "LoadBookmark";
public static final String INTERACT_WITH_SEARCH_URL_AREA = "Interact with search url area";
public static final String WHEN_USER_TAKE_A_SCREENSHOT = "When user take a screenshot";
public static final String SAVE_PASSWORD = "SavePassword";
public static final String VISITING_A_WEBSITE_WITH_MATCH_TO_PAST_HISTORY = "Visiting a website with match to past history";
public static final String LAUNCH_BUT_NOT_DEFAULT_BROWSER = "Launch but not default browser";
private static final String TAG = "MmaDelegate";
private static final String KEY_PREF_BOOLEAN_MMA_ENABLED = "mma.enabled";
private static final String[] PREFS = { KEY_PREF_BOOLEAN_MMA_ENABLED };
@ -22,9 +45,10 @@ public class MmaDelegate {
private static boolean isGeckoPrefOn = false;
private static MmaInterface mmaHelper = MmaConstants.getMma();
private static WeakReference<Context> applicationContext;
public static void init(Activity activity) {
applicationContext = new WeakReference<>(activity.getApplicationContext());
setupPrefHandler(activity);
}
@ -40,6 +64,9 @@ public class MmaDelegate {
Log.d(TAG, "prefValue() called with: pref = [" + pref + "], value = [" + value + "]");
if (value) {
mmaHelper.init(activity);
if (!isDefaultBrowser(activity)) {
mmaHelper.track(MmaDelegate.LAUNCH_BUT_NOT_DEFAULT_BROWSER);
}
isGeckoPrefOn = true;
} else {
isGeckoPrefOn = false;
@ -50,15 +77,47 @@ public class MmaDelegate {
PrefsHelper.addObserver(PREFS, handler);
}
public static void track(String event) {
if (isGeckoPrefOn) {
if (isMmaEnabled()) {
mmaHelper.track(event);
}
}
public static void track(String event, long value) {
if (isGeckoPrefOn) {
if (isMmaEnabled()) {
mmaHelper.track(event, value);
}
}
private static boolean isMmaEnabled() {
if (applicationContext == null) {
return false;
}
final Context context = applicationContext.get();
if (context == null) {
return false;
}
final boolean healthReport = GeckoPreferences.getBooleanPref(context, GeckoPreferences.PREFS_HEALTHREPORT_UPLOAD_ENABLED, true);
final boolean inExperiment = SwitchBoard.isInExperiment(context, Experiments.LEANPLUM);
return inExperiment && healthReport && isGeckoPrefOn;
}
private static boolean isDefaultBrowser(Context context) {
final Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.mozilla.org"));
final ResolveInfo info = context.getPackageManager().resolveActivity(viewIntent, PackageManager.MATCH_DEFAULT_ONLY);
if (info == null) {
// No default is set
return false;
}
final String packageName = info.activityInfo.packageName;
return (TextUtils.equals(packageName, context.getPackageName()));
}
}

Просмотреть файл

@ -7,17 +7,14 @@
package org.mozilla.gecko.mma;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageManager;
import com.leanplum.Leanplum;
import com.leanplum.LeanplumActivityHelper;
import com.leanplum.annotations.Parser;
import org.mozilla.gecko.ActivityHandlerHelper;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.MmaConstants;
import org.mozilla.gecko.util.ContextUtils;
import java.util.HashMap;
import java.util.Map;
@ -25,7 +22,10 @@ import java.util.Map;
public class MmaLeanplumImp implements MmaInterface {
@Override
public void init(Activity activity) {
public void init(final Activity activity) {
if (activity == null) {
return;
}
Leanplum.setApplicationContext(activity.getApplicationContext());
LeanplumActivityHelper.enableLifecycleCallbacks(activity.getApplication());
@ -36,18 +36,29 @@ public class MmaLeanplumImp implements MmaInterface {
Leanplum.setAppIdForDevelopmentMode(MmaConstants.MOZ_LEANPLUM_SDK_CLIENTID, MmaConstants.MOZ_LEANPLUM_SDK_KEY);
}
Map<String, Object> attributes = new HashMap<>();
boolean installedFocus = ContextUtils.isPackageInstalled(activity, "org.mozilla.focus");
boolean installedKlar = ContextUtils.isPackageInstalled(activity, "org.mozilla.klar");
boolean installedFocus = isPackageInstalled(activity, "org.mozilla.focus");
boolean installedKlar = isPackageInstalled(activity, "org.mozilla.klar");
if (installedFocus || installedKlar) {
attributes.put("focus", "installed");
}
Leanplum.start(activity, attributes);
Leanplum.track("Launch");
// this is special to Leanplum. Since we defer LeanplumActivityHelper's onResume call till
// switchboard completes loading, we manually call it here.
LeanplumActivityHelper.onResume(activity);
// switchboard completes loading. We miss the call to LeanplumActivityHelper.onResume.
// So I manually call it here.
//
// There's a risk that if this is called after activity's onPause(Although I've
// tested it's seems okay). We should require their SDK to separate activity call back with
// SDK initialization and Activity lifecycle in the future.
//
// I put it under runOnUiThread because in current Leanplum's SDK design, this should be run in main thread.
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
LeanplumActivityHelper.onResume(activity);
}
});
}
@Override
@ -71,4 +82,14 @@ public class MmaLeanplumImp implements MmaInterface {
public void stop() {
Leanplum.stop();
}
private static boolean isPackageInstalled(final Context context, String packageName) {
try {
PackageManager pm = context.getPackageManager();
pm.getPackageInfo(packageName, 0);
return true;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
}

Просмотреть файл

@ -8,6 +8,7 @@ package org.mozilla.gecko.preferences;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.mma.MmaDelegate;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.icons.storage.DiskStorage;
@ -20,6 +21,9 @@ import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import static org.mozilla.gecko.mma.MmaDelegate.CLEAR_PRIVATE_DATA;
class PrivateDataPreference extends MultiPrefMultiChoicePreference {
private static final String LOGTAG = "GeckoPrivateDataPreference";
private static final String PREF_KEY_PREFIX = "private.data.";
@ -58,5 +62,6 @@ class PrivateDataPreference extends MultiPrefMultiChoicePreference {
// clear private data in gecko
EventDispatcher.getInstance().dispatch("Sanitize:ClearData", data);
MmaDelegate.track(CLEAR_PRIVATE_DATA);
}
}

Просмотреть файл

@ -36,7 +36,7 @@ import ch.boye.httpclientandroidlib.util.TextUtils;
* Promote "Add to home screen" if user visits website often.
*/
public class AddToHomeScreenPromotion extends TabsTrayVisibilityAwareDelegate implements Tabs.OnTabsChangedListener {
private static class URLHistory {
public static class URLHistory {
public final long visits;
public final long lastVisit;
@ -213,7 +213,7 @@ public class AddToHomeScreenPromotion extends TabsTrayVisibilityAwareDelegate im
return urlAnnotations.hasAcceptedOrDeclinedHomeScreenShortcut(context.getContentResolver(), url);
}
protected URLHistory getHistoryForURL(Context context, String url) {
public static URLHistory getHistoryForURL(Context context, String url) {
final GeckoProfile profile = GeckoProfile.get(context);
final BrowserDB browserDB = BrowserDB.from(profile);

Просмотреть файл

@ -195,11 +195,18 @@ var Reader = {
if (browser.isArticle) {
showPageAction("drawable://reader", Strings.reader.GetStringFromName("readerView.enter"));
UITelemetry.addEvent("show.1", "button", null, "reader_available");
this._sendMmaEvent("reader_available");
} else {
UITelemetry.addEvent("show.1", "button", null, "reader_unavailable");
}
},
_sendMmaEvent: function(event) {
WindowEventDispatcher.sendRequest({
type: "Mma:"+event,
});
},
_showSystemUI: function(visibility) {
WindowEventDispatcher.sendRequest({
type: "SystemUI:Visibility",

Просмотреть файл

@ -888,11 +888,15 @@ var BrowserApp = {
}
});
NativeWindow.contextmenus.add(stringGetter("contextmenu.saveImage"),
NativeWindow.contextmenus.imageSaveableContext,
function(aTarget) {
UITelemetry.addEvent("action.1", "contextmenu", null, "web_save_image");
UITelemetry.addEvent("save.1", "contextmenu", null, "image");
WindowEventDispatcher.sendRequest({
type: "Mma:web_save_image",
});
RuntimePermissions.waitForPermissions(RuntimePermissions.WRITE_EXTERNAL_STORAGE).then(function(permissionGranted) {
if (!permissionGranted) {
@ -938,6 +942,9 @@ var BrowserApp = {
function(aTarget) {
UITelemetry.addEvent("action.1", "contextmenu", null, "web_save_media");
UITelemetry.addEvent("save.1", "contextmenu", null, "media");
WindowEventDispatcher.sendRequest({
type: "Mma:web_save_media",
});
let url = aTarget.currentSrc || aTarget.src;

Просмотреть файл

@ -336,9 +336,10 @@
-keep class com.leanplum.messagetemplates.BaseMessageOptions { *; }
-dontwarn android.support.v7.**
-keep class android.support.v7.** { *; }
-keep interface android.support.v7.** { *; }
#-dontwarn android.support.v7.**
-keep class android.support.v7.app.AppCompatActivity
-keep class android.support.v7.app.ActionBarActivity
#-keep interface android.support.v7.** { *; }
-printmapping out.map
-renamesourcefileattribute SourceFile