Java tutorial
package org.wikipedia.page.shareafact; import android.content.Context; import android.graphics.Bitmap; import android.view.ActionMode; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.JSONObject; import org.wikipedia.R; import org.wikipedia.WikipediaApp; import org.wikipedia.activity.ActivityUtil; import org.wikipedia.analytics.ShareAFactFunnel; import org.wikipedia.bridge.CommunicationBridge; import org.wikipedia.dataclient.ServiceFactory; import org.wikipedia.dataclient.mwapi.MwQueryPage; import org.wikipedia.gallery.ImageLicense; import org.wikipedia.page.Namespace; import org.wikipedia.page.NoDimBottomSheetDialog; import org.wikipedia.page.Page; import org.wikipedia.page.PageFragment; import org.wikipedia.page.PageProperties; import org.wikipedia.page.PageTitle; import org.wikipedia.settings.Prefs; import org.wikipedia.util.FeedbackUtil; import org.wikipedia.util.ShareUtil; import org.wikipedia.util.StringUtil; import org.wikipedia.util.UriUtil; import org.wikipedia.util.log.L; import org.wikipedia.wiktionary.WiktionaryDialog; import java.util.Arrays; import java.util.Locale; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; import static org.wikipedia.analytics.ShareAFactFunnel.ShareMode; /** * Let user choose between sharing as text or as image. */ public class ShareHandler { private static final String PAYLOAD_PURPOSE_KEY = "purpose"; private static final String PAYLOAD_PURPOSE_SHARE = "share"; private static final String PAYLOAD_PURPOSE_DEFINE = "define"; private static final String PAYLOAD_PURPOSE_EDIT_HERE = "edit_here"; private static final String PAYLOAD_TEXT_KEY = "text"; @NonNull private final PageFragment fragment; @NonNull private final CommunicationBridge bridge; @Nullable private ActionMode webViewActionMode; @Nullable private ShareAFactFunnel funnel; private CompositeDisposable disposables = new CompositeDisposable(); private void createFunnel() { WikipediaApp app = WikipediaApp.getInstance(); final Page page = fragment.getPage(); final PageProperties pageProperties = page.getPageProperties(); funnel = new ShareAFactFunnel(app, page.getTitle(), pageProperties.getPageId(), pageProperties.getRevisionId()); } public ShareHandler(@NonNull PageFragment fragment, @NonNull CommunicationBridge bridge) { this.fragment = fragment; this.bridge = bridge; bridge.addListener("onGetTextSelection", (String messageType, JSONObject messagePayload) -> { leaveActionMode(); String purpose = messagePayload.optString(PAYLOAD_PURPOSE_KEY, ""); String text = messagePayload.optString(PAYLOAD_TEXT_KEY, ""); switch (purpose) { case PAYLOAD_PURPOSE_SHARE: if (funnel == null) { createFunnel(); } shareSnippet(text); funnel.logShareTap(text); break; case PAYLOAD_PURPOSE_DEFINE: showWiktionaryDefinition(text.toLowerCase(Locale.getDefault())); break; case PAYLOAD_PURPOSE_EDIT_HERE: onEditHerePayload(messagePayload.optInt("sectionID", 0), text, messagePayload.optBoolean("editDescription", false)); break; default: L.d("Unknown purpose=" + purpose); } }); } public void dispose() { disposables.clear(); } public void showWiktionaryDefinition(String text) { PageTitle title = fragment.getTitle(); fragment.showBottomSheet(WiktionaryDialog.newInstance(title, text)); } private void onEditHerePayload(int sectionID, String text, boolean isEditingDescription) { if (sectionID == 0 && isEditingDescription) { fragment.verifyBeforeEditingDescription(text); } else { if (sectionID >= 0) { fragment.getEditHandler().startEditingSection(sectionID, text); } } } private void shareSnippet(@NonNull CharSequence input) { final String selectedText = StringUtil.sanitizeText(input.toString()); final PageTitle title = fragment.getTitle(); final String leadImageNameText = fragment.getPage().getPageProperties().getLeadImageName() != null ? fragment.getPage().getPageProperties().getLeadImageName() : ""; final PageTitle imageTitle = new PageTitle(Namespace.FILE.toLegacyString(), leadImageNameText, title.getWikiSite()); disposables.add(ServiceFactory.get(title.getWikiSite()).getImageExtMetadata(imageTitle.getPrefixedText()) .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).map(response -> { // noinspection ConstantConditions MwQueryPage page = response.query().pages().get(0); return page.imageInfo() != null && page.imageInfo().getMetadata() != null ? new ImageLicense(page.imageInfo().getMetadata()) : new ImageLicense(); }).subscribe(imageLicense -> { final Bitmap snippetBitmap = SnippetImage.getSnippetImage(fragment.requireContext(), fragment.getLeadImageBitmap(), title.getDisplayText(), fragment.getPage().isMainPage() ? "" : StringUtils.capitalize(title.getDescription()), selectedText, imageLicense); fragment.showBottomSheet( new PreviewDialog(fragment.getContext(), snippetBitmap, title, selectedText, funnel)); }, caught -> { // If we failed to get license info for the lead image, just share the text PreviewDialog.shareAsText(fragment.requireContext(), title, selectedText, funnel); L.e("Error fetching image license info for " + title.getDisplayText(), caught); })); } /** * @param mode ActionMode under which this context is starting. */ public void onTextSelected(ActionMode mode) { webViewActionMode = mode; Menu menu = mode.getMenu(); MenuItem shareItem = menu.findItem(R.id.menu_text_select_share); if (Prefs.isShareTutorialEnabled()) { postShowShareToolTip(shareItem); Prefs.setShareTutorialEnabled(false); } // Provide our own listeners for the copy, define, and share buttons. shareItem.setOnMenuItemClickListener(new RequestTextSelectOnMenuItemClickListener(PAYLOAD_PURPOSE_SHARE)); MenuItem copyItem = menu.findItem(R.id.menu_text_select_copy); copyItem.setOnMenuItemClickListener((MenuItem menuItem) -> { fragment.getWebView().copyToClipboard(); FeedbackUtil.showMessage(fragment.getActivity(), R.string.text_copied); leaveActionMode(); return true; }); MenuItem defineItem = menu.findItem(R.id.menu_text_select_define); if (shouldEnableWiktionaryDialog()) { defineItem.setVisible(true); defineItem.setOnMenuItemClickListener( new RequestTextSelectOnMenuItemClickListener(PAYLOAD_PURPOSE_DEFINE)); } MenuItem editItem = menu.findItem(R.id.menu_text_edit_here); editItem.setOnMenuItemClickListener( new RequestTextSelectOnMenuItemClickListener(PAYLOAD_PURPOSE_EDIT_HERE)); if (!fragment.getPage().isArticle()) { editItem.setVisible(false); } if (funnel == null) { createFunnel(); } funnel.logHighlight(); } private boolean shouldEnableWiktionaryDialog() { return Prefs.useRestBase() && isWiktionaryDialogEnabledForArticleLanguage(); } private boolean isWiktionaryDialogEnabledForArticleLanguage() { return Arrays.asList(WiktionaryDialog.getEnabledLanguages()) .contains(fragment.getTitle().getWikiSite().languageCode()); } private void postShowShareToolTip(final MenuItem shareItem) { fragment.getView().post(() -> { View shareItemView = ActivityUtil.getMenuItemView(fragment.requireActivity(), shareItem); if (shareItemView != null) { FeedbackUtil.showTapTargetView(fragment.requireActivity(), shareItemView, R.string.share, R.string.tool_tip_share, null); } }); } private void leaveActionMode() { if (hasWebViewActionMode()) { finishWebViewActionMode(); nullifyWebViewActionMode(); } } private boolean hasWebViewActionMode() { return webViewActionMode != null; } private void nullifyWebViewActionMode() { webViewActionMode = null; } private void finishWebViewActionMode() { webViewActionMode.finish(); } private class RequestTextSelectOnMenuItemClickListener implements MenuItem.OnMenuItemClickListener { @NonNull private final String purpose; RequestTextSelectOnMenuItemClickListener(@NonNull String purpose) { this.purpose = purpose; } @Override public boolean onMenuItemClick(MenuItem item) { // send an event to the WebView that will make it return the // selected text (or first paragraph) back to us... try { JSONObject payload = new JSONObject(); payload.put(PAYLOAD_PURPOSE_KEY, purpose); bridge.sendMessage("getTextSelection", payload); } catch (JSONException e) { throw new RuntimeException(e); } return true; } } /** * A dialog to be displayed before sharing with two action buttons: * "Share as image", "Share as text". */ private static class PreviewDialog extends NoDimBottomSheetDialog { private boolean completed = false; PreviewDialog(final Context context, final Bitmap resultBitmap, final PageTitle title, final String selectedText, final ShareAFactFunnel funnel) { super(context); View rootView = LayoutInflater.from(context).inflate(R.layout.dialog_share_preview, null); setContentView(rootView); ImageView previewImage = rootView.findViewById(R.id.preview_img); previewImage.setImageBitmap(resultBitmap); rootView.findViewById(R.id.close_button).setOnClickListener((v) -> dismiss()); rootView.findViewById(R.id.share_as_image_button).setOnClickListener((v) -> { String introText = context.getString(R.string.snippet_share_intro, title.getDisplayText(), UriUtil.getUrlWithProvenance(context, title, R.string.prov_share_image)); ShareUtil.shareImage(context, resultBitmap, title.getDisplayText(), title.getDisplayText(), introText); funnel.logShareIntent(selectedText, ShareMode.image); completed = true; }); rootView.findViewById(R.id.share_as_text_button).setOnClickListener((v) -> { shareAsText(context, title, selectedText, funnel); completed = true; }); setOnDismissListener((dialog) -> { resultBitmap.recycle(); if (!completed) { funnel.logAbandoned(title.getDisplayText()); } }); startExpanded(); } static void shareAsText(@NonNull Context context, @NonNull PageTitle title, @NonNull String selectedText, @Nullable ShareAFactFunnel funnel) { String introText = context.getString(R.string.snippet_share_intro, title.getDisplayText(), UriUtil.getUrlWithProvenance(context, title, R.string.prov_share_text)); ShareUtil.shareText(context, title.getDisplayText(), constructShareText(selectedText, introText)); if (funnel != null) { funnel.logShareIntent(selectedText, ShareMode.text); } } private static String constructShareText(String selectedText, String introText) { return selectedText + "\n\n" + introText; } } }