package org.wikipedia.page; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.SearchManager; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.location.Location; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.support.design.widget.BottomSheetDialog; import android.support.design.widget.BottomSheetDialogFragment; import android.support.v4.app.Fragment; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.preference.PreferenceManager; import android.support.v7.view.ActionMode; import android.support.v7.widget.Toolbar; import android.text.format.DateUtils; import android.view.KeyEvent; import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.widget.ProgressBar; import android.widget.Toast; import com.squareup.otto.Bus; import com.squareup.otto.Subscribe; import net.hockeyapp.android.metrics.MetricsManager; import org.apache.commons.lang3.StringUtils; import org.wikipedia.Constants; import org.wikipedia.R; import org.wikipedia.WikipediaApp; import org.wikipedia.activity.ThemedActionBarActivity; import org.wikipedia.analytics.IntentFunnel; import org.wikipedia.analytics.LinkPreviewFunnel; import org.wikipedia.dataclient.WikiSite; import org.wikipedia.descriptions.DescriptionEditRevertHelpView; import org.wikipedia.events.ChangeTextSizeEvent; import org.wikipedia.gallery.GalleryActivity; import org.wikipedia.history.HistoryEntry; import org.wikipedia.language.LangLinksActivity; import org.wikipedia.page.linkpreview.LinkPreviewDialog; import org.wikipedia.page.snippet.CompatActionMode; import org.wikipedia.page.tabs.TabsProvider; import org.wikipedia.page.tabs.TabsProvider.TabPosition; import org.wikipedia.readinglist.AddToReadingListDialog; import org.wikipedia.search.SearchFragment; import org.wikipedia.search.SearchInvokeSource; import org.wikipedia.settings.SettingsActivity; import org.wikipedia.staticdata.MainPageNameData; import org.wikipedia.theme.ThemeChooserDialog; import org.wikipedia.tooltip.ToolTipUtil; import org.wikipedia.useroption.sync.UserOptionContentResolver; import org.wikipedia.util.ClipboardUtil; import org.wikipedia.util.DeviceUtil; import org.wikipedia.util.FeedbackUtil; import org.wikipedia.util.ShareUtil; import org.wikipedia.util.log.L; import org.wikipedia.widgets.WidgetProviderFeaturedPage; import org.wikipedia.wiktionary.WiktionaryDialog; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; import static org.wikipedia.util.DeviceUtil.isBackKeyUp; import static org.wikipedia.util.UriUtil.visitInExternalBrowser; public class PageActivity extends ThemedActionBarActivity implements PageFragment.Callback, LinkPreviewDialog.Callback, SearchFragment.Callback, WiktionaryDialog.Callback { public static final String ACTION_PAGE_FOR_TITLE = "org.wikipedia.page_for_title"; public static final String ACTION_SHOW_TAB_LIST = "org.wikipedia.show_tab_list"; public static final String ACTION_RESUME_READING = "org.wikipedia.resume_reading"; public static final String EXTRA_PAGETITLE = "org.wikipedia.pagetitle"; public static final String EXTRA_HISTORYENTRY = "org.wikipedia.history.historyentry"; private static final String LANGUAGE_CODE_BUNDLE_KEY = "language"; @BindView(R.id.tabs_container) View tabsContainerView; @BindView(R.id.page_progress_bar) ProgressBar progressBar; @BindView(R.id.page_toolbar_container) View toolbarContainerView; @BindView(R.id.page_toolbar) Toolbar toolbar; @Nullable private Unbinder unbinder; private PageFragment pageFragment; private WikipediaApp app; @Nullable private Bus bus; private EventBusMethods busMethods; private CompatActionMode currentActionMode; private PageToolbarHideHandler toolbarHideHandler; private ExclusiveBottomSheetPresenter bottomSheetPresenter = new ExclusiveBottomSheetPresenter(); @Nullable private PageLoadCallbacks pageLoadCallbacks; private DialogInterface.OnDismissListener listDialogDismissListener = new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialogInterface) { pageFragment.updateBookmark(); } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); app = (WikipediaApp) getApplicationContext(); MetricsManager.register(app, app); app.checkCrashes(this); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } PreferenceManager.setDefaultValues(this, R.xml.preferences, false); try { setContentView(R.layout.activity_page); } catch (Exception e) { if (e.getMessage().contains("WebView")) { // If the system failed to inflate our activity because of the WebView (which could // be one of several types of exceptions), it likely means that the system WebView // is in the process of being updated. In this case, show the user a message and // bail immediately. Toast.makeText(app, R.string.error_webview_updating, Toast.LENGTH_LONG).show(); finish(); return; } throw e; } unbinder = ButterKnife.bind(this); busMethods = new EventBusMethods(); registerBus(); updateProgressBar(false, true, 0); pageFragment = (PageFragment) getSupportFragmentManager().findFragmentById(R.id.page_fragment); setSupportActionBar(toolbar); getSupportActionBar().setTitle(""); getSupportActionBar().setDisplayHomeAsUpEnabled(true); toolbarHideHandler = new PageToolbarHideHandler(toolbarContainerView); boolean languageChanged = false; if (savedInstanceState != null) { if (savedInstanceState.getBoolean("isSearching")) { openSearchFragment(SearchInvokeSource.TOOLBAR, null); } String language = savedInstanceState.getString(LANGUAGE_CODE_BUNDLE_KEY); languageChanged = !app.getAppOrSystemLanguageCode().equals(language); // Note: when system language is enabled, and the system language is changed outside of // the app, MRU languages are not updated. There's no harm in doing that here but since // the user didin't choose that language in app, it may be unexpected. } if (languageChanged) { app.resetWikiSite(); loadMainPageInForegroundTab(); } if (savedInstanceState == null) { // if there's no savedInstanceState, and we're not coming back from a Theme change, // then we must have been launched with an Intent, so... handle it! handleIntent(getIntent()); } UserOptionContentResolver.requestManualSync(); } private void finishActionMode() { currentActionMode.finish(); } private void nullifyActionMode() { currentActionMode = null; } public void hideSoftKeyboard() { DeviceUtil.hideSoftKeyboard(this); } // Note: this method is invoked even when in CAB mode. @Override public boolean dispatchKeyEvent(@NonNull KeyEvent event) { return isBackKeyUp(event) && ToolTipUtil.dismissToolTip(this) || super.dispatchKeyEvent(event); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: if (shouldRecreateMainActivity()) { startActivity(getSupportParentActivityIntent() .putExtra(Constants.INTENT_RETURN_TO_MAIN, true)); } finish(); return true; default: return super.onOptionsItemSelected(item); } } @Override public boolean onSearchRequested() { showToolbar(); openSearchFragment(SearchInvokeSource.TOOLBAR, null); return true; } public void showToolbar() { // TODO: make toolbar visible, via CoordinatorLayout } /** @return True if the contextual action bar is open. */ public boolean isCabOpen() { return currentActionMode != null; } @NonNull public static Intent newIntent(@NonNull Context context) { return new Intent(ACTION_RESUME_READING).setClass(context, PageActivity.class); } @NonNull public static Intent newIntent(@NonNull Context context, @NonNull String title) { PageTitle pageTitle = new PageTitle(title, WikipediaApp.getInstance().getWikiSite()); return newIntent(context, new HistoryEntry(pageTitle, HistoryEntry.SOURCE_INTERNAL_LINK), pageTitle); } @NonNull public static Intent newIntent(@NonNull Context context, @NonNull HistoryEntry entry, @NonNull PageTitle title) { return new Intent(ACTION_PAGE_FOR_TITLE) .setClass(context, PageActivity.class) .putExtra(EXTRA_HISTORYENTRY, entry) .putExtra(EXTRA_PAGETITLE, title); } @NonNull public static Intent newIntentForTabList(@NonNull Context context) { return new Intent(ACTION_SHOW_TAB_LIST).setClass(context, PageActivity.class); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); handleIntent(intent); } private void handleIntent(@NonNull Intent intent) { if (Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() != null) { WikiSite wiki = new WikiSite(intent.getData().getAuthority()); PageTitle title = wiki.titleForUri(intent.getData()); HistoryEntry historyEntry = new HistoryEntry(title, HistoryEntry.SOURCE_EXTERNAL_LINK); loadPageInForegroundTab(title, historyEntry); } else if (ACTION_PAGE_FOR_TITLE.equals(intent.getAction())) { PageTitle title = intent.getParcelableExtra(EXTRA_PAGETITLE); HistoryEntry historyEntry = intent.getParcelableExtra(EXTRA_HISTORYENTRY); loadPageInForegroundTab(title, historyEntry); if (intent.hasExtra(Constants.INTENT_EXTRA_REVERT_QNUMBER)) { showDescriptionEditRevertDialog(intent.getStringExtra(Constants.INTENT_EXTRA_REVERT_QNUMBER)); } } else if (ACTION_SHOW_TAB_LIST.equals(intent.getAction()) || ACTION_RESUME_READING.equals(intent.getAction())) { // do nothing, since this will be handled indirectly by PageFragment. } else if (Intent.ACTION_SEARCH.equals(intent.getAction())) { String query = intent.getStringExtra(SearchManager.QUERY); PageTitle title = new PageTitle(query, app.getWikiSite()); HistoryEntry historyEntry = new HistoryEntry(title, HistoryEntry.SOURCE_SEARCH); loadPageInForegroundTab(title, historyEntry); } else if (intent.hasExtra(Constants.INTENT_FEATURED_ARTICLE_FROM_WIDGET)) { new IntentFunnel(app).logFeaturedArticleWidgetTap(); loadMainPageInForegroundTab(); } else { loadMainPageInCurrentTab(); } } /** * Update the state of the main progress bar that is shown inside the ActionBar of the activity. * @param visible Whether the progress bar is visible. * @param indeterminate Whether the progress bar is indeterminate. * @param value Value of the progress bar (may be between 0 and 10000). Ignored if the * progress bar is indeterminate. */ public void updateProgressBar(boolean visible, boolean indeterminate, int value) { progressBar.setIndeterminate(indeterminate); if (!indeterminate) { progressBar.setProgress(value); } progressBar.setVisibility(visible ? View.VISIBLE : View.GONE); } /** * Returns whether we're currently in a "searching" state (i.e. the search fragment is shown). * @return True if currently searching, false otherwise. */ public boolean isSearching() { SearchFragment searchFragment = searchFragment(); return searchFragment != null && searchFragment.isSearchActive(); } /** * Load a new page, and put it on top of the backstack. * @param title Title of the page to load. * @param entry HistoryEntry associated with this page. */ public void loadPage(PageTitle title, HistoryEntry entry) { loadPage(title, entry, TabPosition.CURRENT_TAB); } /** * Load a new page, and put it on top of the backstack, optionally allowing state loss of the * fragment manager. Useful for when this function is called from an AsyncTask result. * @param title Title of the page to load. * @param entry HistoryEntry associated with this page. * @param position Whether to open this page in the current tab, a new background tab, or new * foreground tab. */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public void loadPage(final PageTitle title, final HistoryEntry entry, final TabPosition position) { if (isDestroyed()) { return; } if (entry.getSource() != HistoryEntry.SOURCE_INTERNAL_LINK || !app.isLinkPreviewEnabled()) { new LinkPreviewFunnel(app, entry.getSource()).logNavigate(); } app.putCrashReportProperty("api", title.getWikiSite().authority()); app.putCrashReportProperty("title", title.toString()); if (title.isSpecial()) { visitInExternalBrowser(this, Uri.parse(title.getMobileUri())); return; } tabsContainerView.post(new Runnable() { @Override public void run() { if (!pageFragment.isAdded()) { return; } // Close the link preview, if one is open. hideLinkPreview(); pageFragment.closeFindInPage(); if (position == TabPosition.CURRENT_TAB) { pageFragment.loadPage(title, entry, true); } else if (position == TabPosition.NEW_TAB_BACKGROUND) { pageFragment.openInNewBackgroundTabFromMenu(title, entry); } else { pageFragment.openInNewForegroundTabFromMenu(title, entry); } app.getSessionFunnel().pageViewed(entry); } }); } public void loadPageInForegroundTab(PageTitle title, HistoryEntry entry) { loadPage(title, entry, TabPosition.NEW_TAB_FOREGROUND); } public void loadMainPageInForegroundTab() { loadMainPage(TabPosition.NEW_TAB_FOREGROUND); } private void loadMainPageInCurrentTab() { loadMainPage(TabPosition.CURRENT_TAB); } /** * Go directly to the Main Page of the current Wiki, optionally allowing state loss of the * fragment manager. Useful for when this function is called from an AsyncTask result. */ public void loadMainPage(TabPosition position) { PageTitle title = new PageTitle(MainPageNameData.valueFor(app.getAppOrSystemLanguageCode()), app.getWikiSite()); HistoryEntry historyEntry = new HistoryEntry(title, HistoryEntry.SOURCE_MAIN_PAGE); loadPage(title, historyEntry, position); } public void showLinkPreview(PageTitle title, int entrySource) { showLinkPreview(title, entrySource, null); } public void showLinkPreview(PageTitle title, int entrySource, @Nullable Location location) { bottomSheetPresenter.show(getSupportFragmentManager(), LinkPreviewDialog.newInstance(title, entrySource, location)); } private void hideLinkPreview() { bottomSheetPresenter.dismiss(getSupportFragmentManager()); } public void showAddToListDialog(@NonNull PageTitle title, @NonNull AddToReadingListDialog.InvokeSource source) { bottomSheetPresenter.showAddToListDialog(getSupportFragmentManager(), title, source, listDialogDismissListener); } // Note: back button first handled in {@link #onOptionsItemSelected()}; @Override public void onBackPressed() { if (ToolTipUtil.dismissToolTip(this)) { return; } if (isCabOpen()) { finishActionMode(); return; } SearchFragment searchFragment = searchFragment(); if (searchFragment != null && searchFragment.onBackPressed()) { if (searchFragment.isLaunchedFromIntent()) { finish(); } return; } getSupportActionBar().setTitle(""); app.getSessionFunnel().backPressed(); if (pageFragment.onBackPressed()) { return; } finish(); } @Override public void onPageShowBottomSheet(@NonNull BottomSheetDialog dialog) { bottomSheetPresenter.show(getSupportFragmentManager(), dialog); } @Override public void onPageShowBottomSheet(@NonNull BottomSheetDialogFragment dialog) { bottomSheetPresenter.show(getSupportFragmentManager(), dialog); } @Override public void onPageDismissBottomSheet() { bottomSheetPresenter.dismiss(getSupportFragmentManager()); } @Nullable @Override public PageToolbarHideHandler onPageGetToolbarHideHandler() { return toolbarHideHandler; } @Override public void onPageLoadPage(@NonNull PageTitle title, @NonNull HistoryEntry entry) { loadPage(title, entry); } @Override public void onPageShowLinkPreview(@NonNull PageTitle title, int source) { showLinkPreview(title, source); } @Override public void onPageLoadMainPageInForegroundTab() { loadMainPageInForegroundTab(); } @Override public void onPageUpdateProgressBar(boolean visible, boolean indeterminate, int value) { updateProgressBar(visible, indeterminate, value); } @Override public boolean onPageIsSearching() { return isSearching(); } @Nullable @Override public Fragment onPageGetTopFragment() { return pageFragment; } @Override public void onPageShowThemeChooser() { bottomSheetPresenter.show(getSupportFragmentManager(), new ThemeChooserDialog()); } @Nullable @Override public ActionMode onPageStartSupportActionMode(@NonNull ActionMode.Callback callback) { return startSupportActionMode(callback); } @Override public void onPageShowToolbar() { showToolbar(); } @Override public void onPageHideSoftKeyboard() { hideSoftKeyboard(); } @Nullable @Override public PageLoadCallbacks onPageGetPageLoadCallbacks() { return pageLoadCallbacks; } @Override public void onPageLoadPage(@NonNull PageTitle title, @NonNull HistoryEntry entry, @NonNull TabPosition tabPosition) { loadPage(title, entry, tabPosition); } @Override public void onPageAddToReadingList(@NonNull PageTitle title, @NonNull AddToReadingListDialog.InvokeSource source) { showAddToListDialog(title, source); } @Override public void onPageRemoveFromReadingLists(@NonNull PageTitle title) { if (!pageFragment.isAdded()) { return; } FeedbackUtil.showMessage(getActivity(), getString(R.string.reading_list_item_deleted, title.getDisplayText())); pageFragment.updateBookmark(); } @Nullable @Override public View onPageGetContentView() { return pageFragment.getView(); } @Nullable @Override public View onPageGetTabsContainerView() { return tabsContainerView; } @Override public void onPagePopFragment() { finish(); } @Override public void onPageInvalidateOptionsMenu() { supportInvalidateOptionsMenu(); } @Override public void onPageSearchRequested() { openSearchFragment(SearchInvokeSource.TOOLBAR, null); } @Override public void onPageLoadError(@NonNull PageTitle title) { getSupportActionBar().setTitle(title.getDisplayText()); } @Override public void onPageLoadErrorRetry() { getSupportActionBar().setTitle(""); } @Override public void onPageLoadErrorBackPressed() { finish(); } @Override public void onSearchSelectPage(@NonNull HistoryEntry entry, boolean inNewTab) { loadPage(entry.getTitle(), entry, inNewTab ? TabsProvider.TabPosition.NEW_TAB_BACKGROUND : TabsProvider.TabPosition.CURRENT_TAB); } @Override public void onSearchOpen() { toolbarContainerView.setVisibility(View.GONE); } @Override public void onSearchClose(boolean launchedFromIntent) { SearchFragment fragment = searchFragment(); if (fragment != null) { closeSearchFragment(fragment); } toolbarContainerView.setVisibility(View.VISIBLE); hideSoftKeyboard(); } @Override public void onSearchResultAddToList(@NonNull PageTitle title, @NonNull AddToReadingListDialog.InvokeSource source) { showAddToListDialog(title, source); } @Override public void onSearchResultShareLink(@NonNull PageTitle title) { ShareUtil.shareText(this, title); } @Override public void onLinkPreviewLoadPage(@NonNull PageTitle title, @NonNull HistoryEntry entry, boolean inNewTab) { loadPage(title, entry, inNewTab ? TabPosition.NEW_TAB_BACKGROUND : TabPosition.CURRENT_TAB); } @Override public void onLinkPreviewCopyLink(@NonNull PageTitle title) { copyLink(title.getCanonicalUri()); showCopySuccessMessage(); } @Override public void onLinkPreviewAddToList(@NonNull PageTitle title) { showAddToListDialog(title, AddToReadingListDialog.InvokeSource.LINK_PREVIEW_MENU); } @Override public void onLinkPreviewShareLink(@NonNull PageTitle title) { ShareUtil.shareText(this, title); } @Override public void onSearchResultCopyLink(@NonNull PageTitle title) { copyLink(title.getCanonicalUri()); showCopySuccessMessage(); } @Override public void wiktionaryShowDialogForTerm(@NonNull String term) { pageFragment.getShareHandler().showWiktionaryDefinition(term); } @Override public boolean shouldLoadFromBackStack() { return getIntent() != null && (ACTION_SHOW_TAB_LIST.equals(getIntent().getAction()) || ACTION_RESUME_READING.equals(getIntent().getAction())); } @Override public boolean shouldShowTabList() { return getIntent() != null && ACTION_SHOW_TAB_LIST.equals(getIntent().getAction()); } private void copyLink(@NonNull String url) { ClipboardUtil.setPlainText(this, null, url); } private void showCopySuccessMessage() { FeedbackUtil.showMessage(this, R.string.address_copied); } @Nullable @Override public AppCompatActivity getActivity() { return this; } private boolean shouldRecreateMainActivity() { return getIntent().getAction() == null || getIntent().getAction().equals(Intent.ACTION_VIEW); } @Override protected void onResume() { super.onResume(); app.resetWikiSite(); app.getSessionFunnel().touchSession(); } @Override public void onPause() { if (isCabOpen()) { // Explicitly close any current ActionMode (see T147191) finishActionMode(); } super.onPause(); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); saveState(outState); } private void saveState(Bundle outState) { outState.putBoolean("isSearching", isSearching()); outState.putString(LANGUAGE_CODE_BUNDLE_KEY, app.getAppOrSystemLanguageCode()); } @Override public void onActivityResult(int requestCode, int resultCode, final Intent data) { if (settingsActivityRequested(requestCode)) { handleSettingsActivityResult(resultCode); } else if (newArticleLanguageSelected(requestCode, resultCode) || galleryPageSelected(requestCode, resultCode)) { handleLangLinkOrPageResult(data); } else { super.onActivityResult(requestCode, resultCode, data); } } private void handleLangLinkOrPageResult(final Intent data) { tabsContainerView.post(new Runnable() { @Override public void run() { handleIntent(data); } }); } @Override protected void onStop() { app.getSessionFunnel().persistSession(); super.onStop(); } @Override public void onDestroy() { if (unbinder != null) { unbinder.unbind(); } unregisterBus(); super.onDestroy(); } /** * ActionMode that is invoked when the user long-presses inside the WebView. * @param mode ActionMode under which this context is starting. */ @Override public void onSupportActionModeStarted(@NonNull ActionMode mode) { if (!isCabOpen()) { conditionallyInjectCustomCabMenu(mode); } super.onSupportActionModeStarted(mode); } @Override public void onSupportActionModeFinished(@NonNull ActionMode mode) { super.onSupportActionModeFinished(mode); nullifyActionMode(); } @Override public void onActionModeStarted(android.view.ActionMode mode) { if (!isCabOpen()) { conditionallyInjectCustomCabMenu(mode); } super.onActionModeStarted(mode); } @Override public void onActionModeFinished(android.view.ActionMode mode) { super.onActionModeFinished(mode); nullifyActionMode(); } private <T> void conditionallyInjectCustomCabMenu(T mode) { currentActionMode = new CompatActionMode(mode); if (currentActionMode.shouldInjectCustomMenu()) { currentActionMode.injectCustomMenu(pageFragment); } } private void registerBus() { bus = app.getBus(); bus.register(busMethods); L.d("Registered bus."); } private void unregisterBus() { if (bus != null) { bus.unregister(busMethods); } bus = null; L.d("Unregistered bus."); } private void handleSettingsActivityResult(int resultCode) { if (languageChanged(resultCode)) { loadNewLanguageMainPage(); } } private boolean settingsActivityRequested(int requestCode) { return requestCode == SettingsActivity.ACTIVITY_REQUEST_SHOW_SETTINGS; } private boolean newArticleLanguageSelected(int requestCode, int resultCode) { return requestCode == Constants.ACTIVITY_REQUEST_LANGLINKS && resultCode == LangLinksActivity.ACTIVITY_RESULT_LANGLINK_SELECT; } private boolean galleryPageSelected(int requestCode, int resultCode) { return requestCode == Constants.ACTIVITY_REQUEST_GALLERY && resultCode == GalleryActivity.ACTIVITY_RESULT_PAGE_SELECTED; } private boolean languageChanged(int resultCode) { return resultCode == SettingsActivity.ACTIVITY_RESULT_LANGUAGE_CHANGED; } /** * Reload the main page in the new language, after delaying for one second in order to: * (1) Make sure that onStart in MainActivity gets called, thus registering the activity for the bus. * (2) Ensure a smooth transition, which is very jarring without a delay. */ private void loadNewLanguageMainPage() { Handler uiThread = new Handler(Looper.getMainLooper()); uiThread.postDelayed(new Runnable() { @Override public void run() { loadMainPageInForegroundTab(); updateFeaturedPageWidget(); } }, DateUtils.SECOND_IN_MILLIS); } /** * Update any instances of our Featured Page widget, since it will change with the currently selected language. */ private void updateFeaturedPageWidget() { Intent widgetIntent = new Intent(this, WidgetProviderFeaturedPage.class); widgetIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); int[] ids = AppWidgetManager.getInstance(getApplication()).getAppWidgetIds( new ComponentName(this, WidgetProviderFeaturedPage.class)); widgetIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids); sendBroadcast(widgetIntent); } private void showDescriptionEditRevertDialog(@NonNull String qNumber) { new AlertDialog.Builder(this) .setTitle(R.string.notification_reverted_title) .setView(new DescriptionEditRevertHelpView(this, qNumber)) .setPositiveButton(android.R.string.ok, null) .create() .show(); } @VisibleForTesting public void setPageLoadCallbacks(@Nullable PageLoadCallbacks pageLoadCallbacks) { this.pageLoadCallbacks = pageLoadCallbacks; } @SuppressLint("CommitTransaction") private void openSearchFragment(@NonNull SearchInvokeSource source, @Nullable String query) { Fragment fragment = searchFragment(); if (fragment == null) { fragment = SearchFragment.newInstance(source, StringUtils.trim(query), true); getSupportFragmentManager() .beginTransaction() .add(R.id.activity_page_container, fragment) .commitNowAllowingStateLoss(); } } @SuppressLint("CommitTransaction") private void closeSearchFragment(@NonNull SearchFragment fragment) { getSupportFragmentManager().beginTransaction().remove(fragment).commitNowAllowingStateLoss(); } @Nullable private SearchFragment searchFragment() { return (SearchFragment) getSupportFragmentManager() .findFragmentById(R.id.activity_page_container); } private class EventBusMethods { @Subscribe public void on(ChangeTextSizeEvent event) { if (pageFragment != null && pageFragment.getWebView() != null) { pageFragment.updateFontSize(); } } } }