OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.chrome.browser.firstrun; |
| 6 |
| 7 import android.app.Activity; |
| 8 import android.app.Fragment; |
| 9 import android.content.Intent; |
| 10 import android.os.Bundle; |
| 11 import android.support.v4.view.ViewPager; |
| 12 import android.support.v7.app.ActionBarActivity; |
| 13 import android.text.TextUtils; |
| 14 |
| 15 import org.chromium.base.ApplicationStatus; |
| 16 import org.chromium.base.VisibleForTesting; |
| 17 import org.chromium.chrome.R; |
| 18 import org.chromium.chrome.browser.ChromiumApplication; |
| 19 import org.chromium.chrome.browser.metrics.UmaBridge; |
| 20 import org.chromium.chrome.browser.profiles.Profile; |
| 21 |
| 22 import java.lang.ref.WeakReference; |
| 23 import java.lang.reflect.Constructor; |
| 24 import java.util.ArrayList; |
| 25 import java.util.List; |
| 26 import java.util.concurrent.Callable; |
| 27 |
| 28 /** |
| 29 * Handles the First Run Experience sequences shown to the user launching Chrome
for the first time. |
| 30 * It supports only a simple format of FRE: |
| 31 * [Welcome] |
| 32 * [Intro pages...] |
| 33 * [Sign-in page] |
| 34 * The activity might be run more than once, e.g. 1) for ToS and sign-in, and 2)
for intro. |
| 35 */ |
| 36 public class FirstRunActivity extends ActionBarActivity implements FirstRunPageD
elegate { |
| 37 protected static final String TAG = "FirstRunActivity"; |
| 38 |
| 39 // Incoming parameters: |
| 40 public static final String ORIGINAL_INTENT = "OriginalIntent"; |
| 41 public static final String FIRE_ORIGINAL_INTENT = "FireOriginalIntent"; |
| 42 public static final String COMING_FROM_CHROME_ICON = "ComingFromChromeIcon"; |
| 43 public static final String USE_FRE_FLOW_SEQUENCER = "UseFreFlowSequencer"; |
| 44 |
| 45 public static final String SHOW_WELCOME_PAGE = "ShowWelcome"; |
| 46 public static final String SKIP_WELCOME_PAGE_IF_ACCEPTED_TOS = "SkipWelcomeP
ageIfAcceptedToS"; |
| 47 public static final String SHOW_INTRO_BITMAP = "ShowIntroBitmap"; |
| 48 public static final String SKIP_ALL_INTRO = "SkipAllIntro"; // Marks all in
tros as seen. |
| 49 public static final String SHOW_SIGNIN_PAGE = "ShowSignIn"; |
| 50 |
| 51 // Outcoming results: |
| 52 public static final String RESULT_CLOSE_APP = "Close App"; |
| 53 public static final String RESULT_SIGNIN_ACCOUNT_NAME = "ResultSignInTo"; |
| 54 public static final String RESULT_SHOW_SYNC_SETTINGS = "ResultShowSyncSettin
gs"; |
| 55 |
| 56 @VisibleForTesting |
| 57 static FirstRunGlue sGlue = new FirstRunGlueImpl(); |
| 58 |
| 59 private boolean mShowWelcomePage = true; |
| 60 |
| 61 private String mResultSignInAccountName; |
| 62 private boolean mResultShowSyncSettings; |
| 63 |
| 64 // TODO(aurimas): make this private once all of FirstRunActivity is upstream
ed. |
| 65 protected boolean mNativeSideIsInitialized; |
| 66 |
| 67 private ProfileDataCache mProfileDataCache; |
| 68 private ViewPager mPager; |
| 69 |
| 70 private Bundle mFreProperties; |
| 71 |
| 72 private List<Callable<FirstRunPage>> mPages; |
| 73 private int mSkipIntroPageNumber; |
| 74 |
| 75 /** |
| 76 * The pager adapter, which provides the pages to the view pager widget. |
| 77 */ |
| 78 private FirstRunPagerAdapter mPagerAdapter; |
| 79 |
| 80 /** |
| 81 * Defines a sequence of pages to be shown (depending on parameters etc). |
| 82 */ |
| 83 private void createPageSequence() { |
| 84 mPages = new ArrayList<Callable<FirstRunPage>>(); |
| 85 |
| 86 // An optional welcome page. |
| 87 if (mShowWelcomePage) mPages.add(pageOf(ToSAndUMAFirstRunFragment.class)
); |
| 88 |
| 89 // An optional sequence of intro pages. |
| 90 if (!mFreProperties.getBoolean(SKIP_ALL_INTRO)) { |
| 91 final long bitmap = mFreProperties.getLong(SHOW_INTRO_BITMAP); |
| 92 |
| 93 // "Hera" (recents/tabs) promo. |
| 94 if (((bitmap & FirstRunIntroPage.INTRO_RECENTS) != 0) |
| 95 && sGlue.isDocumentModeEligible(getApplicationContext())) { |
| 96 mPages.add(pageOf(FirstRunIntroRecentsPage.class)); |
| 97 } |
| 98 } |
| 99 |
| 100 // Set the anchor to jump to if the user skips intro pages. |
| 101 mSkipIntroPageNumber = mPages.size(); |
| 102 |
| 103 // An optional sign-in page. |
| 104 if (mFreProperties.getBoolean(SHOW_SIGNIN_PAGE)) { |
| 105 mPages.add(pageOf(AccountFirstRunFragment.class)); |
| 106 } |
| 107 } |
| 108 |
| 109 // Activity: |
| 110 |
| 111 @Override |
| 112 protected void onCreate(Bundle savedInstanceState) { |
| 113 initializeBrowserProcess(); |
| 114 |
| 115 super.onCreate(savedInstanceState); |
| 116 setFinishOnTouchOutside(false); |
| 117 |
| 118 if (savedInstanceState != null) { |
| 119 mFreProperties = savedInstanceState; |
| 120 } else if (getIntent() != null) { |
| 121 mFreProperties = getIntent().getExtras(); |
| 122 } else { |
| 123 mFreProperties = new Bundle(); |
| 124 } |
| 125 |
| 126 mPager = new ViewPager(this); |
| 127 mPager.setId(R.id.fre_pager); |
| 128 setContentView(mPager); |
| 129 android.util.Log.i("FirstRunActivity", "onCreate: after setContentView")
; |
| 130 |
| 131 mProfileDataCache = new ProfileDataCache(FirstRunActivity.this, null); |
| 132 mProfileDataCache.setProfile(Profile.getLastUsedProfile()); |
| 133 new FirstRunFlowSequencer(this, mFreProperties) { |
| 134 @Override |
| 135 public void onFlowIsKnown(Activity activity, Bundle freProperties) { |
| 136 android.util.Log.i("FirstRunActivity", "in onFlowIsKnown"); |
| 137 |
| 138 if (freProperties == null) { |
| 139 completeFirstRunExperience(); |
| 140 return; |
| 141 } |
| 142 |
| 143 mFreProperties = freProperties; |
| 144 mShowWelcomePage = mFreProperties.getBoolean(SHOW_WELCOME_PAGE); |
| 145 if (mShowWelcomePage |
| 146 && mFreProperties.getBoolean(SKIP_WELCOME_PAGE_IF_ACCEPT
ED_TOS)) { |
| 147 mShowWelcomePage = !sGlue.didAcceptTermsOfService(getApplica
tionContext()); |
| 148 } |
| 149 createPageSequence(); |
| 150 |
| 151 if (TextUtils.isEmpty(mResultSignInAccountName)) { |
| 152 mResultSignInAccountName = mFreProperties.getString( |
| 153 AccountFirstRunFragment.FORCE_SIGNIN_ACCOUNT_TO); |
| 154 } |
| 155 |
| 156 if (mPages.size() == 0) { |
| 157 completeFirstRunExperience(); |
| 158 return; |
| 159 } |
| 160 |
| 161 mPagerAdapter = |
| 162 new FirstRunPagerAdapter(getFragmentManager(), mPages, m
FreProperties); |
| 163 stopProgressionIfNotAcceptedTermsOfService(); |
| 164 mPager.setAdapter(mPagerAdapter); |
| 165 |
| 166 skipPagesIfNecessary(); |
| 167 android.util.Log.i("FirstRunActivity", "before return from onFlo
wIsKnown"); |
| 168 } |
| 169 }.start(); |
| 170 android.util.Log.i("FirstRunActivity", "onCreate: after starting the seq
uencer"); |
| 171 } |
| 172 |
| 173 @Override |
| 174 protected void onSaveInstanceState(Bundle outState) { |
| 175 super.onSaveInstanceState(outState); |
| 176 outState.putAll(mFreProperties); |
| 177 } |
| 178 |
| 179 @Override |
| 180 protected void onPause() { |
| 181 super.onPause(); |
| 182 flushPersistentData(); |
| 183 } |
| 184 |
| 185 @Override |
| 186 protected void onResumeFragments() { |
| 187 skipPagesIfNecessary(); |
| 188 } |
| 189 |
| 190 @Override |
| 191 protected void onDestroy() { |
| 192 super.onDestroy(); |
| 193 mProfileDataCache.onDestroy(); |
| 194 } |
| 195 |
| 196 @Override |
| 197 protected void onStart() { |
| 198 super.onStart(); |
| 199 stopProgressionIfNotAcceptedTermsOfService(); |
| 200 if (!mFreProperties.getBoolean(USE_FRE_FLOW_SEQUENCER)) { |
| 201 if (FirstRunStatus.getFirstRunFlowComplete(this) |
| 202 && FirstRunIntroPage.wereAllNecessaryPagesShown(this)) { |
| 203 // This is a parallel flow that needs to be refreshed/re-fired. |
| 204 // Signal the FRE flow completion and re-launch the original int
ent. |
| 205 completeFirstRunExperience(); |
| 206 } |
| 207 } |
| 208 } |
| 209 |
| 210 @Override |
| 211 public void onBackPressed() { |
| 212 // Terminate if we are still waiting for the native or for Android EDU /
GAIA Child checks. |
| 213 if (mPagerAdapter == null) { |
| 214 abortFirstRunExperience(); |
| 215 return; |
| 216 } |
| 217 |
| 218 Object currentItem = mPagerAdapter.instantiateItem(mPager, mPager.getCur
rentItem()); |
| 219 if (currentItem instanceof FirstRunPage) { |
| 220 FirstRunPage page = (FirstRunPage) currentItem; |
| 221 if (page.interceptBackPressed()) return; |
| 222 } |
| 223 |
| 224 if (mPager.getCurrentItem() == 0) { |
| 225 abortFirstRunExperience(); |
| 226 } else { |
| 227 mPager.setCurrentItem(mPager.getCurrentItem() - 1); |
| 228 } |
| 229 } |
| 230 |
| 231 // FirstRunPageDelegate: |
| 232 |
| 233 @Override |
| 234 public ProfileDataCache getProfileDataCache() { |
| 235 return mProfileDataCache; |
| 236 } |
| 237 |
| 238 @Override |
| 239 public void advanceToNextPage() { |
| 240 jumpToPage(mPager.getCurrentItem() + 1, true); |
| 241 } |
| 242 |
| 243 @Override |
| 244 public void skipIntroPages() { |
| 245 jumpToPage(mSkipIntroPageNumber, false); |
| 246 } |
| 247 |
| 248 @Override |
| 249 public void recreateCurrentPage() { |
| 250 mPagerAdapter.notifyDataSetChanged(); |
| 251 } |
| 252 |
| 253 @Override |
| 254 public void abortFirstRunExperience() { |
| 255 Intent intent = new Intent(); |
| 256 if (mFreProperties != null) intent.putExtras(mFreProperties); |
| 257 intent.putExtra(RESULT_CLOSE_APP, true); |
| 258 finishAllFREActivities(Activity.RESULT_CANCELED, intent); |
| 259 } |
| 260 |
| 261 @Override |
| 262 public void completeFirstRunExperience() { |
| 263 if (!TextUtils.isEmpty(mResultSignInAccountName)) { |
| 264 UmaBridge.freSignInAccepted( |
| 265 mResultShowSyncSettings, |
| 266 sGlue.isDefaultAccountName(getApplicationContext(), mResultS
ignInAccountName)); |
| 267 } |
| 268 |
| 269 mFreProperties.putString(RESULT_SIGNIN_ACCOUNT_NAME, mResultSignInAccoun
tName); |
| 270 mFreProperties.putBoolean(RESULT_SHOW_SYNC_SETTINGS, mResultShowSyncSett
ings); |
| 271 FirstRunFlowSequencer.markFlowAsCompleted(this, mFreProperties); |
| 272 |
| 273 if (mFreProperties.getBoolean(FirstRunActivity.FIRE_ORIGINAL_INTENT)) { |
| 274 Intent originalIntent = mFreProperties.getParcelable(FirstRunActivit
y.ORIGINAL_INTENT); |
| 275 startActivity(originalIntent); |
| 276 } |
| 277 |
| 278 Intent resultData = new Intent(); |
| 279 resultData.putExtras(mFreProperties); |
| 280 finishAllFREActivities(Activity.RESULT_OK, resultData); |
| 281 } |
| 282 |
| 283 @Override |
| 284 public void onSigninDialogShown() { |
| 285 UmaBridge.freSignInShown(); |
| 286 } |
| 287 |
| 288 @Override |
| 289 public void refuseSignIn() { |
| 290 UmaBridge.freSkipSignIn(); |
| 291 UmaBridge.freSignInDeclined(); |
| 292 mResultSignInAccountName = null; |
| 293 mResultShowSyncSettings = false; |
| 294 } |
| 295 |
| 296 @Override |
| 297 public void acceptSignIn(String accountName) { |
| 298 mResultSignInAccountName = accountName; |
| 299 } |
| 300 |
| 301 @Override |
| 302 public void askToOpenSyncSettings() { |
| 303 mResultShowSyncSettings = true; |
| 304 } |
| 305 |
| 306 @Override |
| 307 public boolean didAcceptTermsOfService() { |
| 308 return sGlue.didAcceptTermsOfService(getApplicationContext()); |
| 309 } |
| 310 |
| 311 @Override |
| 312 public boolean isNeverUploadCrashDump() { |
| 313 return sGlue.isNeverUploadCrashDump(getApplicationContext()); |
| 314 } |
| 315 |
| 316 @Override |
| 317 public void acceptTermsOfService(boolean allowCrashUpload) { |
| 318 sGlue.acceptTermsOfService(getApplicationContext(), allowCrashUpload); |
| 319 flushPersistentData(); |
| 320 stopProgressionIfNotAcceptedTermsOfService(); |
| 321 jumpToPage(mPager.getCurrentItem() + 1, true); |
| 322 } |
| 323 |
| 324 @Override |
| 325 public void openAccountAdder(Fragment fragment) { |
| 326 sGlue.openAccountAdder(fragment); |
| 327 } |
| 328 |
| 329 protected void flushPersistentData() { |
| 330 if (mNativeSideIsInitialized) ChromiumApplication.flushPersistentData(); |
| 331 } |
| 332 |
| 333 private static void finishAllFREActivities(int result, Intent data) { |
| 334 List<WeakReference<Activity>> activities = ApplicationStatus.getRunningA
ctivities(); |
| 335 for (WeakReference<Activity> weakActivity : activities) { |
| 336 Activity activity = weakActivity.get(); |
| 337 if (activity instanceof FirstRunActivity) { |
| 338 activity.setResult(result, data); |
| 339 activity.finish(); |
| 340 } |
| 341 } |
| 342 } |
| 343 |
| 344 |
| 345 /** |
| 346 * Transitions to a given page. |
| 347 * @return Whether the transition to a given page was allowed. |
| 348 * @param position A page index to transition to. |
| 349 * @param smooth Whether the transition should be smooth. |
| 350 */ |
| 351 private boolean jumpToPage(int position, boolean smooth) { |
| 352 if (mShowWelcomePage && !didAcceptTermsOfService()) { |
| 353 return position == 0; |
| 354 } |
| 355 if (position >= mPagerAdapter.getCount()) { |
| 356 completeFirstRunExperience(); |
| 357 return false; |
| 358 } |
| 359 mPager.setCurrentItem(position, smooth); |
| 360 return true; |
| 361 } |
| 362 |
| 363 private void stopProgressionIfNotAcceptedTermsOfService() { |
| 364 if (mPagerAdapter == null) return; |
| 365 mPagerAdapter.setStopAtTheFirstPage(mShowWelcomePage && !didAcceptTermsO
fService()); |
| 366 } |
| 367 |
| 368 private void skipPagesIfNecessary() { |
| 369 if (mPagerAdapter == null) return; |
| 370 |
| 371 int currentPageIndex = mPager.getCurrentItem(); |
| 372 while (currentPageIndex < mPagerAdapter.getCount()) { |
| 373 FirstRunPage currentPage = (FirstRunPage) mPagerAdapter.getItem(curr
entPageIndex); |
| 374 if (!currentPage.shouldSkipPageOnResume(getApplicationContext())) re
turn; |
| 375 if (!jumpToPage(currentPageIndex + 1, false)) return; |
| 376 currentPageIndex = mPager.getCurrentItem(); |
| 377 } |
| 378 } |
| 379 |
| 380 protected void initializeBrowserProcess() { |
| 381 // TODO(aurimas): implement this once browser process initialization is
upstreamed. |
| 382 } |
| 383 |
| 384 /** |
| 385 * Creates a trivial page constructor for a given page type. |
| 386 * @param clazz The .class of the page type. |
| 387 * @return The simple constructor for a given page type (no parameters, no t
uning). |
| 388 */ |
| 389 public static Callable<FirstRunPage> pageOf(final Class<? extends FirstRunPa
ge> clazz) { |
| 390 return new Callable<FirstRunPage>() { |
| 391 @Override |
| 392 public FirstRunPage call() throws Exception { |
| 393 Constructor<? extends FirstRunPage> constructor = clazz.getDecla
redConstructor(); |
| 394 return constructor.newInstance(); |
| 395 } |
| 396 }; |
| 397 } |
| 398 |
| 399 @Override |
| 400 public void showEmbedContentViewActivity(int title, int url) { |
| 401 // TODO(aurimas): implement this once EmbededContentViewActivity is upst
reamed. |
| 402 } |
| 403 |
| 404 public void showSignInNotification() { |
| 405 // TODO(aurimas): implement this once GoogleServicesManager is upstreame
d. |
| 406 } |
| 407 |
| 408 @Override |
| 409 public void openDocumentModeSettings() { |
| 410 // TODO(aurimas): implement opening settings once DocumentModeSettings i
s upstreamed. |
| 411 } |
| 412 } |
OLD | NEW |