зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1386192 - Test Leanplum Custom Message for Onboarding; r=cnevinchen+582291
Created LeanPlumVariables to allow LeanPlum overwriting the values used for populating the OnBoarding screens. By simply adding the @Variable annotation to it's fields, on the first run of the app, they will appear in "LeanPlum dashboard - Variables" and will allow overwriting for future runs. The OnBoarding process will now try to use LeanPlum values if possible. Because connecting to LeanPlum and downloading the Variables might take a few seconds we use a delay of up to 3 seconds until starting to show the Onboarding screens. The default values will still be used if: - if the LP experiment is not available - if no internet connection - if more than 3 seconds have passed and LP didn't finish it's download Added two new events that could be tracked to Leanplum MmaDelegate.ONBOARDING_DEFAULT_VALUES and MmaDelegate.ONBOARDING_REMOTE_VALUES to inform if showing the Onboarding with server values was possible or not. Because of the 3 seconds delay until showing the Onboarding panels leaking the could be possible. Used WeakReferences for both the Activity in OnboardingHelper and the OnboardingHelper in MmaLeanplumImp to avoid it. MozReview-Commit-ID: H30e9Ng7jrM --HG-- extra : rebase_source : e403b8010005aa82f8b6440586c533ce99952f9f
This commit is contained in:
Родитель
99a1868412
Коммит
b9eed730ee
|
@ -145,6 +145,7 @@ android {
|
|||
exclude 'org/mozilla/gecko/mma/MmaStubImp.java'
|
||||
} else {
|
||||
exclude 'org/mozilla/gecko/mma/MmaLeanplumImp.java'
|
||||
exclude 'org/mozilla/gecko/mma/LeanplumVariables.java'
|
||||
}
|
||||
|
||||
if (!mozconfig.substs.MOZ_ANDROID_GCM) {
|
||||
|
|
|
@ -41,7 +41,6 @@ import android.os.StrictMode;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
|
@ -94,7 +93,7 @@ import org.mozilla.gecko.distribution.Distribution;
|
|||
import org.mozilla.gecko.distribution.DistributionStoreCallback;
|
||||
import org.mozilla.gecko.dlc.DownloadContentService;
|
||||
import org.mozilla.gecko.extensions.ExtensionPermissionsHelper;
|
||||
import org.mozilla.gecko.firstrun.FirstrunAnimationContainer;
|
||||
import org.mozilla.gecko.firstrun.OnboardingHelper;
|
||||
import org.mozilla.gecko.gfx.DynamicToolbarAnimator;
|
||||
import org.mozilla.gecko.gfx.DynamicToolbarAnimator.PinReason;
|
||||
import org.mozilla.gecko.home.BrowserSearch;
|
||||
|
@ -162,6 +161,7 @@ import org.mozilla.gecko.util.GeckoBundle;
|
|||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
import org.mozilla.gecko.util.IntentUtils;
|
||||
import org.mozilla.gecko.util.MenuUtils;
|
||||
import org.mozilla.gecko.util.NetworkUtils;
|
||||
import org.mozilla.gecko.util.PrefUtils;
|
||||
import org.mozilla.gecko.util.ShortcutUtils;
|
||||
import org.mozilla.gecko.util.StringUtils;
|
||||
|
@ -186,7 +186,6 @@ import java.util.EnumSet;
|
|||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.mozilla.gecko.mma.MmaDelegate.NEW_TAB;
|
||||
|
@ -204,7 +203,8 @@ public class BrowserApp extends GeckoApp
|
|||
OnUrlOpenInBackgroundListener,
|
||||
PropertyAnimator.PropertyAnimationListener,
|
||||
TabsPanel.TabsLayoutChangeListener,
|
||||
View.OnKeyListener {
|
||||
View.OnKeyListener,
|
||||
OnboardingHelper.OnboardingListener {
|
||||
private static final String LOGTAG = "GeckoBrowserApp";
|
||||
|
||||
private static final int TABS_ANIMATION_DURATION = 450;
|
||||
|
@ -233,15 +233,8 @@ public class BrowserApp extends GeckoApp
|
|||
|
||||
public static final String ACTION_VIEW_MULTIPLE = AppConstants.ANDROID_PACKAGE_NAME + ".action.VIEW_MULTIPLE";
|
||||
|
||||
@RobocopTarget
|
||||
public static final String EXTRA_SKIP_STARTPANE = "skipstartpane";
|
||||
private static final String EOL_NOTIFIED = "eol_notified";
|
||||
|
||||
/**
|
||||
* Be aware of {@link org.mozilla.gecko.fxa.EnvironmentUtils.GECKO_PREFS_FIRSTRUN_UUID}.
|
||||
*/
|
||||
private static final String FIRSTRUN_UUID = "firstrun_uuid";
|
||||
|
||||
private BrowserSearch mBrowserSearch;
|
||||
private View mBrowserSearchContainer;
|
||||
|
||||
|
@ -254,7 +247,6 @@ public class BrowserApp extends GeckoApp
|
|||
// We can't name the TabStrip class because it's not included on API 9.
|
||||
private TabStripInterface mTabStrip;
|
||||
private AnimatedProgressBar mProgressView;
|
||||
private FirstrunAnimationContainer mFirstrunAnimationContainer;
|
||||
private HomeScreen mHomeScreen;
|
||||
private TabsPanel mTabsPanel;
|
||||
|
||||
|
@ -433,6 +425,7 @@ public class BrowserApp extends GeckoApp
|
|||
|
||||
@NonNull
|
||||
private SearchEngineManager mSearchEngineManager; // Contains reference to Context - DO NOT LEAK!
|
||||
private OnboardingHelper mOnboardingHelper; // Contains reference to Context - DO NOT LEAK!
|
||||
|
||||
private boolean mHasResumed;
|
||||
|
||||
|
@ -749,6 +742,7 @@ public class BrowserApp extends GeckoApp
|
|||
return;
|
||||
}
|
||||
|
||||
mOnboardingHelper = new OnboardingHelper(this, safeStartingIntent);
|
||||
initSwitchboardAndMma(this, safeStartingIntent, isInAutomation);
|
||||
initTelemetryUploader(isInAutomation);
|
||||
|
||||
|
@ -1014,14 +1008,16 @@ public class BrowserApp extends GeckoApp
|
|||
|
||||
final String serverExtra = intent.getStringExtra(INTENT_KEY_SWITCHBOARD_SERVER);
|
||||
final String serverUrl = TextUtils.isEmpty(serverExtra) ? SWITCHBOARD_SERVER : serverExtra;
|
||||
new AsyncConfigLoader(context, serverUrl) {
|
||||
final SwitchBoard.ConfigStatusListener configStatuslistener = mOnboardingHelper;
|
||||
final MmaDelegate.MmaVariablesChangedListener variablesChangedListener = mOnboardingHelper;
|
||||
new AsyncConfigLoader(context, serverUrl, configStatuslistener) {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
super.doInBackground(params);
|
||||
SwitchBoard.loadConfig(context, serverUrl);
|
||||
SwitchBoard.loadConfig(context, serverUrl, configStatuslistener);
|
||||
if (GeckoPreferences.isMmaAvailableAndEnabled(context)) {
|
||||
// Do LeanPlum start/init here
|
||||
MmaDelegate.init(BrowserApp.this);
|
||||
MmaDelegate.init(BrowserApp.this, variablesChangedListener);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -1089,106 +1085,6 @@ public class BrowserApp extends GeckoApp
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Code to actually show the first run pager, separated
|
||||
* for distribution purposes.
|
||||
*/
|
||||
@UiThread
|
||||
private void checkFirstrunInternal() {
|
||||
showFirstrunPager();
|
||||
|
||||
if (HardwareUtils.isTablet()) {
|
||||
mTabStrip.setOnTabChangedListener(new TabStripInterface.OnTabAddedOrRemovedListener() {
|
||||
@Override
|
||||
public void onTabChanged() {
|
||||
hideFirstrunPager(TelemetryContract.Method.BUTTON);
|
||||
mTabStrip.setOnTabChangedListener(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and show the firstrun pane if the browser has never been launched and
|
||||
* is not opening an external link from another application.
|
||||
*
|
||||
* @param context Context of application; used to show firstrun pane if appropriate
|
||||
* @param intent Intent that launched this activity
|
||||
*/
|
||||
private void checkFirstrun(Context context, SafeIntent intent) {
|
||||
if (getProfile().inGuestMode()) {
|
||||
// We do not want to show any first run tour for guest profiles.
|
||||
return;
|
||||
}
|
||||
|
||||
if (intent.getBooleanExtra(EXTRA_SKIP_STARTPANE, false)) {
|
||||
// Note that we don't set the pref, so subsequent launches can result
|
||||
// in the firstrun pane being shown.
|
||||
return;
|
||||
}
|
||||
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
|
||||
|
||||
try {
|
||||
final SharedPreferences prefs = GeckoSharedPrefs.forProfile(this);
|
||||
|
||||
if (prefs.getBoolean(FirstrunAnimationContainer.PREF_FIRSTRUN_ENABLED_OLD, true) &&
|
||||
prefs.getBoolean(FirstrunAnimationContainer.PREF_FIRSTRUN_ENABLED, true)) {
|
||||
showSplashScreen = false;
|
||||
if (!Intent.ACTION_VIEW.equals(intent.getAction())) {
|
||||
// Check to see if a distribution has turned off the first run pager.
|
||||
final Distribution distribution = Distribution.getInstance(BrowserApp.this);
|
||||
if (!distribution.shouldWaitForSystemDistribution()) {
|
||||
checkFirstrunInternal();
|
||||
} else {
|
||||
distribution.addOnDistributionReadyCallback(new Distribution.ReadyCallback() {
|
||||
@Override
|
||||
public void distributionNotFound() {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
checkFirstrunInternal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void distributionFound(final Distribution distribution) {
|
||||
// Check preference again in case distribution turned it off.
|
||||
if (prefs.getBoolean(FirstrunAnimationContainer.PREF_FIRSTRUN_ENABLED, true)) {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
checkFirstrunInternal();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void distributionArrivedLate(final Distribution distribution) {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
prefs.edit()
|
||||
// Don't bother trying again to show the v1 minimal first run.
|
||||
.putBoolean(FirstrunAnimationContainer.PREF_FIRSTRUN_ENABLED, false)
|
||||
// Generate a unique identify for the current first run.
|
||||
// See Bug 1429735 for why we care to do this.
|
||||
.putString(FIRSTRUN_UUID, UUID.randomUUID().toString())
|
||||
.apply();
|
||||
|
||||
// We have no intention of stopping this session. The FIRSTRUN session
|
||||
// ends when the browsing session/activity has ended. All events
|
||||
// during firstrun will be tagged as FIRSTRUN.
|
||||
Telemetry.startUISession(TelemetryContract.Session.FIRSTRUN);
|
||||
}
|
||||
} finally {
|
||||
StrictMode.setThreadPolicy(savedPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> getMediaPlayerManager() {
|
||||
if (AppConstants.MOZ_MEDIA_PLAYER) {
|
||||
try {
|
||||
|
@ -1246,7 +1142,7 @@ public class BrowserApp extends GeckoApp
|
|||
|
||||
if (!IntentUtils.getIsInAutomationFromEnvironment(intent)) {
|
||||
// We can't show the first run experience until Gecko has finished initialization (bug 1077583).
|
||||
checkFirstrun(this, intent);
|
||||
mOnboardingHelper.checkFirstRun();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2660,9 +2556,10 @@ public class BrowserApp extends GeckoApp
|
|||
&& mHomeScreenContainer != null && mHomeScreenContainer.getVisibility() == View.VISIBLE);
|
||||
}
|
||||
|
||||
private boolean isFirstrunVisible() {
|
||||
return (mFirstrunAnimationContainer != null && mFirstrunAnimationContainer.isVisible()
|
||||
&& mHomeScreenContainer != null && mHomeScreenContainer.getVisibility() == View.VISIBLE);
|
||||
private SplashScreen getSplashScreen() {
|
||||
final ViewGroup main = (ViewGroup) findViewById(R.id.gecko_layout);
|
||||
final View splashLayout = LayoutInflater.from(this).inflate(R.layout.splash_screen, main);
|
||||
return (SplashScreen) splashLayout.findViewById(R.id.splash_root);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2915,8 +2812,11 @@ public class BrowserApp extends GeckoApp
|
|||
// URL, but the reverse doesn't apply: manually switching panels doesn't update the URL.)
|
||||
// Hence we need to restore the panel, in addition to panel state, here.
|
||||
if (isAboutHome(tab)) {
|
||||
// For some reason(e.g. from SearchWidget) we are showing the splash schreen. We should hide it now.
|
||||
if (splashScreen != null && splashScreen.getVisibility() == View.VISIBLE) {
|
||||
// For some reason(e.g. from SearchWidget) we are showing the splash schreen.
|
||||
// If we are not waiting for the onboarding screens we should hide it now.
|
||||
if (!mOnboardingHelper.isPreparing() &&
|
||||
splashScreen != null &&
|
||||
splashScreen.getVisibility() == View.VISIBLE) {
|
||||
// Below line will be run when LOCATION_CHANGE. Which means the page load is almost completed.
|
||||
splashScreen.hide();
|
||||
}
|
||||
|
@ -2955,10 +2855,9 @@ public class BrowserApp extends GeckoApp
|
|||
// But if GeckoThread.isRunning, the will be 0 sec for web rendering.
|
||||
// In that case, we don't want to show the SlashScreen/
|
||||
if (showSplashScreen && !GeckoThread.isRunning()) {
|
||||
|
||||
final ViewGroup main = (ViewGroup) findViewById(R.id.gecko_layout);
|
||||
final View splashLayout = LayoutInflater.from(this).inflate(R.layout.splash_screen, main);
|
||||
splashScreen = (SplashScreen) splashLayout.findViewById(R.id.splash_root);
|
||||
if (splashScreen == null) {
|
||||
splashScreen = getSplashScreen();
|
||||
}
|
||||
|
||||
showSplashScreen = false;
|
||||
} else if (splashScreen != null) {
|
||||
|
@ -3023,26 +2922,6 @@ public class BrowserApp extends GeckoApp
|
|||
}
|
||||
}
|
||||
|
||||
private void showFirstrunPager() {
|
||||
|
||||
if (mFirstrunAnimationContainer == null) {
|
||||
final ViewStub firstrunPagerStub = (ViewStub) findViewById(R.id.firstrun_pager_stub);
|
||||
mFirstrunAnimationContainer = (FirstrunAnimationContainer) firstrunPagerStub.inflate();
|
||||
mFirstrunAnimationContainer.load(getApplicationContext(), getSupportFragmentManager());
|
||||
mFirstrunAnimationContainer.registerOnFinishListener(new FirstrunAnimationContainer.OnFinishListener() {
|
||||
@Override
|
||||
public void onFinish() {
|
||||
if (mFirstrunAnimationContainer.showBrowserHint() &&
|
||||
!Tabs.hasHomepage(BrowserApp.this)) {
|
||||
enterEditingMode();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mHomeScreenContainer.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void showHomePager(String panelId, Bundle panelRestoreData) {
|
||||
showHomePagerWithAnimator(panelId, panelRestoreData, null);
|
||||
}
|
||||
|
@ -3174,15 +3053,12 @@ public class BrowserApp extends GeckoApp
|
|||
* @return boolean of whether pager was visible
|
||||
*/
|
||||
private boolean hideFirstrunPager(TelemetryContract.Method method) {
|
||||
if (!isFirstrunVisible()) {
|
||||
if (!mOnboardingHelper.hideOnboarding()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, method, "firstrun-pane");
|
||||
|
||||
// Don't show any onFinish actions when hiding from this Activity.
|
||||
mFirstrunAnimationContainer.registerOnFinishListener(null);
|
||||
mFirstrunAnimationContainer.hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4634,4 +4510,35 @@ public class BrowserApp extends GeckoApp
|
|||
final boolean isPrivate = mBrowserToolbar.isPrivateMode();
|
||||
WindowUtil.setStatusBarColor(BrowserApp.this, isPrivate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOnboardingProcessStarted() {
|
||||
if (splashScreen == null) {
|
||||
splashScreen = getSplashScreen();
|
||||
}
|
||||
|
||||
splashScreen.show(OnboardingHelper.DELAY_SHOW_DEFAULT_ONBOARDING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOnboardingScreensVisible() {
|
||||
mHomeScreenContainer.setVisibility(View.VISIBLE);
|
||||
|
||||
if (HardwareUtils.isTablet()) {
|
||||
mTabStrip.setOnTabChangedListener(new BrowserApp.TabStripInterface.OnTabAddedOrRemovedListener() {
|
||||
@Override
|
||||
public void onTabChanged() {
|
||||
hideFirstrunPager(TelemetryContract.Method.BUTTON);
|
||||
mTabStrip.setOnTabChangedListener(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishedOnboarding(final boolean showBrowserHint) {
|
||||
if (showBrowserHint && !Tabs.hasHomepage(this)) {
|
||||
enterEditingMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ public abstract class GeckoApp extends GeckoActivity
|
|||
|
||||
* Originally, this was only used for the telemetry core ping logic. To avoid
|
||||
* having to write custom migration logic, we just keep the original pref key.
|
||||
* Be aware of {@link org.mozilla.gecko.fxa.EnvironmentUtils.GECKO_PREFS_IS_FIRST_RUN}.
|
||||
* Be aware of {@link org.mozilla.gecko.fxa.EnvironmentUtils#GECKO_PREFS_IS_FIRST_RUN}.
|
||||
*/
|
||||
public static final String PREFS_IS_FIRST_RUN = "telemetry-isFirstRun";
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
/* -*- 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.firstrun;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
public interface FirstRunPanelConfigProviderStrategy {
|
||||
PanelConfig getPanelConfig(@NonNull Context context, PanelConfig.TYPE panelConfigType, final boolean useLocalValues);
|
||||
}
|
|
@ -15,9 +15,6 @@ import android.animation.Animator;
|
|||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Telemetry;
|
||||
import org.mozilla.gecko.TelemetryContract;
|
||||
import org.mozilla.gecko.Experiments;
|
||||
import org.mozilla.gecko.mma.MmaDelegate;
|
||||
import org.mozilla.gecko.preferences.GeckoPreferences;
|
||||
|
||||
|
@ -31,8 +28,8 @@ public class FirstrunAnimationContainer extends LinearLayout {
|
|||
// After 57, the pref name will be changed. Thus all user since 57 will check this new pref.
|
||||
public static final String PREF_FIRSTRUN_ENABLED = GeckoPreferences.NON_PREF_PREFIX + "startpane_enabled_after_57";
|
||||
|
||||
public static interface OnFinishListener {
|
||||
public void onFinish();
|
||||
public interface OnFinishListener {
|
||||
void onFinish();
|
||||
}
|
||||
|
||||
private FirstrunPager pager;
|
||||
|
@ -46,15 +43,21 @@ public class FirstrunAnimationContainer extends LinearLayout {
|
|||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void load(Context appContext, FragmentManager fm) {
|
||||
public void load(Context appContext, FragmentManager fm, final boolean useLocalValues) {
|
||||
visible = true;
|
||||
pager = (FirstrunPager) findViewById(R.id.firstrun_pager);
|
||||
pager.load(appContext, fm, new OnFinishListener() {
|
||||
pager = findViewById(R.id.firstrun_pager);
|
||||
pager.load(appContext, fm, useLocalValues, new OnFinishListener() {
|
||||
@Override
|
||||
public void onFinish() {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
|
||||
if (useLocalValues) {
|
||||
MmaDelegate.track(MmaDelegate.ONBOARDING_DEFAULT_VALUES);
|
||||
} else {
|
||||
MmaDelegate.track(MmaDelegate.ONBOARDING_REMOTE_VALUES);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
|
|
|
@ -63,15 +63,16 @@ public class FirstrunPager extends RtlViewPager {
|
|||
super.addView(child, index, params);
|
||||
}
|
||||
|
||||
public void load(Context appContext, FragmentManager fm, final FirstrunAnimationContainer.OnFinishListener onFinishListener) {
|
||||
public void load(Context appContext, FragmentManager fm, final boolean useLocalValues,
|
||||
final FirstrunAnimationContainer.OnFinishListener onFinishListener) {
|
||||
final List<FirstrunPagerConfig.FirstrunPanelConfig> panels;
|
||||
|
||||
if (Restrictions.isRestrictedProfile(context)) {
|
||||
panels = FirstrunPagerConfig.getRestricted();
|
||||
} else if (FirefoxAccounts.firefoxAccountsExist(context)) {
|
||||
panels = FirstrunPagerConfig.forFxAUser(appContext);
|
||||
if (Restrictions.isRestrictedProfile(appContext)) {
|
||||
panels = FirstrunPagerConfig.getRestricted(appContext);
|
||||
} else if (FirefoxAccounts.firefoxAccountsExist(appContext)) {
|
||||
panels = FirstrunPagerConfig.forFxAUser(appContext, useLocalValues);
|
||||
} else {
|
||||
panels = FirstrunPagerConfig.getDefault(appContext);
|
||||
panels = FirstrunPagerConfig.getDefault(appContext, useLocalValues);
|
||||
}
|
||||
|
||||
setAdapter(new ViewPagerAdapter(fm, panels));
|
||||
|
@ -144,7 +145,7 @@ public class FirstrunPager extends RtlViewPager {
|
|||
this.panels = panels;
|
||||
this.fragments = new Fragment[panels.size()];
|
||||
for (FirstrunPagerConfig.FirstrunPanelConfig panel : panels) {
|
||||
mDecor.onAddPagerView(context.getString(panel.getTitleRes()));
|
||||
mDecor.onAddPagerView(panel.getTitle());
|
||||
}
|
||||
|
||||
if (panels.size() > 0) {
|
||||
|
@ -172,7 +173,7 @@ public class FirstrunPager extends RtlViewPager {
|
|||
@Override
|
||||
public CharSequence getPageTitle(int i) {
|
||||
// Unused now that we use TabMenuStrip.
|
||||
return context.getString(panels.get(i).getTitleRes()).toUpperCase();
|
||||
return panels.get(i).getTitle().toUpperCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,95 +6,94 @@
|
|||
package org.mozilla.gecko.firstrun;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import org.mozilla.gecko.GeckoSharedPrefs;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Telemetry;
|
||||
import org.mozilla.gecko.TelemetryContract;
|
||||
import org.mozilla.gecko.Experiments;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.mozilla.gecko.mma.MmaDelegate;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class FirstrunPagerConfig {
|
||||
public static final String LOGTAG = "FirstrunPagerConfig";
|
||||
class FirstrunPagerConfig {
|
||||
static final String LOGTAG = "FirstrunPagerConfig";
|
||||
|
||||
public static final String KEY_IMAGE = "imageRes";
|
||||
public static final String KEY_TEXT = "textRes";
|
||||
public static final String KEY_SUBTEXT = "subtextRes";
|
||||
static final String KEY_IMAGE = "panelImage";
|
||||
static final String KEY_MESSAGE = "panelMessage";
|
||||
static final String KEY_SUBTEXT = "panelDescription";
|
||||
|
||||
public static List<FirstrunPanelConfig> getDefault(Context context) {
|
||||
static List<FirstrunPanelConfig> getDefault(Context context, final boolean useLocalValues) {
|
||||
final List<FirstrunPanelConfig> panels = new LinkedList<>();
|
||||
panels.add(SimplePanelConfigs.welcomePanelConfig);
|
||||
panels.add(SimplePanelConfigs.privatePanelConfig);
|
||||
panels.add(SimplePanelConfigs.customizePanelConfig);
|
||||
panels.add(SimplePanelConfigs.syncPanelConfig);
|
||||
panels.add(FirstrunPanelConfig.getConfiguredPanel(context, PanelConfig.TYPE.WELCOME, useLocalValues));
|
||||
panels.add(FirstrunPanelConfig.getConfiguredPanel(context, PanelConfig.TYPE.PRIVACY, useLocalValues));
|
||||
panels.add(FirstrunPanelConfig.getConfiguredPanel(context, PanelConfig.TYPE.CUSTOMIZE, useLocalValues));
|
||||
panels.add(FirstrunPanelConfig.getConfiguredPanel(context, PanelConfig.TYPE.SYNC, useLocalValues));
|
||||
|
||||
return panels;
|
||||
}
|
||||
|
||||
public static List<FirstrunPanelConfig> forFxAUser(Context context) {
|
||||
static List<FirstrunPanelConfig> forFxAUser(Context context, final boolean useLocalValues) {
|
||||
final List<FirstrunPanelConfig> panels = new LinkedList<>();
|
||||
panels.add(SimplePanelConfigs.welcomePanelConfig);
|
||||
panels.add(SimplePanelConfigs.privatePanelConfig);
|
||||
panels.add(SimplePanelConfigs.customizeLastPanelConfig);
|
||||
panels.add(FirstrunPanelConfig.getConfiguredPanel(context, PanelConfig.TYPE.WELCOME, useLocalValues));
|
||||
panels.add(FirstrunPanelConfig.getConfiguredPanel(context, PanelConfig.TYPE.PRIVACY, useLocalValues));
|
||||
panels.add(FirstrunPanelConfig.getConfiguredPanel(context, PanelConfig.TYPE.LAST_CUSTOMIZE, useLocalValues));
|
||||
|
||||
return panels;
|
||||
}
|
||||
|
||||
public static List<FirstrunPanelConfig> getRestricted() {
|
||||
static List<FirstrunPanelConfig> getRestricted(Context context) {
|
||||
final List<FirstrunPanelConfig> panels = new LinkedList<>();
|
||||
panels.add(new FirstrunPanelConfig(RestrictedWelcomePanel.class.getName(), RestrictedWelcomePanel.TITLE_RES));
|
||||
panels.add(new FirstrunPanelConfig(RestrictedWelcomePanel.class.getName(),
|
||||
context.getString(RestrictedWelcomePanel.TITLE_RES)));
|
||||
return panels;
|
||||
}
|
||||
|
||||
public static class FirstrunPanelConfig {
|
||||
|
||||
static class FirstrunPanelConfig {
|
||||
private String classname;
|
||||
private int titleRes;
|
||||
private String title;
|
||||
private Bundle args;
|
||||
|
||||
public FirstrunPanelConfig(String resource, int titleRes) {
|
||||
this(resource, titleRes, -1, -1, -1, true);
|
||||
FirstrunPanelConfig(String resource, String title) {
|
||||
this(resource, title, null, null, null, true);
|
||||
}
|
||||
|
||||
public FirstrunPanelConfig(String classname, int titleRes, int imageRes, int textRes, int subtextRes) {
|
||||
this(classname, titleRes, imageRes, textRes, subtextRes, false);
|
||||
}
|
||||
|
||||
private FirstrunPanelConfig(String classname, int titleRes, int imageRes, int textRes, int subtextRes, boolean isCustom) {
|
||||
private FirstrunPanelConfig(String classname, String title, Bitmap image, String message,
|
||||
String subtext, boolean isCustom) {
|
||||
this.classname = classname;
|
||||
this.titleRes = titleRes;
|
||||
this.title = title;
|
||||
|
||||
if (!isCustom) {
|
||||
this.args = new Bundle();
|
||||
this.args.putInt(KEY_IMAGE, imageRes);
|
||||
this.args.putInt(KEY_TEXT, textRes);
|
||||
this.args.putInt(KEY_SUBTEXT, subtextRes);
|
||||
args = new Bundle();
|
||||
args.putParcelable(KEY_IMAGE, image);
|
||||
args.putString(KEY_MESSAGE, message);
|
||||
args.putString(KEY_SUBTEXT, subtext);
|
||||
}
|
||||
}
|
||||
|
||||
public String getClassname() {
|
||||
return this.classname;
|
||||
static FirstrunPanelConfig getConfiguredPanel(@NonNull Context context,
|
||||
PanelConfig.TYPE wantedPanelConfig,
|
||||
final boolean useLocalValues) {
|
||||
PanelConfig panelConfig;
|
||||
if (useLocalValues) {
|
||||
panelConfig = new LocalFirstRunPanelProvider().getPanelConfig(context, wantedPanelConfig, useLocalValues);
|
||||
} else {
|
||||
panelConfig = new RemoteFirstRunPanelConfig().getPanelConfig(context, wantedPanelConfig, useLocalValues);
|
||||
}
|
||||
return new FirstrunPanelConfig(panelConfig.getClassName(), panelConfig.getTitle(),
|
||||
panelConfig.getImage(), panelConfig.getMessage(), panelConfig.getText(), false);
|
||||
}
|
||||
|
||||
public int getTitleRes() {
|
||||
return this.titleRes;
|
||||
|
||||
String getClassname() {
|
||||
return classname;
|
||||
}
|
||||
|
||||
public Bundle getArgs() {
|
||||
String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
Bundle getArgs() {
|
||||
return args;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SimplePanelConfigs {
|
||||
public static final FirstrunPanelConfig welcomePanelConfig = new FirstrunPanelConfig(FirstrunPanel.class.getName(), R.string.firstrun_panel_title_welcome, R.drawable.firstrun_welcome, R.string.firstrun_urlbar_message, R.string.firstrun_urlbar_subtext);
|
||||
public static final FirstrunPanelConfig privatePanelConfig = new FirstrunPanelConfig(FirstrunPanel.class.getName(), R.string.firstrun_panel_title_privacy, R.drawable.firstrun_private, R.string.firstrun_privacy_message, R.string.firstrun_privacy_subtext);
|
||||
public static final FirstrunPanelConfig customizePanelConfig = new FirstrunPanelConfig(FirstrunPanel.class.getName(), R.string.firstrun_panel_title_customize, R.drawable.firstrun_data, R.string.firstrun_customize_message, R.string.firstrun_customize_subtext);
|
||||
public static final FirstrunPanelConfig customizeLastPanelConfig = new FirstrunPanelConfig(LastPanel.class.getName(), R.string.firstrun_panel_title_customize, R.drawable.firstrun_data, R.string.firstrun_customize_message, R.string.firstrun_customize_subtext);
|
||||
|
||||
public static final FirstrunPanelConfig syncPanelConfig = new FirstrunPanelConfig(SyncPanel.class.getName(), R.string.firstrun_sync_title, R.drawable.firstrun_sync, R.string.firstrun_sync_message, R.string.firstrun_sync_subtext);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
package org.mozilla.gecko.firstrun;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -12,6 +13,7 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Telemetry;
|
||||
import org.mozilla.gecko.TelemetryContract;
|
||||
|
@ -24,7 +26,6 @@ import org.mozilla.gecko.TelemetryContract;
|
|||
*/
|
||||
public class FirstrunPanel extends Fragment {
|
||||
|
||||
public static final int TITLE_RES = -1;
|
||||
protected boolean showBrowserHint = true;
|
||||
|
||||
@Override
|
||||
|
@ -32,13 +33,13 @@ public class FirstrunPanel extends Fragment {
|
|||
final ViewGroup root = (ViewGroup) inflater.inflate(R.layout.firstrun_basepanel_checkable_fragment, container, false);
|
||||
final Bundle args = getArguments();
|
||||
if (args != null) {
|
||||
final int imageRes = args.getInt(FirstrunPagerConfig.KEY_IMAGE);
|
||||
final int textRes = args.getInt(FirstrunPagerConfig.KEY_TEXT);
|
||||
final int subtextRes = args.getInt(FirstrunPagerConfig.KEY_SUBTEXT);
|
||||
final Bitmap image = args.getParcelable(FirstrunPagerConfig.KEY_IMAGE);
|
||||
final String message = args.getString(FirstrunPagerConfig.KEY_MESSAGE);
|
||||
final String subtext = args.getString(FirstrunPagerConfig.KEY_SUBTEXT);
|
||||
|
||||
((ImageView) root.findViewById(R.id.firstrun_image)).setImageResource(imageRes);
|
||||
((TextView) root.findViewById(R.id.firstrun_text)).setText(textRes);
|
||||
((TextView) root.findViewById(R.id.firstrun_subtext)).setText(subtextRes);
|
||||
((ImageView) root.findViewById(R.id.firstrun_image)).setImageBitmap(image);
|
||||
((TextView) root.findViewById(R.id.firstrun_text)).setText(message);
|
||||
((TextView) root.findViewById(R.id.firstrun_subtext)).setText(subtext);
|
||||
}
|
||||
|
||||
root.findViewById(R.id.firstrun_link).setOnClickListener(new View.OnClickListener() {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
package org.mozilla.gecko.firstrun;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -22,15 +23,14 @@ public class LastPanel extends FirstrunPanel {
|
|||
final ViewGroup root = (ViewGroup) inflater.inflate(R.layout.firstrun_basepanel_checkable_fragment, container, false);
|
||||
final Bundle args = getArguments();
|
||||
if (args != null) {
|
||||
final int imageRes = args.getInt(FirstrunPagerConfig.KEY_IMAGE);
|
||||
final int textRes = args.getInt(FirstrunPagerConfig.KEY_TEXT);
|
||||
final int subtextRes = args.getInt(FirstrunPagerConfig.KEY_SUBTEXT);
|
||||
final Bitmap image = args.getParcelable(FirstrunPagerConfig.KEY_IMAGE);
|
||||
final String message = args.getString(FirstrunPagerConfig.KEY_MESSAGE);
|
||||
final String subtext = args.getString(FirstrunPagerConfig.KEY_SUBTEXT);
|
||||
|
||||
((ImageView) root.findViewById(R.id.firstrun_image)).setImageResource(imageRes);
|
||||
((TextView) root.findViewById(R.id.firstrun_text)).setText(textRes);
|
||||
((TextView) root.findViewById(R.id.firstrun_subtext)).setText(subtextRes);
|
||||
((ImageView) root.findViewById(R.id.firstrun_image)).setImageBitmap(image);
|
||||
((TextView) root.findViewById(R.id.firstrun_text)).setText(message);
|
||||
((TextView) root.findViewById(R.id.firstrun_subtext)).setText(subtext);
|
||||
((TextView) root.findViewById(R.id.firstrun_link)).setText(R.string.firstrun_welcome_button_browser);
|
||||
|
||||
}
|
||||
|
||||
root.findViewById(R.id.firstrun_link).setOnClickListener(new View.OnClickListener() {
|
||||
|
@ -41,7 +41,6 @@ public class LastPanel extends FirstrunPanel {
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/* -*- 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.firstrun;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
|
||||
public class LocalFirstRunPanelProvider implements FirstRunPanelConfigProviderStrategy {
|
||||
public PanelConfig getPanelConfig(@NonNull Context context, PanelConfig.TYPE type, final boolean useLocalValues) {
|
||||
final Resources resources = context.getResources();
|
||||
switch (type) {
|
||||
case WELCOME:
|
||||
return new PanelConfig(type, useLocalValues, resources.getString(R.string.firstrun_panel_title_welcome),
|
||||
resources.getString(R.string.firstrun_urlbar_message),
|
||||
resources.getString(R.string.firstrun_urlbar_subtext),
|
||||
BitmapFactory.decodeResource(resources, R.drawable.firstrun_welcome));
|
||||
case PRIVACY:
|
||||
return new PanelConfig(type, useLocalValues, resources.getString(R.string.firstrun_panel_title_privacy),
|
||||
resources.getString(R.string.firstrun_privacy_message),
|
||||
resources.getString(R.string.firstrun_privacy_subtext),
|
||||
BitmapFactory.decodeResource(resources, R.drawable.firstrun_private));
|
||||
case CUSTOMIZE:
|
||||
case LAST_CUSTOMIZE:
|
||||
return new PanelConfig(type, useLocalValues, resources.getString(R.string.firstrun_panel_title_customize),
|
||||
resources.getString(R.string.firstrun_customize_message),
|
||||
resources.getString(R.string.firstrun_customize_subtext),
|
||||
BitmapFactory.decodeResource(resources, R.drawable.firstrun_data));
|
||||
case SYNC:
|
||||
return new PanelConfig(type, useLocalValues, resources.getString(R.string.firstrun_sync_title),
|
||||
resources.getString(R.string.firstrun_sync_message),
|
||||
resources.getString(R.string.firstrun_sync_subtext),
|
||||
BitmapFactory.decodeResource(resources, R.drawable.firstrun_sync));
|
||||
default: // This will also be the case for "WELCOME"
|
||||
return new PanelConfig(type, useLocalValues, resources.getString(R.string.firstrun_panel_title_welcome),
|
||||
resources.getString(R.string.firstrun_urlbar_message),
|
||||
resources.getString(R.string.firstrun_urlbar_subtext),
|
||||
BitmapFactory.decodeResource(resources, R.drawable.firstrun_welcome));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,354 @@
|
|||
/* -*- 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.firstrun;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.StrictMode;
|
||||
import android.support.annotation.MainThread;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.view.ViewStub;
|
||||
|
||||
import org.mozilla.gecko.GeckoSharedPrefs;
|
||||
import org.mozilla.gecko.GeckoThread;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Telemetry;
|
||||
import org.mozilla.gecko.TelemetryContract;
|
||||
import org.mozilla.gecko.annotation.RobocopTarget;
|
||||
import org.mozilla.gecko.distribution.Distribution;
|
||||
import org.mozilla.gecko.mma.MmaDelegate;
|
||||
import org.mozilla.gecko.mozglue.SafeIntent;
|
||||
import org.mozilla.gecko.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.util.NetworkUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Helper class of an an {@link AppCompatActivity} for managing showing the Onboarding screens.
|
||||
* <br>The user class will have to implement {@link OnboardingListener}.
|
||||
*/
|
||||
public class OnboardingHelper implements MmaDelegate.MmaVariablesChangedListener,
|
||||
SwitchBoard.ConfigStatusListener {
|
||||
private static final String LOGTAG = "OnboardingHelper";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
@RobocopTarget
|
||||
public static final String EXTRA_SKIP_STARTPANE = "skipstartpane";
|
||||
|
||||
/** Be aware of {@link org.mozilla.gecko.fxa.EnvironmentUtils#GECKO_PREFS_FIRSTRUN_UUID}. */
|
||||
private static final String FIRSTRUN_UUID = "firstrun_uuid";
|
||||
|
||||
// Speculative timeout for showing the Onboarding screens with the default local values.
|
||||
public static final int DELAY_SHOW_DEFAULT_ONBOARDING = 3 * 1000;
|
||||
|
||||
private WeakReference<AppCompatActivity> activityRef;
|
||||
private OnboardingListener listener;
|
||||
private SafeIntent activityStartingIntent;
|
||||
private FirstrunAnimationContainer firstrunAnimationContainer;
|
||||
private Runnable showOnboarding;
|
||||
private boolean onboardingIsPreparing;
|
||||
private boolean abortOnboarding;
|
||||
private long startTimeForCheckingOnlineVariables;
|
||||
|
||||
public OnboardingHelper(
|
||||
@NonNull final AppCompatActivity activity,
|
||||
@NonNull final SafeIntent activityStartingIntent)
|
||||
throws IllegalArgumentException {
|
||||
|
||||
if (!(activity instanceof OnboardingListener)) {
|
||||
final String activityClass = activity.getClass().getSimpleName();
|
||||
final String listenerInterface = OnboardingListener.class.getSimpleName();
|
||||
throw new IllegalArgumentException(
|
||||
String.format("%s does not implement %s", activityClass, listenerInterface));
|
||||
}
|
||||
|
||||
this.activityRef = new WeakReference<>(activity);
|
||||
this.listener = (OnboardingListener) activity;
|
||||
this.activityStartingIntent = activityStartingIntent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and show the firstrun pane if the browser has never been launched and
|
||||
* is not opening an external link from another application.
|
||||
*/
|
||||
public void checkFirstRun() {
|
||||
if (GeckoThread.getActiveProfile().inGuestMode()) {
|
||||
// We do not want to show any first run tour for guest profiles.
|
||||
return;
|
||||
}
|
||||
|
||||
if (activityStartingIntent.getBooleanExtra(EXTRA_SKIP_STARTPANE, false)) {
|
||||
// Note that we don't set the pref, so subsequent launches can result
|
||||
// in the firstrun pane being shown.
|
||||
return;
|
||||
}
|
||||
|
||||
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
|
||||
|
||||
try {
|
||||
AppCompatActivity activity = activityRef.get();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
final SharedPreferences prefs = GeckoSharedPrefs.forProfile(activity);
|
||||
|
||||
if (prefs.getBoolean(FirstrunAnimationContainer.PREF_FIRSTRUN_ENABLED_OLD, true) &&
|
||||
prefs.getBoolean(FirstrunAnimationContainer.PREF_FIRSTRUN_ENABLED, true)) {
|
||||
|
||||
onboardingIsPreparing = true;
|
||||
listener.onOnboardingProcessStarted();
|
||||
|
||||
// Allow the activity to be gc'ed while waiting for the distribution
|
||||
activity = null;
|
||||
|
||||
if (!Intent.ACTION_VIEW.equals(activityStartingIntent.getAction())) {
|
||||
// Check to see if a distribution has turned off the first run pager.
|
||||
final Distribution distribution = Distribution.getInstance(activityRef.get());
|
||||
if (!distribution.shouldWaitForSystemDistribution()) {
|
||||
checkFirstrunInternal();
|
||||
} else {
|
||||
distribution.addOnDistributionReadyCallback(new Distribution.ReadyCallback() {
|
||||
@Override
|
||||
public void distributionNotFound() {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
checkFirstrunInternal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void distributionFound(final Distribution distribution) {
|
||||
// Check preference again in case distribution turned it off.
|
||||
if (prefs.getBoolean(FirstrunAnimationContainer.PREF_FIRSTRUN_ENABLED, true)) {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
checkFirstrunInternal();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void distributionArrivedLate(final Distribution distribution) {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// We have no intention of stopping this session. The FIRSTRUN session
|
||||
// ends when the browsing session/activity has ended. All events
|
||||
// during firstrun will be tagged as FIRSTRUN.
|
||||
Telemetry.startUISession(TelemetryContract.Session.FIRSTRUN);
|
||||
}
|
||||
} finally {
|
||||
StrictMode.setThreadPolicy(savedPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to prevent or finish displaying of the Onboarding process.<br>
|
||||
* If it has not yet been shown to the user and now it has been prevented to,
|
||||
* showing the Onboarding screens will be retried at the next app start.
|
||||
*
|
||||
* @return whether Onboarding was prevented / finished early or not.
|
||||
*/
|
||||
public boolean hideOnboarding() {
|
||||
abortOnboarding = true;
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, "hideOnboarding");
|
||||
}
|
||||
|
||||
if (isPreparing()) {
|
||||
onboardingIsPreparing = false;
|
||||
// Cancel showing Onboarding. Will retry automatically at the next app startup.
|
||||
ThreadUtils.removeCallbacksFromUiThread(showOnboarding);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isOnboardingVisible()) {
|
||||
onboardingIsPreparing = false;
|
||||
firstrunAnimationContainer.registerOnFinishListener(null);
|
||||
firstrunAnimationContainer.hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isOnboardingVisible() {
|
||||
return firstrunAnimationContainer != null && firstrunAnimationContainer.isVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if we are in the process of preparing the Onboarding screens.<br>
|
||||
* If the Onboarding screens should be shown to the user, they will be so after a small delay -
|
||||
* up to {@link #DELAY_SHOW_DEFAULT_ONBOARDING} necessary for downloading the data
|
||||
* needed to populate the screens.
|
||||
*
|
||||
* @return <code>true</code> - we are preparing for showing Onboarding but haven't yet done
|
||||
* <code>false</code> - Onboarding has been displayed
|
||||
*/
|
||||
public boolean isPreparing() {
|
||||
return onboardingIsPreparing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Code to actually show the first run pager, separated for distribution purposes.<br>
|
||||
* If network is available it will first try to use server values for populating the
|
||||
* onboarding screens. If that isn't possible the default local values will be used.
|
||||
*/
|
||||
@UiThread
|
||||
private void checkFirstrunInternal() {
|
||||
final AppCompatActivity activity = activityRef.get();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (abortOnboarding) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (NetworkUtils.isConnected(activity)) {
|
||||
showOnboarding = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
showFirstrunPager(true);
|
||||
}
|
||||
};
|
||||
|
||||
if (DEBUG) {
|
||||
startTimeForCheckingOnlineVariables = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
ThreadUtils.postDelayedToUiThread(showOnboarding, DELAY_SHOW_DEFAULT_ONBOARDING);
|
||||
} else {
|
||||
showFirstrunPager(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void showFirstrunPager(final boolean useLocalValues) {
|
||||
final AppCompatActivity activity = activityRef.get();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
onboardingIsPreparing = false;
|
||||
|
||||
if (firstrunAnimationContainer == null) {
|
||||
final ViewStub firstrunPagerStub = (ViewStub) activity.findViewById(R.id.firstrun_pager_stub);
|
||||
firstrunAnimationContainer = (FirstrunAnimationContainer) firstrunPagerStub.inflate();
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
final StringBuilder logMessage =
|
||||
new StringBuilder("Will show Onboarding using ")
|
||||
.append((useLocalValues ? "local" : "server"))
|
||||
.append(" values");
|
||||
Log.d(LOGTAG, logMessage.toString());
|
||||
}
|
||||
|
||||
firstrunAnimationContainer.load
|
||||
(activity.getApplicationContext(), activity.getSupportFragmentManager(), useLocalValues);
|
||||
firstrunAnimationContainer.registerOnFinishListener(new FirstrunAnimationContainer.OnFinishListener() {
|
||||
@Override
|
||||
public void onFinish() {
|
||||
listener.onFinishedOnboarding(firstrunAnimationContainer.showBrowserHint());
|
||||
}
|
||||
});
|
||||
|
||||
listener.onOnboardingScreensVisible();
|
||||
saveOnboardingShownStatus();
|
||||
}
|
||||
|
||||
// The Onboarding screens should only be shown one time.
|
||||
private void saveOnboardingShownStatus() {
|
||||
// The method is called serially from showFirstrunPager()
|
||||
// which stores a hard reference to the activity so it's safe to use it directly
|
||||
final SharedPreferences prefs = GeckoSharedPrefs.forProfile(activityRef.get());
|
||||
|
||||
prefs.edit()
|
||||
// Don't bother trying again to show the v1 minimal first run.
|
||||
.putBoolean(FirstrunAnimationContainer.PREF_FIRSTRUN_ENABLED, false)
|
||||
// Generate a unique identifier for the current first run.
|
||||
// See Bug 1429735 for why we care to do this.
|
||||
.putString(FIRSTRUN_UUID, UUID.randomUUID().toString())
|
||||
.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try showing the Onboarding screens even before #DELAY_SHOW_DEFAULT_ONBOARDING.<br>
|
||||
* If they have already been shown calling this method has no effect.
|
||||
*/
|
||||
private void tryShowOnboarding(final boolean shouldUseLocalValues) {
|
||||
final AppCompatActivity activity = activityRef.get();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPreparing()) {
|
||||
ThreadUtils.removeCallbacksFromUiThread(showOnboarding);
|
||||
showFirstrunPager(shouldUseLocalValues);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@MainThread
|
||||
public void onRemoteVariablesChanged() {
|
||||
if (DEBUG) {
|
||||
final long timeElapsed = System.currentTimeMillis() - startTimeForCheckingOnlineVariables;
|
||||
Log.d(LOGTAG, String.format("Got online variables after: %d millis", timeElapsed));
|
||||
}
|
||||
|
||||
tryShowOnboarding(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@MainThread
|
||||
public void onRemoteVariablesUnavailable() {
|
||||
tryShowOnboarding(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@MainThread
|
||||
public void onExperimentsConfigLoaded() {
|
||||
final AppCompatActivity activity = activityRef.get();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only if the Mma experiment is available we should continue to wait for server values.
|
||||
if (!MmaDelegate.isMmaExperimentEnabled(activity)) {
|
||||
tryShowOnboarding(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@MainThread
|
||||
public void onExperimentsConfigLoadFailed() {
|
||||
tryShowOnboarding(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs about the status of the onboarding process.
|
||||
*/
|
||||
public interface OnboardingListener {
|
||||
|
||||
void onOnboardingProcessStarted();
|
||||
|
||||
void onOnboardingScreensVisible();
|
||||
|
||||
void onFinishedOnboarding(final boolean showBrowserHint);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/* -*- 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.firstrun;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
/**
|
||||
* Onboarding screens configuration object.
|
||||
*/
|
||||
public class PanelConfig {
|
||||
public enum TYPE {
|
||||
WELCOME, PRIVACY, CUSTOMIZE, LAST_CUSTOMIZE, SYNC
|
||||
}
|
||||
|
||||
private final TYPE type;
|
||||
private final boolean useLocalValues;
|
||||
private final String title;
|
||||
private final String message;
|
||||
private final String text;
|
||||
private final Bitmap image;
|
||||
|
||||
public PanelConfig(TYPE type, boolean useLocalValues, String title, String message, String text, Bitmap image) {
|
||||
this.type = type;
|
||||
this.useLocalValues = useLocalValues;
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
this.text = text;
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
switch (type) {
|
||||
case WELCOME:
|
||||
case PRIVACY:
|
||||
case CUSTOMIZE:
|
||||
return FirstrunPanel.class.getName();
|
||||
case LAST_CUSTOMIZE:
|
||||
return LastPanel.class.getName();
|
||||
case SYNC:
|
||||
return SyncPanel.class.getName();
|
||||
default: // Return the default Panel, same as for "WELCOME"
|
||||
return FirstrunPanel.class.getName();
|
||||
}
|
||||
}
|
||||
|
||||
public TYPE getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean isUsingLocalValues() {
|
||||
return useLocalValues;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public Bitmap getImage() {
|
||||
return image;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* -*- 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.firstrun;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.mozilla.gecko.mma.MmaDelegate;
|
||||
|
||||
public class RemoteFirstRunPanelConfig implements FirstRunPanelConfigProviderStrategy {
|
||||
@Override
|
||||
public PanelConfig getPanelConfig(@NonNull Context context, PanelConfig.TYPE type, final boolean useLocalValues) {
|
||||
return MmaDelegate.getPanelConfig(context, type, useLocalValues);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
package org.mozilla.gecko.firstrun;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -24,13 +25,13 @@ public class SyncPanel extends FirstrunPanel {
|
|||
final ViewGroup root = (ViewGroup) inflater.inflate(R.layout.firstrun_sync_fragment, container, false);
|
||||
final Bundle args = getArguments();
|
||||
if (args != null) {
|
||||
final int imageRes = args.getInt(FirstrunPagerConfig.KEY_IMAGE);
|
||||
final int textRes = args.getInt(FirstrunPagerConfig.KEY_TEXT);
|
||||
final int subtextRes = args.getInt(FirstrunPagerConfig.KEY_SUBTEXT);
|
||||
final Bitmap image = args.getParcelable(FirstrunPagerConfig.KEY_IMAGE);
|
||||
final String message = args.getString(FirstrunPagerConfig.KEY_MESSAGE);
|
||||
final String subtext = args.getString(FirstrunPagerConfig.KEY_SUBTEXT);
|
||||
|
||||
((ImageView) root.findViewById(R.id.firstrun_image)).setImageResource(imageRes);
|
||||
((TextView) root.findViewById(R.id.firstrun_text)).setText(textRes);
|
||||
((TextView) root.findViewById(R.id.firstrun_subtext)).setText(subtextRes);
|
||||
((ImageView) root.findViewById(R.id.firstrun_image)).setImageBitmap(image);
|
||||
((TextView) root.findViewById(R.id.firstrun_text)).setText(message);
|
||||
((TextView) root.findViewById(R.id.firstrun_subtext)).setText(subtext);
|
||||
}
|
||||
|
||||
root.findViewById(R.id.welcome_account).setOnClickListener(new View.OnClickListener() {
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/* -*- 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.mma;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.leanplum.annotations.Variable;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* Unified repo for all LeanPlum variables.<br>
|
||||
* <ul>To make them appear in the LP dashboard and get new values from the server
|
||||
* <li>they must be annotated with {@link com.leanplum.annotations.Variable}.</li>
|
||||
* <li>they need to be parsed with {@link com.leanplum.annotations.Parser} after {@link com.leanplum.Leanplum#setApplicationContext(Context)}</li>
|
||||
* </ul>
|
||||
* Although some fields are public (LP SDK limitation) they are not to be written into.
|
||||
*
|
||||
* @see <a href="https://docs.leanplum.com/reference#defining-variables">Official LP variables documentation</a>
|
||||
*/
|
||||
public class LeanplumVariables {
|
||||
private static LeanplumVariables INSTANCE;
|
||||
private static Resources appResources;
|
||||
|
||||
private static final String FIRSTRUN_WELCOME_PANEL_GROUP_NAME = "FirstRun Welcome Panel";
|
||||
private static final String FIRSTRUN_PRIVACY_PANEL_GROUP_NAME = "FirstRun Privacy Panel";
|
||||
private static final String FIRSTRUN_CUSTOMIZE_PANEL_GROUP_NAME = "FirstRun Customize Panel";
|
||||
private static final String FIRSTRUN_SYNC_PANEL_GROUP_NAME = "FirstRun Sync Panel";
|
||||
|
||||
@Variable(group = FIRSTRUN_WELCOME_PANEL_GROUP_NAME) public static String welcomePanelTitle;
|
||||
@Variable(group = FIRSTRUN_WELCOME_PANEL_GROUP_NAME) public static String welcomePanelMessage;
|
||||
@Variable(group = FIRSTRUN_WELCOME_PANEL_GROUP_NAME) public static String welcomePanelSubtext;
|
||||
@DrawableRes private static int welcomeDrawableId;
|
||||
|
||||
@Variable(group = FIRSTRUN_PRIVACY_PANEL_GROUP_NAME) public static String privacyPanelTitle;
|
||||
@Variable(group = FIRSTRUN_PRIVACY_PANEL_GROUP_NAME) public static String privacyPanelMessage;
|
||||
@Variable(group = FIRSTRUN_PRIVACY_PANEL_GROUP_NAME) public static String privacyPanelSubtext;
|
||||
@DrawableRes private static int privacyDrawableId;
|
||||
|
||||
@Variable(group = FIRSTRUN_CUSTOMIZE_PANEL_GROUP_NAME) public static String customizePanelTitle;
|
||||
@Variable(group = FIRSTRUN_CUSTOMIZE_PANEL_GROUP_NAME) public static String customizePanelMessage;
|
||||
@Variable(group = FIRSTRUN_CUSTOMIZE_PANEL_GROUP_NAME) public static String customizePanelSubtext;
|
||||
@DrawableRes private static int customizingDrawableId;
|
||||
|
||||
@Variable(group = FIRSTRUN_SYNC_PANEL_GROUP_NAME) public static String syncPanelTitle;
|
||||
@Variable(group = FIRSTRUN_SYNC_PANEL_GROUP_NAME) public static String syncPanelMessage;
|
||||
@Variable(group = FIRSTRUN_SYNC_PANEL_GROUP_NAME) public static String syncPanelSubtext;
|
||||
@DrawableRes private static int syncDrawableId;
|
||||
|
||||
/**
|
||||
* Allows constructing and/or returning an already constructed instance of this class
|
||||
* which has all it's fields populated with values from Resources.<br><br>
|
||||
*
|
||||
* An instance of this class needs exist to allow overwriting it's fields with downloaded values from LeanPlum
|
||||
* @see com.leanplum.annotations.Parser#defineFileVariable(Object, String, String, Field)
|
||||
*/
|
||||
public static LeanplumVariables getInstance(Context appContext) {
|
||||
// Simple Singleton as we don't expect concurrency problems.
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new LeanplumVariables(appContext);
|
||||
}
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows setting default values for instance variables.
|
||||
* @param context used to access application resources
|
||||
*/
|
||||
private LeanplumVariables(@NonNull Context context) {
|
||||
appResources = context.getResources();
|
||||
welcomePanelTitle = appResources.getString(R.string.firstrun_panel_title_welcome);
|
||||
welcomePanelMessage = appResources.getString(R.string.firstrun_urlbar_message);
|
||||
welcomePanelSubtext = appResources.getString(R.string.firstrun_urlbar_subtext);
|
||||
welcomeDrawableId = R.drawable.firstrun_welcome;
|
||||
|
||||
privacyPanelTitle = appResources.getString(R.string.firstrun_panel_title_privacy);
|
||||
privacyPanelMessage = appResources.getString(R.string.firstrun_privacy_message);
|
||||
privacyPanelSubtext = appResources.getString(R.string.firstrun_privacy_subtext);
|
||||
privacyDrawableId = R.drawable.firstrun_private;
|
||||
|
||||
customizePanelTitle = appResources.getString(R.string.firstrun_panel_title_customize);
|
||||
customizePanelMessage = appResources.getString(R.string.firstrun_customize_message);
|
||||
customizePanelSubtext = appResources.getString(R.string.firstrun_customize_subtext);
|
||||
customizingDrawableId = R.drawable.firstrun_data;
|
||||
|
||||
syncPanelTitle = appResources.getString(R.string.firstrun_sync_title);
|
||||
syncPanelMessage = appResources.getString(R.string.firstrun_sync_message);
|
||||
syncPanelSubtext = appResources.getString(R.string.firstrun_sync_subtext);
|
||||
syncDrawableId = R.drawable.firstrun_sync;
|
||||
}
|
||||
|
||||
public static Bitmap getWelcomeImage() {
|
||||
return getBitmapFromMmaVar(welcomeDrawableId);
|
||||
}
|
||||
|
||||
public static Bitmap getPrivacyImage() {
|
||||
return getBitmapFromMmaVar(privacyDrawableId);
|
||||
}
|
||||
|
||||
public static Bitmap getCustomizingImage() {
|
||||
return getBitmapFromMmaVar(customizingDrawableId);
|
||||
}
|
||||
|
||||
public static Bitmap getSyncImage() {
|
||||
return getBitmapFromMmaVar(syncDrawableId);
|
||||
}
|
||||
|
||||
private static Bitmap getBitmapFromMmaVar(@DrawableRes final int drawableRes) {
|
||||
return BitmapFactory.decodeResource(appResources, drawableRes);
|
||||
}
|
||||
}
|
|
@ -24,10 +24,12 @@ import org.mozilla.gecko.R;
|
|||
import org.mozilla.gecko.Tab;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.activitystream.homepanel.ActivityStreamConfiguration;
|
||||
import org.mozilla.gecko.firstrun.PanelConfig;
|
||||
import org.mozilla.gecko.fxa.FirefoxAccounts;
|
||||
import org.mozilla.gecko.preferences.GeckoPreferences;
|
||||
import org.mozilla.gecko.switchboard.SwitchBoard;
|
||||
import org.mozilla.gecko.util.ContextUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
@ -47,6 +49,8 @@ public class MmaDelegate {
|
|||
public static final String RESUMED_FROM_BACKGROUND = "E_Resumed_From_Background";
|
||||
public static final String NEW_TAB = "E_Opened_New_Tab";
|
||||
public static final String DISMISS_ONBOARDING = "E_Dismiss_Onboarding";
|
||||
public static final String ONBOARDING_DEFAULT_VALUES = "E_Onboarding_With_Default_Values";
|
||||
public static final String ONBOARDING_REMOTE_VALUES = "E_Onboarding_With_Remote_Values";
|
||||
|
||||
private static final String LAUNCH_BUT_NOT_DEFAULT_BROWSER = "E_Launch_But_Not_Default_Browser";
|
||||
private static final String LAUNCH_BROWSER = "E_Launch_Browser";
|
||||
|
@ -75,7 +79,8 @@ public class MmaDelegate {
|
|||
private static final MmaInterface mmaHelper = MmaConstants.getMma();
|
||||
private static Context applicationContext;
|
||||
|
||||
public static void init(Activity activity) {
|
||||
public static void init(final Activity activity,
|
||||
final MmaVariablesChangedListener remoteVariablesListener) {
|
||||
applicationContext = activity.getApplicationContext();
|
||||
// Since user attributes are gathered in Fennec, not in MMA implementation,
|
||||
// we gather the information here then pass to mmaHelper.init()
|
||||
|
@ -92,6 +97,12 @@ public class MmaDelegate {
|
|||
}
|
||||
mmaHelper.event(MmaDelegate.LAUNCH_BROWSER);
|
||||
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mmaHelper.listenOnceForVariableChanges(remoteVariablesListener);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void stop() {
|
||||
|
@ -211,6 +222,10 @@ public class MmaDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
public static PanelConfig getPanelConfig(@NonNull Context context, PanelConfig.TYPE panelConfigType, final boolean useLocalValues) {
|
||||
return mmaHelper.getPanelConfig(context, panelConfigType, useLocalValues);
|
||||
}
|
||||
|
||||
private static String getDeviceId(Activity activity) {
|
||||
if (SwitchBoard.isInExperiment(activity, Experiments.LEANPLUM_DEBUG)) {
|
||||
return DEBUG_LEANPLUM_DEVICE_ID;
|
||||
|
@ -224,4 +239,10 @@ public class MmaDelegate {
|
|||
}
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public interface MmaVariablesChangedListener {
|
||||
void onRemoteVariablesChanged();
|
||||
|
||||
void onRemoteVariablesUnavailable();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ import android.support.annotation.CheckResult;
|
|||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.mozilla.gecko.firstrun.PanelConfig;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
|
@ -33,4 +35,8 @@ public interface MmaInterface {
|
|||
@CheckResult boolean handleGcmMessage(Context context, String from, Bundle bundle);
|
||||
|
||||
void setDeviceId(@NonNull String deviceId);
|
||||
|
||||
PanelConfig getPanelConfig(@NonNull Context context, PanelConfig.TYPE panelConfigType, final boolean useLocalValues);
|
||||
|
||||
void listenOnceForVariableChanges(@NonNull final MmaDelegate.MmaVariablesChangedListener listener);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ package org.mozilla.gecko.mma;
|
|||
import android.app.Activity;
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
|
@ -19,15 +18,17 @@ import com.leanplum.Leanplum;
|
|||
import com.leanplum.LeanplumActivityHelper;
|
||||
import com.leanplum.LeanplumPushNotificationCustomizer;
|
||||
import com.leanplum.LeanplumPushService;
|
||||
import com.leanplum.annotations.Parser;
|
||||
import com.leanplum.callbacks.VariablesChangedCallback;
|
||||
import com.leanplum.internal.Constants;
|
||||
import com.leanplum.internal.LeanplumInternal;
|
||||
import com.leanplum.internal.VarCache;
|
||||
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.MmaConstants;
|
||||
import org.mozilla.gecko.firstrun.PanelConfig;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
public class MmaLeanplumImp implements MmaInterface {
|
||||
|
@ -47,6 +48,8 @@ public class MmaLeanplumImp implements MmaInterface {
|
|||
|
||||
LeanplumActivityHelper.enableLifecycleCallbacks(activity.getApplication());
|
||||
|
||||
Parser.parseVariables(LeanplumVariables.getInstance(activity.getApplicationContext()));
|
||||
|
||||
if (AppConstants.MOZILLA_OFFICIAL) {
|
||||
Leanplum.setAppIdForProductionMode(MmaConstants.MOZ_LEANPLUM_SDK_CLIENTID, MmaConstants.MOZ_LEANPLUM_SDK_KEY);
|
||||
} else {
|
||||
|
@ -145,4 +148,45 @@ public class MmaLeanplumImp implements MmaInterface {
|
|||
Leanplum.setDeviceId(deviceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PanelConfig getPanelConfig(@NonNull Context context, PanelConfig.TYPE type, final boolean useLocalValues) {
|
||||
if (useLocalValues) {
|
||||
throw new UnsupportedOperationException("Cannot build remote panel config with local values");
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case WELCOME:
|
||||
return new PanelConfig(type, useLocalValues, LeanplumVariables.welcomePanelTitle, LeanplumVariables.welcomePanelMessage,
|
||||
LeanplumVariables.welcomePanelSubtext, LeanplumVariables.getWelcomeImage());
|
||||
case PRIVACY:
|
||||
return new PanelConfig(type, useLocalValues, LeanplumVariables.privacyPanelTitle, LeanplumVariables.privacyPanelMessage,
|
||||
LeanplumVariables.privacyPanelSubtext, LeanplumVariables.getPrivacyImage());
|
||||
case CUSTOMIZE:
|
||||
case LAST_CUSTOMIZE:
|
||||
return new PanelConfig(type, useLocalValues, LeanplumVariables.customizePanelTitle, LeanplumVariables.customizePanelMessage,
|
||||
LeanplumVariables.customizePanelSubtext, LeanplumVariables.getCustomizingImage());
|
||||
case SYNC:
|
||||
return new PanelConfig(type, useLocalValues, LeanplumVariables.syncPanelTitle, LeanplumVariables.syncPanelMessage,
|
||||
LeanplumVariables.syncPanelSubtext, LeanplumVariables.getSyncImage());
|
||||
default: // This will also be the case for "WELCOME"
|
||||
return new PanelConfig(type, useLocalValues, LeanplumVariables.welcomePanelTitle, LeanplumVariables.welcomePanelMessage,
|
||||
LeanplumVariables.welcomePanelSubtext, LeanplumVariables.getWelcomeImage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void listenOnceForVariableChanges(@NonNull final MmaDelegate.MmaVariablesChangedListener listener) {
|
||||
final WeakReference<MmaDelegate.MmaVariablesChangedListener> listenerRef = new WeakReference<>(listener);
|
||||
|
||||
Leanplum.addVariablesChangedHandler(new VariablesChangedCallback() {
|
||||
@Override
|
||||
public void variablesChanged() {
|
||||
Leanplum.removeVariablesChangedHandler(this);
|
||||
MmaDelegate.MmaVariablesChangedListener variablesChangesListener = listenerRef.get();
|
||||
if (variablesChangesListener != null) {
|
||||
variablesChangesListener.onRemoteVariablesChanged();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ import android.os.Bundle;
|
|||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.mozilla.gecko.firstrun.PanelConfig;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
|
@ -56,4 +58,13 @@ public class MmaStubImp implements MmaInterface {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PanelConfig getPanelConfig(@NonNull Context context, PanelConfig.TYPE panelConfigType, boolean useLocalValues) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void listenOnceForVariableChanges(@NonNull MmaDelegate.MmaVariablesChangedListener listener) {
|
||||
listener.onRemoteVariablesUnavailable();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ public class AsyncConfigLoader extends AsyncTask<Void, Void, Void> {
|
|||
|
||||
private Context context;
|
||||
private String defaultServerUrl;
|
||||
private SwitchBoard.ConfigStatusListener listener;
|
||||
|
||||
/**
|
||||
* Sets the params for async loading either SwitchBoard.updateConfigServerUrl()
|
||||
|
@ -41,14 +42,16 @@ public class AsyncConfigLoader extends AsyncTask<Void, Void, Void> {
|
|||
* @param c Application context
|
||||
* @param defaultServerUrl Default URL endpoint for Switchboard config.
|
||||
*/
|
||||
public AsyncConfigLoader(Context c, String defaultServerUrl) {
|
||||
public AsyncConfigLoader(Context c, String defaultServerUrl,
|
||||
SwitchBoard.ConfigStatusListener listener) {
|
||||
this.context = c;
|
||||
this.defaultServerUrl = defaultServerUrl;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
SwitchBoard.loadConfig(context, defaultServerUrl);
|
||||
SwitchBoard.loadConfig(context, defaultServerUrl, listener);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.mozilla.gecko.search.SearchEngineManager;
|
|||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
import org.mozilla.gecko.util.IOUtils;
|
||||
import org.mozilla.gecko.util.ProxySelector;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
|
@ -99,11 +100,18 @@ public class SwitchBoard {
|
|||
* @param c ApplicationContext
|
||||
* @param serverUrl Server URL endpoint.
|
||||
*/
|
||||
public static void loadConfig(Context c, @NonNull String serverUrl) {
|
||||
public static void loadConfig(Context c, @NonNull String serverUrl,
|
||||
@NonNull final ConfigStatusListener listener) {
|
||||
final URL url;
|
||||
try {
|
||||
url = new URL(serverUrl);
|
||||
} catch (MalformedURLException e) {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onExperimentsConfigLoadFailed();
|
||||
}
|
||||
});
|
||||
Log.e(TAG, "Exception creating server URL", e);
|
||||
return;
|
||||
}
|
||||
|
@ -111,11 +119,23 @@ public class SwitchBoard {
|
|||
final String result = readFromUrlGET(url);
|
||||
if (DEBUG) Log.d(TAG, "Result: " + result);
|
||||
if (result == null) {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onExperimentsConfigLoadFailed();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache result locally in shared preferences.
|
||||
Preferences.setDynamicConfigJson(c, result);
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onExperimentsConfigLoaded();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean isInBucket(Context c, int low, int high) {
|
||||
|
@ -453,4 +473,10 @@ public class SwitchBoard {
|
|||
long checksum = crc.getValue();
|
||||
return (int)(checksum % 100L);
|
||||
}
|
||||
|
||||
public interface ConfigStatusListener {
|
||||
void onExperimentsConfigLoaded();
|
||||
|
||||
void onExperimentsConfigLoadFailed();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,10 @@ public class SplashScreen extends RelativeLayout {
|
|||
}
|
||||
}
|
||||
|
||||
public void show(final int duration) {
|
||||
atLeast(duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
|
|
|
@ -13,11 +13,11 @@ import android.support.annotation.Nullable;
|
|||
*/
|
||||
public class EnvironmentUtils {
|
||||
/**
|
||||
* Must be kept in-sync with {@link org.mozilla.gecko.GeckoApp.PREFS_IS_FIRST_RUN}.
|
||||
* Must be kept in-sync with {@link org.mozilla.gecko.GeckoApp#PREFS_IS_FIRST_RUN}.
|
||||
*/
|
||||
private static final String GECKO_PREFS_IS_FIRST_RUN = "telemetry-isFirstRun";
|
||||
/**
|
||||
* Must be kept in-sync with {@link org.mozilla.gecko.BrowserApp.FIRSTRUN_UUID}.
|
||||
* Must be kept in-sync with {@link org.mozilla.gecko.firstrun.OnboardingHelper#FIRSTRUN_UUID}.
|
||||
*/
|
||||
private static final String GECKO_PREFS_FIRSTRUN_UUID = "firstrun_uuid";
|
||||
|
||||
|
|
|
@ -17,15 +17,14 @@ import com.robotium.solo.Solo;
|
|||
import org.mozilla.gecko.Actions;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.Assert;
|
||||
import org.mozilla.gecko.BrowserApp;
|
||||
import org.mozilla.gecko.Driver;
|
||||
import org.mozilla.gecko.FennecInstrumentationTestRunner;
|
||||
import org.mozilla.gecko.FennecMochitestAssert;
|
||||
import org.mozilla.gecko.FennecNativeActions;
|
||||
import org.mozilla.gecko.FennecNativeDriver;
|
||||
import org.mozilla.gecko.FennecTalosAssert;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.firstrun.OnboardingHelper;
|
||||
import org.mozilla.gecko.updater.UpdateServiceHelper;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
|
@ -126,7 +125,7 @@ public abstract class BaseRobocopTest extends ActivityInstrumentationTestCase2<A
|
|||
final Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.putExtra("args", "-no-remote -profile " + config.get("profile"));
|
||||
// Don't show the first run experience.
|
||||
intent.putExtra(BrowserApp.EXTRA_SKIP_STARTPANE, true);
|
||||
intent.putExtra(OnboardingHelper.EXTRA_SKIP_STARTPANE, true);
|
||||
|
||||
final String envString = config.get("envvars");
|
||||
if (!TextUtils.isEmpty(envString)) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче