@Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_toc_entry, parent, false); } Section section = (Section) getItem(position); TextView sectionHeading = (TextView) convertView.findViewById(R.id.page_toc_item_text); View sectionFiller = convertView.findViewById(R.id.page_toc_filler); LinearLayout.LayoutParams indentLayoutParameters = new LinearLayout.LayoutParams(sectionFiller.getLayoutParams()); indentLayoutParameters.width = (section.getLevel() - 1) * (int) (INDENTATION_WIDTH_DP * WikipediaApp.getInstance().getScreenDensity()); sectionFiller.setLayoutParams(indentLayoutParameters); sectionHeading.setText(Html.fromHtml(section.getHeading())); if (section.getLevel() > 1) { sectionHeading.setTextColor( WikipediaApp.getInstance() .getResources() .getColor(getThemedAttributeId(parentActivity, R.attr.toc_subsection_text_color))); } else { sectionHeading.setTextColor( WikipediaApp.getInstance() .getResources() .getColor(getThemedAttributeId(parentActivity, R.attr.toc_section_text_color))); } return convertView; }
public void logDone() { log( "pageID", pageId, "pageHeight", (int) (pageHeight / app.getScreenDensity()), "scrollFluxDown", (int) (scrollFluxDown / app.getScreenDensity()), "scrollFluxUp", (int) (scrollFluxUp / app.getScreenDensity()), "maxPercentViewed", getMaxPercentViewed()); }
private void displayLeadSection() { try { final Page page = model.getPage(); final PageProperties pageProperties = page.getPageProperties(); JSONObject marginPayload = new JSONObject(); int margin = DimenUtil.roundedPxToDp(activity.getResources().getDimension(R.dimen.content_margin)); marginPayload.put("marginLeft", margin); marginPayload.put("marginRight", margin); bridge.sendMessage("setMargins", marginPayload); JSONObject leadSectionPayload = new JSONObject(); leadSectionPayload.put("sequence", pageSequenceNum); leadSectionPayload.put("title", page.getDisplayTitle()); leadSectionPayload.put("section", page.getSections().get(0).toJSON()); leadSectionPayload.put( "string_page_similar_titles", activity.getString(R.string.page_similar_titles)); leadSectionPayload.put("string_page_issues", activity.getString(R.string.button_page_issues)); leadSectionPayload.put("string_table_infobox", activity.getString(R.string.table_infobox)); leadSectionPayload.put("string_table_other", activity.getString(R.string.table_other)); leadSectionPayload.put("string_table_close", activity.getString(R.string.table_close)); leadSectionPayload.put("string_expand_refs", activity.getString(R.string.expand_refs)); leadSectionPayload.put("isBeta", app.getReleaseType() != WikipediaApp.RELEASE_PROD); leadSectionPayload.put("siteLanguage", model.getTitle().getSite().getLanguageCode()); leadSectionPayload.put("isMainPage", page.isMainPage()); leadSectionPayload.put("apiLevel", Build.VERSION.SDK_INT); bridge.sendMessage("displayLeadSection", leadSectionPayload); Log.d(TAG, "Sent message 'displayLeadSection' for page: " + page.getDisplayTitle()); Utils.setupDirectionality( model.getTitle().getSite().getLanguageCode(), Locale.getDefault().getLanguage(), bridge); // Hide edit pencils if anon editing is disabled by remote killswitch or if this is a file // page JSONObject miscPayload = new JSONObject(); boolean isAnonEditingDisabled = app.getRemoteConfig().getConfig().optBoolean("disableAnonEditing", false) && !app.getUserInfoStorage().isLoggedIn(); miscPayload.put("noedit", (isAnonEditingDisabled || page.isFilePage() || page.isMainPage())); miscPayload.put("protect", !pageProperties.canEdit()); bridge.sendMessage("setPageProtected", miscPayload); } catch (JSONException e) { // This should never happen throw new RuntimeException(e); } if (webView.getVisibility() != View.VISIBLE) { webView.setVisibility(View.VISIBLE); } refreshView.setRefreshing(false); activity.updateProgressBar(true, true, 0); }
/** * Called when an exception is thrown in the background process. Checks whether the exception is * an SSLException and, if so, prompts user to try again if the SSLException is the first. * * <p>Called on the UI Thread. * * <p>Default implementation just throws it as a RuntimeException, so exceptions are never * swallowed. Unless specific exceptions are handled. * * @param caught The exception that was thrown. */ @Override public void onCatch(Throwable caught) { if (Utils.throwableContainsSpecificType(caught, SSLException.class) && WikipediaApp.getInstance().incSslFailCount() < 2) { WikipediaApp.getInstance().setSslFallback(true); if (!isCancelled()) { NetworkUtils.toastNetworkFail(); } cancel(); return; } throw new RuntimeException(caught); }
private void refreshOneSavedPage(@NonNull final PageTitle title) { getApiService(title) .pageCombo( title.getPrefixedText(), !WikipediaApp.getInstance().isImageDownloadEnabled(), new SaveOtherPageCallback(title) { @Override protected void onComplete() { if (!progressDialog.isShowing()) { isRefreshCancelled = true; // no longer attached to activity! return; } savedPagesCompleted++; progressDialog.setProgress(savedPagesCompleted); L.d("Count is " + savedPagesCompleted + " of " + savedPages.size()); if (savedPagesCompleted == savedPages.size()) { progressDialog.dismiss(); } } @Override protected void onError() { isRefreshCancelled = true; if (!progressDialog.isShowing()) { // no longer attached to activity! return; } progressDialog.dismiss(); getErrorDialog().show(); } }); }
public abstract class ApiTask<T> extends SaneAsyncTask<T> { private static final boolean VERBOSE = WikipediaApp.getInstance().isDevRelease(); private final Api api; public ApiTask(int concurrency, Api api) { super(concurrency); this.api = api; } @Override public T performTask() throws Throwable { RequestBuilder request = buildRequest(api); if (VERBOSE) { Log.v("ApiTask", buildUrl(api.getApiUrl().toString(), request.getParams())); } ApiResult result = makeRequest(request); return processResult(result); } /** * Called when an exception is thrown in the background process. Checks whether the exception is * an SSLException and, if so, prompts user to try again if the SSLException is the first. * * <p>Called on the UI Thread. * * <p>Default implementation just throws it as a RuntimeException, so exceptions are never * swallowed. Unless specific exceptions are handled. * * @param caught The exception that was thrown. */ @Override public void onCatch(Throwable caught) { if (Utils.throwableContainsSpecificType(caught, SSLException.class) && WikipediaApp.getInstance().incSslFailCount() < 2) { WikipediaApp.getInstance().setSslFallback(true); if (!isCancelled()) { NetworkUtils.toastNetworkFail(); } cancel(); return; } throw new RuntimeException(caught); } protected ApiResult makeRequest(RequestBuilder builder) throws ApiException { return builder.get(); } public abstract RequestBuilder buildRequest(Api api); public abstract T processResult(ApiResult result) throws Throwable; private String buildUrl(String url, Map<String, String> params) { Uri.Builder builder = new Uri.Builder().encodedPath(url); for (Map.Entry<String, String> param : params.entrySet()) { builder.appendQueryParameter(param.getKey(), param.getValue()); } return builder.build().toString(); } }
private void setState(int state, int subState) { if (!fragment.isAdded()) { return; } this.state = state; this.subState = subState; activity.supportInvalidateOptionsMenu(); // FIXME: Move this out into a PageComplete event of sorts if (state == STATE_COMPLETE_FETCH) { fragment.setupToC(model, isFirstPage()); // add the page to cache! if (cacheOnComplete) { app.getPageCache() .put( model.getTitleOriginal(), model.getPage(), new PageCache.CachePutListener() { @Override public void onPutComplete() {} @Override public void onPutError(Throwable e) { Log.e(TAG, "Failed to add page to cache.", e); } }); } } }
@Override public void onCreate() { super.onCreate(); ACRA.init(this); bus = new Bus(); final Resources resources = getResources(); ViewAnimations.init(resources); screenDensity = resources.getDisplayMetrics().density; PrefKeys.initPreferenceKeys(resources); // Enable debugging on the webview if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { WebView.setWebContentsDebuggingEnabled(true); } Api.setConnectionFactory(new OkHttpConnectionFactory(this)); if (WikipediaApp.isWikipediaZeroDevmodeOn()) { IntentFilter connFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); connChangeReceiver = new ConnectionChangeReceiver(); this.registerReceiver(connChangeReceiver, connFilter); } try { appVersionString = getPackageManager().getPackageInfo(getPackageName(), 0).versionName; } catch (PackageManager.NameNotFoundException e) { // This will never happen! throw new RuntimeException(e); } new PerformMigrationsTask().execute(); }
public PageScrollFunnel(WikipediaApp app, int pageId) { super( app, SCHEMA_NAME, REV_ID, app.isProdRelease() ? Funnel.SAMPLE_LOG_100 : Funnel.SAMPLE_LOG_ALL); this.app = app; this.pageId = pageId; }
public void setupToC(final Page page, Site site, boolean firstPage) { tocProgress.setVisibility(View.GONE); tocList.setVisibility(View.VISIBLE); headerView.setText(Html.fromHtml(page.getDisplayTitle())); headerView.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { scrollToSection(page.getSections().get(0)); wasClicked = true; funnel.logClick(0, page.getTitle().getDisplayText()); hide(); } }); tocList.setAdapter(new ToCAdapter(page), site.getLanguageCode()); tocList.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Section section = (Section) parent.getAdapter().getItem(position); scrollToSection(section); wasClicked = true; funnel.logClick(position, section.getHeading()); hide(); } }); funnel = new ToCInteractionFunnel( WikipediaApp.getInstance(), site, page.getPageProperties().getPageId(), tocList.getAdapter().getCount()); if (!page.isMainPage() && !firstPage) { if (WikipediaApp.getInstance().getOnboardingStateMachine().isTocTutorialEnabled()) { showTocOnboarding(); } } }
@RunWith(AndroidJUnit4.class) public class LoginTaskTest { private static final Site TEST_WIKI_SITE = new Site("test.wikipedia.org"); private static final String SUCCESS = "Success"; private static final String USERNAME = getString(R.string.test_username); private static final String PASSWORD = getString(R.string.test_password); private final WikipediaApp app = WikipediaApp.getInstance(); private final TestLatch completionLatch = new TestLatch(); @Test public void testLogin() throws Throwable { runOnMainSync( new Runnable() { @Override public void run() { loginTestTask.execute(); } }); completionLatch.await(); } private LoginTask loginTestTask = new LoginTask(app, TEST_WIKI_SITE, USERNAME, PASSWORD) { @Override public void onFinish(LoginResult result) { super.onFinish(result); assertThat(result.getCode(), equalTo(SUCCESS)); app.getEditTokenStorage().get(TEST_WIKI_SITE, callback); } }; private EditTokenStorage.TokenRetrievedCallback callback = new EditTokenStorage.TokenRetrievedCallback() { @Override public void onTokenRetrieved(String token) { assertThat(token.equals("+\\"), is(false)); completionLatch.countDown(); } @Override public void onTokenFailed(Throwable caught) { throw new RuntimeException(caught); } }; private void runOnMainSync(Runnable r) { getInstrumentation().runOnMainSync(r); } private static String getString(@StringRes int id) { return getInstrumentation().getContext().getString(id); } }
public ToCHandler( final AppCompatActivity activity, final WikiDrawerLayout slidingPane, final CommunicationBridge bridge) { this.parentActivity = activity; this.bridge = bridge; this.slidingPane = slidingPane; this.tocList = (ConfigurableListView) slidingPane.findViewById(R.id.page_toc_list); ((FrameLayout.LayoutParams) tocList.getLayoutParams()) .setMargins(0, getContentTopOffsetPx(activity), 0, 0); this.tocProgress = (ProgressBar) slidingPane.findViewById(R.id.page_toc_in_progress); bridge.addListener( "currentSectionResponse", new CommunicationBridge.JSEventListener() { @Override public void onMessage(String messageType, JSONObject messagePayload) { int sectionID = messagePayload.optInt("sectionID"); Log.d("Wikipedia", "current section is " + sectionID); if (tocList.getAdapter() == null) { return; } int itemToSelect = 0; // Find the list item that corresponds to the returned sectionID. // Start with index 1 of the list adapter, since index 0 is the header view, // and won't have a Section object associated with it. // And end with the second-to-last section, since the last section is the // artificial Read More section, and unknown to the WebView. // The lead section (id 0) will automatically fall through the loop. for (int i = 1; i < tocList.getAdapter().getCount() - 1; i++) { if (((Section) tocList.getAdapter().getItem(i)).getId() <= sectionID) { itemToSelect = i; } else { break; } } tocList.setItemChecked(itemToSelect, true); tocList.smoothScrollToPosition(itemToSelect); } }); headerView = (TextView) LayoutInflater.from(tocList.getContext()) .inflate(R.layout.header_toc_list, null, false); tocList.addHeaderView(headerView); // create a dummy funnel, in case the drawer is pulled out before a page is loaded. funnel = new ToCInteractionFunnel( WikipediaApp.getInstance(), WikipediaApp.getInstance().getSite(), 0, 0); slidingPane.setDrawerListener( new DrawerLayout.SimpleDrawerListener() { private boolean sectionRequested = false; @Override public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); parentActivity.supportInvalidateOptionsMenu(); funnel.logOpen(); wasClicked = false; } @Override public void onDrawerClosed(View drawerView) { super.onDrawerClosed(drawerView); parentActivity.supportInvalidateOptionsMenu(); if (!wasClicked) { funnel.logClose(); } sectionRequested = false; } @Override public void onDrawerSlide(View drawerView, float slideOffset) { super.onDrawerSlide(drawerView, slideOffset); // make sure the ActionBar is showing ((PageActivity) parentActivity).showToolbar(); ((PageActivity) parentActivity) .getSearchBarHideHandler() .setForceNoFade(slideOffset != 0); // request the current section to highlight, if we haven't yet if (!sectionRequested) { bridge.sendMessage("requestCurrentSection", new JSONObject()); sectionRequested = true; } } }); }
private void showTocOnboarding() { View tocButton = parentActivity.findViewById(R.id.floating_toc_button); ToolTipUtil.showToolTip( parentActivity, tocButton, R.layout.inflate_tool_tip_toc_button, ToolTip.Position.CENTER); WikipediaApp.getInstance().getOnboardingStateMachine().setTocTutorial(); }
public RbPageService(final Site site) { responseHeaderHandler = WikipediaApp.getInstance().getWikipediaZeroHandler(); webService = RbPageEndpointsCache.INSTANCE.getRbPageEndpoints(site); }
// TODO: use getResources().getDimensionPixelSize()? Define leadImageWidth with px, not dp? public static int calculateLeadImageWidth() { Resources res = WikipediaApp.getInstance().getResources(); return (int) (res.getDimension(R.dimen.leadImageWidth) / res.getDisplayMetrics().density); }
@Override public void onFinish(LoginResult result) { super.onFinish(result); assertThat(result.getCode(), equalTo(SUCCESS)); app.getEditTokenStorage().get(TEST_WIKI_SITE, callback); }
public void setPageHeight(int height) { this.pageHeight = (int) (height * app.getScreenDensity()); }
private void loadPageOnWebViewReady(boolean tryFromCache) { // stage any section-specific link target from the title, since the title may be // replaced (normalized) sectionTargetFromTitle = model.getTitle().getFragment(); Utils.setupDirectionality( model.getTitle().getSite().getLanguageCode(), Locale.getDefault().getLanguage(), bridge); // hide the native top and bottom components... leadImagesHandler.hide(); bottomContentHandler.hide(); bottomContentHandler.setTitle(model.getTitle()); if (model.getCurEntry().getSource() == HistoryEntry.SOURCE_SAVED_PAGE) { state = STATE_NO_FETCH; loadSavedPage(); } else if (tryFromCache) { // is this page in cache?? app.getPageCache() .get( model.getTitleOriginal(), pageSequenceNum, new PageCache.CacheGetListener() { @Override public void onGetComplete(Page page, int sequence) { if (sequence != pageSequenceNum) { return; } if (page != null) { Log.d( TAG, "Using page from cache: " + model.getTitleOriginal().getDisplayText()); model.setPage(page); model.setTitle(page.getTitle()); // load the current title's thumbnail from sqlite updateThumbnail( PageImage.PERSISTENCE_HELPER.getImageUrlForTitle(app, model.getTitle())); // Save history entry... new SaveHistoryTask(model.getCurEntry(), app).execute(); // don't re-cache the page after loading. cacheOnComplete = false; state = STATE_COMPLETE_FETCH; setState(state); performActionForState(state); } else { // page isn't in cache, so fetch it from the network... loadPageFromNetwork(); } } @Override public void onGetError(Throwable e, int sequence) { Log.e(TAG, "Failed to get page from cache.", e); if (sequence != pageSequenceNum) { return; } // something failed when loading it from cache, so fetch it from network... loadPageFromNetwork(); } }); } else { loadPageFromNetwork(); } }