Bug 1421174 - Remove old AddToHomeScreenPromotion; r=jchen

Deprecated by offering full support for Progressive Web Applications.

Differential Revision: https://phabricator.services.mozilla.com/D7696

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Petru Lingurar 2018-10-08 05:42:14 +00:00
Родитель 4c95ba0b3d
Коммит 53dceac9f9
9 изменённых файлов: 0 добавлений и 614 удалений

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

@ -53,24 +53,6 @@
"max": "100"
}
},
{
"name": "promote-add-to-homescreen",
"buckets": {
"max": "100",
"min": "50"
},
"last_modified": 1467705654772,
"values": {
"lastVisitMaximumAgeMs": 600000,
"minimumTotalVisits": 5,
"lastVisitMinimumAgeMs": 30000
},
"id": "20d278d7-0d35-4811-8f01-bf24e31ba51b",
"match": {
"appId": "^org.mozilla.fennec|^org.mozilla.firefox_beta$"
},
"schema": 1467705310595
},
{
"name": "offline-cache",
"buckets": {

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

@ -211,10 +211,6 @@
android:launchMode="singleTop"
android:theme="@style/OverlayActivity" />
<activity android:name="org.mozilla.gecko.promotion.HomeScreenPrompt"
android:launchMode="singleTop"
android:theme="@style/OverlayActivity" />
<!-- The main reason for the Tab Queue build flag is to not mess with the VIEW intent filter
before the rest of the plumbing is in place -->

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

@ -118,7 +118,6 @@ import org.mozilla.gecko.overlays.ui.ShareDialog;
import org.mozilla.gecko.permissions.Permissions;
import org.mozilla.gecko.preferences.ClearOnShutdownPref;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.promotion.AddToHomeScreenPromotion;
import org.mozilla.gecko.promotion.ReaderViewBookmarkPromotion;
import org.mozilla.gecko.prompts.Prompt;
import org.mozilla.gecko.reader.ReaderModeUtils;
@ -318,7 +317,6 @@ public class BrowserApp extends GeckoApp
private final TelemetryCorePingDelegate mTelemetryCorePingDelegate = new TelemetryCorePingDelegate();
private final List<BrowserAppDelegate> delegates = Collections.unmodifiableList(Arrays.asList(
new AddToHomeScreenPromotion(),
new ScreenshotDelegate(),
new BookmarkStateChangeDelegate(),
new ReaderViewBookmarkPromotion(),

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

@ -28,9 +28,6 @@ public class Experiments {
// Synchronizing the catalog of downloadable content from Kinto
public static final String DOWNLOAD_CONTENT_CATALOG_SYNC = "download-content-catalog-sync";
// Promotion for "Add to homescreen"
public static final String PROMOTE_ADD_TO_HOMESCREEN = "promote-add-to-homescreen";
// Promotion to bookmark reader-view items after entering reader view three times (Bug 1247689)
public static final String TRIPLE_READERVIEW_BOOKMARK_PROMPT = "triple-readerview-bookmark-prompt";

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

@ -1,243 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.promotion;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.CallSuper;
import android.support.annotation.Nullable;
import android.util.Log;
import org.mozilla.gecko.switchboard.SwitchBoard;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.AboutPages;
import org.mozilla.gecko.BrowserApp;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.UrlAnnotations;
import org.mozilla.gecko.delegates.TabsTrayVisibilityAwareDelegate;
import org.mozilla.gecko.Experiments;
import org.mozilla.gecko.util.ThreadUtils;
import java.lang.ref.WeakReference;
import ch.boye.httpclientandroidlib.util.TextUtils;
/**
* Promote "Add to home screen" if user visits website often.
*/
public class AddToHomeScreenPromotion extends TabsTrayVisibilityAwareDelegate implements Tabs.OnTabsChangedListener {
public static class URLHistory {
public final long visits;
public final long lastVisit;
private URLHistory(long visits, long lastVisit) {
this.visits = visits;
this.lastVisit = lastVisit;
}
}
private static final String LOGTAG = "GeckoPromoteShortcut";
private static final String EXPERIMENT_MINIMUM_TOTAL_VISITS = "minimumTotalVisits";
private static final String EXPERIMENT_LAST_VISIT_MINIMUM_AGE = "lastVisitMinimumAgeMs";
private static final String EXPERIMENT_LAST_VISIT_MAXIMUM_AGE = "lastVisitMaximumAgeMs";
private WeakReference<Activity> activityReference;
private boolean isEnabled;
private int minimumVisits;
private int lastVisitMinimumAgeMs;
private int lastVisitMaximumAgeMs;
@CallSuper
@Override
public void onCreate(BrowserApp browserApp, Bundle savedInstanceState) {
super.onCreate(browserApp, savedInstanceState);
activityReference = new WeakReference<Activity>(browserApp);
initializeExperiment(browserApp);
}
@Override
public void onResume(BrowserApp browserApp) {
Tabs.registerOnTabsChangedListener(this);
}
@Override
public void onPause(BrowserApp browserApp) {
Tabs.unregisterOnTabsChangedListener(this);
}
private void initializeExperiment(Context context) {
if (!SwitchBoard.isInExperiment(context, Experiments.PROMOTE_ADD_TO_HOMESCREEN)) {
Log.v(LOGTAG, "Experiment not enabled");
// Experiment is not enabled. No need to try to read values.
return;
}
JSONObject values = SwitchBoard.getExperimentValuesFromJson(context, Experiments.PROMOTE_ADD_TO_HOMESCREEN);
if (values == null) {
// We didn't get any values for this experiment. Let's disable it instead of picking default
// values that might be bad.
return;
}
try {
initializeWithValues(
values.getInt(EXPERIMENT_MINIMUM_TOTAL_VISITS),
values.getInt(EXPERIMENT_LAST_VISIT_MINIMUM_AGE),
values.getInt(EXPERIMENT_LAST_VISIT_MAXIMUM_AGE));
} catch (JSONException e) {
Log.w(LOGTAG, "Could not read experiment values", e);
}
}
private void initializeWithValues(int minimumVisits, int lastVisitMinimumAgeMs, int lastVisitMaximumAgeMs) {
this.isEnabled = true;
this.minimumVisits = minimumVisits;
this.lastVisitMinimumAgeMs = lastVisitMinimumAgeMs;
this.lastVisitMaximumAgeMs = lastVisitMaximumAgeMs;
}
@Override
public void onTabChanged(final Tab tab, Tabs.TabEvents msg, String data) {
if (tab == null) {
return;
}
if (!Tabs.getInstance().isSelectedTab(tab)) {
// We only ever want to show this promotion for the current tab.
return;
}
if (Tabs.TabEvents.LOADED != msg) {
return;
}
if (tab.isPrivate()) {
// Never show the prompt for private browsing tabs.
return;
}
if (isTabsTrayVisible()) {
// We only want to show this prompt if this tab is in the foreground and not on top
// of the tabs tray.
return;
}
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
maybeShowPromotionForUrl(tab.getURL(), tab.getTitle());
}
});
}
private void maybeShowPromotionForUrl(String url, String title) {
if (!isEnabled) {
return;
}
final Context context = activityReference.get();
if (context == null) {
return;
}
if (!shouldShowPromotion(context, url, title)) {
return;
}
HomeScreenPrompt.show(context, url, title);
}
private boolean shouldShowPromotion(Context context, String url, String title) {
if (TextUtils.isEmpty(url) || TextUtils.isEmpty(title)) {
// We require an URL and a title for the shortcut.
return false;
}
if (AboutPages.isAboutPage(url)) {
// No promotion for our internal sites.
return false;
}
if (!url.startsWith("https://")) {
// Only promote websites that are served over HTTPS.
return false;
}
URLHistory history = getHistoryForURL(context, url);
if (history == null) {
// There's no history for this URL yet or we can't read it right now. Just ignore.
return false;
}
if (history.visits < minimumVisits) {
// This URL has not been visited often enough.
return false;
}
if (history.lastVisit > System.currentTimeMillis() - lastVisitMinimumAgeMs) {
// The last visit is too new. Do not show promotion. This is mostly to avoid that the
// promotion shows up for a quick refreshs and in the worst case the last visit could
// be the current visit (race).
return false;
}
if (history.lastVisit < System.currentTimeMillis() - lastVisitMaximumAgeMs) {
// The last visit is to old. Do not show promotion.
return false;
}
if (hasAcceptedOrDeclinedHomeScreenShortcut(context, url)) {
// The user has already created a shortcut in the past or actively declined to create one.
// Let's not ask again for this url - We do not want to be annoying.
return false;
}
return true;
}
protected boolean hasAcceptedOrDeclinedHomeScreenShortcut(Context context, String url) {
final UrlAnnotations urlAnnotations = BrowserDB.from(context).getUrlAnnotations();
return urlAnnotations.hasAcceptedOrDeclinedHomeScreenShortcut(context.getContentResolver(), url);
}
@Nullable
public static URLHistory getHistoryForURL(Context context, String url) {
final GeckoProfile profile = GeckoProfile.get(context);
final BrowserDB browserDB = BrowserDB.from(profile);
Cursor cursor = null;
try {
cursor = browserDB.getHistoryForURL(context.getContentResolver(), url);
if (cursor == null) {
return null;
}
if (cursor.moveToFirst()) {
return new URLHistory(
cursor.getInt(cursor.getColumnIndex(BrowserContract.History.VISITS)),
cursor.getLong(cursor.getColumnIndex(BrowserContract.History.DATE_LAST_VISITED)));
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return null;
}
}

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

@ -1,235 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.promotion;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.mozilla.gecko.GeckoApplication;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.Locales;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.UrlAnnotations;
import org.mozilla.gecko.icons.IconCallback;
import org.mozilla.gecko.icons.IconResponse;
import org.mozilla.gecko.icons.Icons;
import org.mozilla.gecko.Experiments;
import org.mozilla.gecko.util.ActivityUtils;
import org.mozilla.gecko.util.ThreadUtils;
/**
* Prompt to promote adding the current website to the home screen.
*/
public class HomeScreenPrompt extends Locales.LocaleAwareActivity implements IconCallback {
private static final String EXTRA_TITLE = "title";
private static final String EXTRA_URL = "url";
private static final String TELEMETRY_EXTRA = "home_screen_promotion";
private View containerView;
private ImageView iconView;
private String title;
private String url;
private boolean isAnimating;
private boolean hasAccepted;
private boolean hasDeclined;
public static void show(Context context, String url, String title) {
Intent intent = new Intent(context, HomeScreenPrompt.class);
intent.putExtra(EXTRA_TITLE, title);
intent.putExtra(EXTRA_URL, url);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fetchDataFromIntent();
setupViews();
loadShortcutIcon();
slideIn();
Telemetry.startUISession(TelemetryContract.Session.EXPERIMENT, Experiments.PROMOTE_ADD_TO_HOMESCREEN);
// Technically this isn't triggered by a "service". But it's also triggered by a background task and without
// actual user interaction.
Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.SERVICE, TELEMETRY_EXTRA);
}
@Override
protected void onDestroy() {
super.onDestroy();
Telemetry.stopUISession(TelemetryContract.Session.EXPERIMENT, Experiments.PROMOTE_ADD_TO_HOMESCREEN);
}
private void fetchDataFromIntent() {
final Bundle extras = getIntent().getExtras();
title = extras.getString(EXTRA_TITLE);
url = extras.getString(EXTRA_URL);
}
private void setupViews() {
setContentView(R.layout.homescreen_prompt);
((TextView) findViewById(R.id.title)).setText(title);
Uri uri = Uri.parse(url);
((TextView) findViewById(R.id.host)).setText(uri.getHost());
containerView = findViewById(R.id.container);
iconView = (ImageView) findViewById(R.id.icon);
findViewById(R.id.add).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
hasAccepted = true;
addToHomeScreen();
}
});
findViewById(R.id.close).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onDecline();
}
});
}
private void addToHomeScreen() {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
GeckoApplication.createBrowserShortcut(title, url);
Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, TELEMETRY_EXTRA);
ActivityUtils.goToHomeScreen(HomeScreenPrompt.this);
finish();
}
});
}
private void loadShortcutIcon() {
Icons.with(this)
.pageUrl(url)
.skipNetwork()
.skipMemory()
.forLauncherIcon()
.build()
.execute(this);
}
private void slideIn() {
containerView.setTranslationY(500);
containerView.setAlpha(0);
final Animator translateAnimator = ObjectAnimator.ofFloat(containerView, "translationY", 0);
translateAnimator.setDuration(400);
final Animator alphaAnimator = ObjectAnimator.ofFloat(containerView, "alpha", 1);
alphaAnimator.setStartDelay(200);
alphaAnimator.setDuration(600);
final AnimatorSet set = new AnimatorSet();
set.playTogether(alphaAnimator, translateAnimator);
set.setStartDelay(400);
set.start();
}
/**
* Remember that the user rejected creating a home screen shortcut for this URL.
*/
private void rememberRejection() {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
final UrlAnnotations urlAnnotations = BrowserDB.from(HomeScreenPrompt.this).getUrlAnnotations();
urlAnnotations.insertHomeScreenShortcut(getContentResolver(), url, false);
}
});
}
private void slideOut() {
if (isAnimating) {
return;
}
isAnimating = true;
ObjectAnimator animator = ObjectAnimator.ofFloat(containerView, "translationY", containerView.getHeight());
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
finish();
}
});
animator.start();
}
@Override
public void finish() {
super.finish();
// Don't perform an activity-dismiss animation.
overridePendingTransition(0, 0);
}
@Override
public void onBackPressed() {
onDecline();
}
private void onDecline() {
if (hasDeclined || hasAccepted) {
return;
}
rememberRejection();
slideOut();
// Technically not always an action triggered by the "back" button but with the same effect: Finishing this
// activity and going back to the previous one.
Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.BACK, TELEMETRY_EXTRA);
hasDeclined = true;
}
/**
* User clicked outside of the prompt.
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
onDecline();
return true;
}
@Override
public void onIconResponse(IconResponse response) {
iconView.setImageBitmap(response.getBitmap());
}
}

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

@ -835,8 +835,6 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY whatsnew_notification_title "&brandShortName; is up to date">
<!ENTITY whatsnew_notification_summary "Find out what\'s new in this version">
<!ENTITY promotion_add_page_shortcut "Add page shortcut">
<!ENTITY helper_first_offline_bookmark_title "Read offline">
<!ENTITY helper_first_offline_bookmark_message "Find your Reader View items in Bookmarks, even offline.">
<!ENTITY helper_first_offline_bookmark_button "Go to Bookmarks">

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

@ -608,8 +608,6 @@
<!-- https://support.mozilla.org/1/mobile/%VERSION%/%OS%/%LOCALE%/new-android -->
<string name="whatsnew_notification_url">https://support.mozilla.org/1/mobile/&formatS1;/&formatS2;/&formatS3;/new-android</string>
<string name="promotion_add_page_shortcut">&promotion_add_page_shortcut;</string>
<string name="helper_first_offline_bookmark_title">&helper_first_offline_bookmark_title;</string>
<string name="helper_first_offline_bookmark_message">&helper_first_offline_bookmark_message;</string>
<string name="helper_first_offline_bookmark_button">&helper_first_offline_bookmark_button;</string>

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

@ -1,105 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false">
<RelativeLayout
android:id="@+id/container"
android:layout_width="@dimen/overlay_prompt_container_width"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:background="@android:color/white"
android:clickable="true"
android:orientation="vertical">
<ImageView
android:id="@+id/close"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_marginRight="30dp"
android:layout_marginEnd="30dp"
android:layout_marginTop="30dp"
android:ellipsize="end"
android:maxLines="2"
android:padding="6dp"
android:src="@drawable/tab_close_active" />
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:layout_marginTop="30dp"
android:layout_toLeftOf="@id/close"
android:layout_toStartOf="@id/close"
android:fontFamily="sans-serif-light"
android:textColor="@color/text_and_tabs_tray_grey"
android:textSize="20sp"
tools:text="The Pokedex" />
<TextView
android:id="@+id/host"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_marginBottom="20dp"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:layout_marginRight="30dp"
android:layout_marginEnd="30dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/placeholder_grey"
android:textSize="16sp"
tools:text="pokedex.org" />
<ImageView
android:id="@+id/icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_below="@id/host"
android:layout_marginBottom="20dp"
android:layout_marginLeft="30dp"
android:layout_marginStart="30dp"
android:src="@drawable/icon" />
<Button
android:id="@+id/add"
style="@style/Widget.BaseButton"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_below="@id/host"
android:layout_marginBottom="20dp"
android:layout_marginLeft="100dp"
android:layout_marginStart="100dp"
android:layout_marginRight="30dp"
android:layout_marginEnd="30dp"
android:background="@drawable/button_background_action_photon_round"
android:paddingLeft="16dp"
android:paddingStart="16dp"
android:paddingRight="16dp"
android:paddingEnd="16dp"
android:text="@string/promotion_add_page_shortcut"
android:maxLines="2"
android:ellipsize="end"
android:textColor="@android:color/white"
android:textSize="16sp" />
</RelativeLayout>
</merge>