Skip to content

Commit c0c935f

Browse files
committed
Squashed 'libs/editor/' changes from 6647346..d99856b
d99856b Merge branch 'merge-wpandroid' into develop aee093e Implemented missing onAuthHeaderRequested method in the example app fdccfc1 Hide video fullscreen button 6fd8591 Pause video playback when the WebView is no longer visible to the user 84da174 Use a blank placeholder src instead of videopress.mp4 to trigger videopress video loading 435c012 Added some sanity checks for substring usage to JsCallbackReceiver 6c4d042 Fixed a bug where videos would sometimes get deleted if they were the only thing in the post d1aeaca Added support for video shortcodes on non-VideoPress sites 9f1b954 Merge branch 'feature/visual-editor' into feature/editor-video-playback a61fbf9 Added missing bracket in JS editor 040ceb8 Follow redirects when requesting thumbnails for the image settings dialog 4fbd821 Always defer to super.shouldInterceptRequest in editor WebView unless an auth header is present c933ee8 Merge branch 'feature/visual-editor' into feature/editor-video-playback 10a08fa Merge pull request #273 from wordpress-mobile/issue/15-track-editor-evnts bce6d92 Merge branch 'develop' into issue/15-track-editor-evnts 3e2c261 When looking up a VideoPress ID in the DB fails, refresh the blog media and check again c803d21 Remove the onError attribute from VideoPress videos if an empty video url is returned 4ca4c4c Added the ability to tap a placeholder VideoPress video to attempt to load it 1c638bf Don't use the placeholder poster image for videopress videos e9e8f7f Implemented missing method in example app d8a8510 fix typo in event name b13ac66 Track EDITOR_EDITED_IMAGE da3cd0c Add space after videos when converting from VideoPress shortcode ca6499f track most editor events 74b9932 use wp-analytics 1.1.0 1964844 Only apply custom headers for network URL resource requests b3dd7e3 Implement new EditorFragmentListener method in mock activity for integration tests fee3ffb Convert VideoPress shortcodes to video elements and back in the visual editor 9d56f49 Imported ZSSRichTextEditor video methods and callback methods from iOS 5b9a4c3 Imported video CSS from iOS 5bbc482 Added missing method implementation to test mock activity 77f7096 Retrieve an auth header if necessary when launching the image settings dialog 1214827 Use the safeToAddWordPressComAuthHeader utility method for resource requests in the new editor's WebView b2e2b74 Updated the utils artifact version for the editor 12f748a Moved setupUrlConnection() to the utils library 108a24c Renamed EditorFragment's setWebViewHeader to setCustomHttpHeader 1ac139e Use custom headers for image settings dialog image requests 542db23 Extracted some duplicated HttpURLConnection building code into a utility method 96984f0 When an Authorization HTTP header is detected, force using the HTTPS protocol in the WebViewClient 3302ab9 Added support for custom headers to the editor's WebView, set through the EditorFragmentAbstract 02ce3e4 Editor 0.5 version bump 190a822 Added full support for gallery types 25b3632 Added a fix enabling galleries to be added to the editor before the DOM loads acd7f9c Fixed creating a new post by making a gallery from the Media Library (the gallery wasn't being added to the new post) 1b6d47c Merge branch 'feature/visual-editor' into feature/editor-galleries b1cb369 Added support for creating galleries from local images, using a placeholder shortcode while uploading 92a3359 Extracted duplicate paragraph-wrapping code into a separate method in the ZSS editor 26d93c0 Added support for inserting galleries using media library images git-subtree-dir: libs/editor git-subtree-split: d99856bfb3e35e5c86f74af3f517739749e923ea
1 parent 2467f23 commit c0c935f

File tree

17 files changed

+988
-44
lines changed

17 files changed

+988
-44
lines changed

WordPressEditor/build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ android {
2323
buildToolsVersion "23.0.2"
2424

2525
defaultConfig {
26-
versionCode 4
27-
versionName "0.4"
26+
versionCode 5
27+
versionName "0.5"
2828
minSdkVersion 14
2929
targetSdkVersion 23
3030
}
@@ -48,8 +48,8 @@ android {
4848
dependencies {
4949
compile 'com.android.support:appcompat-v7:23.1.1'
5050
compile 'com.android.support:support-v4:23.1.1'
51-
compile 'org.wordpress:analytics:1.0.0'
52-
compile 'org.wordpress:utils:1.6.0'
51+
compile 'org.wordpress:analytics:1.2.0'
52+
compile 'org.wordpress:utils:1.7.0'
5353

5454
// Test libraries
5555
testCompile 'junit:junit:4.11'

WordPressEditor/src/androidTest/java/org.wordpress.android.editor/MockEditorActivity.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@ public void onFeaturedImageChanged(int mediaId) {
6161

6262
}
6363

64+
@Override
65+
public void onVideoPressInfoRequested(String videoId) {
66+
67+
}
68+
69+
@Override
70+
public String onAuthHeaderRequested(String url) {
71+
return "";
72+
}
73+
6474
@Override
6575
public void saveMediaFile(MediaFile mediaFile) {
6676

WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragment.java

Lines changed: 146 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,21 @@
3030

3131
import com.android.volley.toolbox.ImageLoader;
3232

33+
import org.json.JSONException;
3334
import org.json.JSONObject;
35+
import org.wordpress.android.analytics.AnalyticsTracker;
36+
import org.wordpress.android.analytics.AnalyticsTracker.Stat;
3437
import org.wordpress.android.util.AppLog;
3538
import org.wordpress.android.util.AppLog.T;
3639
import org.wordpress.android.util.JSONUtils;
3740
import org.wordpress.android.util.StringUtils;
3841
import org.wordpress.android.util.ToastUtils;
42+
import org.wordpress.android.util.UrlUtils;
3943
import org.wordpress.android.util.helpers.MediaFile;
4044
import org.wordpress.android.util.helpers.MediaGallery;
4145

4246
import java.util.ArrayList;
47+
import java.util.Collections;
4348
import java.util.HashMap;
4449
import java.util.HashSet;
4550
import java.util.Locale;
@@ -50,7 +55,8 @@
5055
import java.util.concurrent.TimeUnit;
5156

5257
public class EditorFragment extends EditorFragmentAbstract implements View.OnClickListener, View.OnTouchListener,
53-
OnJsEditorStateChangedListener, OnImeBackListener, EditorMediaUploadListener {
58+
OnJsEditorStateChangedListener, OnImeBackListener, EditorWebViewAbstract.AuthHeaderRequestListener,
59+
EditorMediaUploadListener {
5460
private static final String ARG_PARAM_TITLE = "param_title";
5561
private static final String ARG_PARAM_CONTENT = "param_content";
5662

@@ -87,8 +93,10 @@ public class EditorFragment extends EditorFragmentAbstract implements View.OnCli
8793
private boolean mHideActionBarOnSoftKeyboardUp = false;
8894

8995
private ConcurrentHashMap<String, MediaFile> mWaitingMediaFiles;
96+
private Set<MediaGallery> mWaitingGalleries;
9097
private Set<String> mUploadingMediaIds;
9198
private Set<String> mFailedMediaIds;
99+
private MediaGallery mUploadingMediaGallery;
92100

93101
private String mJavaScriptResult = "";
94102

@@ -126,6 +134,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
126134
}
127135

128136
mWaitingMediaFiles = new ConcurrentHashMap<>();
137+
mWaitingGalleries = Collections.newSetFromMap(new ConcurrentHashMap<MediaGallery, Boolean>());
129138
mUploadingMediaIds = new HashSet<>();
130139
mFailedMediaIds = new HashSet<>();
131140

@@ -135,6 +144,13 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
135144

136145
mWebView.setOnTouchListener(this);
137146
mWebView.setOnImeBackListener(this);
147+
mWebView.setAuthHeaderRequestListener(this);
148+
149+
if (mCustomHttpHeaders != null && mCustomHttpHeaders.size() > 0) {
150+
for (Map.Entry<String, String> entry : mCustomHttpHeaders.entrySet()) {
151+
mWebView.setCustomHeader(entry.getKey(), entry.getValue());
152+
}
153+
}
138154

139155
// Ensure that the content field is always filling the remaining screen space
140156
mWebView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@@ -222,6 +238,14 @@ public void onDetach() {
222238
super.onDetach();
223239
}
224240

241+
@Override
242+
public void setUserVisibleHint(boolean isVisibleToUser) {
243+
if (mDomHasLoaded) {
244+
mWebView.notifyVisibilityChanged(isVisibleToUser);
245+
}
246+
super.setUserVisibleHint(isVisibleToUser);
247+
}
248+
225249
@Override
226250
public void onSaveInstanceState(Bundle outState) {
227251
outState.putCharSequence(KEY_TITLE, getTitle());
@@ -365,6 +389,8 @@ protected void initJsEditor() {
365389
public void onClick(View v) {
366390
int id = v.getId();
367391
if (id == R.id.format_bar_button_html) {
392+
AnalyticsTracker.track(Stat.EDITOR_TAPPED_HTML);
393+
368394
// Don't switch to HTML mode if currently uploading media
369395
if (!mUploadingMediaIds.isEmpty()) {
370396
((ToggleButton) v).setChecked(false);
@@ -405,6 +431,7 @@ public void onClick(View v) {
405431
mWebView.execJavaScriptFromString("ZSSEditor.getField('zss_field_content').focus();");
406432
}
407433
} else if (id == R.id.format_bar_button_media) {
434+
AnalyticsTracker.track(Stat.EDITOR_TAPPED_IMAGE);
408435
((ToggleButton) v).setChecked(false);
409436

410437
if (mSourceView.getVisibility() == View.VISIBLE) {
@@ -419,8 +446,10 @@ public void onClick(View v) {
419446
if (!((ToggleButton) v).isChecked()) {
420447
// The link button was checked when it was pressed; remove the current link
421448
mWebView.execJavaScriptFromString("ZSSEditor.unlink();");
449+
AnalyticsTracker.track(Stat.EDITOR_TAPPED_UNLINK);
422450
return;
423451
}
452+
AnalyticsTracker.track(Stat.EDITOR_TAPPED_LINK);
424453

425454
((ToggleButton) v).setChecked(false);
426455

@@ -479,6 +508,11 @@ public void onImeBack() {
479508
showActionBarIfNeeded();
480509
}
481510

511+
@Override
512+
public String onAuthHeaderRequested(String url) {
513+
return mEditorFragmentListener.onAuthHeaderRequested(url);
514+
}
515+
482516
@Override
483517
public void onActivityResult(int requestCode, int resultCode, Intent data) {
484518
super.onActivityResult(requestCode, resultCode, data);
@@ -706,19 +740,49 @@ public void run() {
706740
if (URLUtil.isNetworkUrl(mediaUrl)) {
707741
String mediaId = mediaFile.getMediaId();
708742
mWebView.execJavaScriptFromString("ZSSEditor.insertImage('" + mediaUrl + "', '" + mediaId + "');");
743+
AnalyticsTracker.track(Stat.EDITOR_ADDED_PHOTO_VIA_WP_MEDIA_LIBRARY);
709744
} else {
710745
String id = mediaFile.getMediaId();
711746
mWebView.execJavaScriptFromString("ZSSEditor.insertLocalImage(" + id + ", '" + mediaUrl + "');");
712747
mWebView.execJavaScriptFromString("ZSSEditor.setProgressOnImage(" + id + ", " + 0 + ");");
713748
mUploadingMediaIds.add(id);
749+
AnalyticsTracker.track(Stat.EDITOR_ADDED_PHOTO_VIA_LOCAL_LIBRARY);
714750
}
715751
}
716752
});
717753
}
718754

719755
@Override
720756
public void appendGallery(MediaGallery mediaGallery) {
721-
// TODO
757+
if (!mDomHasLoaded) {
758+
// If the DOM hasn't loaded yet, we won't be able to add a gallery to the ZSSEditor
759+
// Place it in a queue to be handled when the DOM loaded callback is received
760+
mWaitingGalleries.add(mediaGallery);
761+
return;
762+
}
763+
764+
if (mediaGallery.getIds().isEmpty()) {
765+
mUploadingMediaGallery = mediaGallery;
766+
mWebView.execJavaScriptFromString("ZSSEditor.insertLocalGallery('" + mediaGallery.getUniqueId() + "');");
767+
} else {
768+
// Ensure that the content field is in focus (it may not be if we're adding a gallery to a new post by a
769+
// share action and not via the format bar button)
770+
mWebView.execJavaScriptFromString("ZSSEditor.getField('zss_field_content').focus();");
771+
772+
mWebView.execJavaScriptFromString("ZSSEditor.insertGallery('" + mediaGallery.getIdsStr() + "', '" +
773+
mediaGallery.getType() + "', " + mediaGallery.getNumColumns() + ");");
774+
}
775+
}
776+
777+
@Override
778+
public void setUrlForVideoPressId(final String videoId, final String videoUrl, final String posterUrl) {
779+
mWebView.post(new Runnable() {
780+
@Override
781+
public void run() {
782+
mWebView.execJavaScriptFromString("ZSSEditor.setVideoPressLinks('" + videoId + "', '" +
783+
videoUrl + "', '" + posterUrl + "');");
784+
}
785+
});
722786
}
723787

724788
@Override
@@ -770,13 +834,35 @@ public void onMediaUploadFailed(final String mediaId) {
770834
mWebView.post(new Runnable() {
771835
@Override
772836
public void run() {
837+
AnalyticsTracker.track(Stat.EDITOR_UPLOAD_MEDIA_FAILED);
773838
mWebView.execJavaScriptFromString("ZSSEditor.markImageUploadFailed(" + mediaId + ");");
774839
mFailedMediaIds.add(mediaId);
775840
mUploadingMediaIds.remove(mediaId);
776841
}
777842
});
778843
}
779844

845+
@Override
846+
public void onGalleryMediaUploadSucceeded(final long galleryId, String remoteMediaId, int remaining) {
847+
if (galleryId == mUploadingMediaGallery.getUniqueId()) {
848+
ArrayList<String> mediaIds = mUploadingMediaGallery.getIds();
849+
mediaIds.add(remoteMediaId);
850+
mUploadingMediaGallery.setIds(mediaIds);
851+
852+
if (remaining == 0) {
853+
mWebView.post(new Runnable() {
854+
@Override
855+
public void run() {
856+
mWebView.execJavaScriptFromString("ZSSEditor.replacePlaceholderGallery('" + galleryId + "', '" +
857+
mUploadingMediaGallery.getIdsStr() + "', '" +
858+
mUploadingMediaGallery.getType() + "', " +
859+
mUploadingMediaGallery.getNumColumns() + ");");
860+
}
861+
});
862+
}
863+
}
864+
}
865+
780866
public void onDomLoaded() {
781867
mWebView.post(new Runnable() {
782868
public void run() {
@@ -820,6 +906,19 @@ public void run() {
820906
}
821907
mWaitingMediaFiles.clear();
822908
}
909+
910+
// Add any galleries that were placed in a queue due to the DOM not having loaded yet
911+
if (mWaitingGalleries.size() > 0) {
912+
// Gallery insertion will only work if the content field is in focus
913+
// (for a new post, no field is in focus until user action)
914+
mWebView.execJavaScriptFromString("ZSSEditor.getField('zss_field_content').focus();");
915+
916+
for (MediaGallery mediaGallery : mWaitingGalleries) {
917+
appendGallery(mediaGallery);
918+
}
919+
920+
mWaitingGalleries.clear();
921+
}
823922
}
824923
});
825924
}
@@ -894,6 +993,7 @@ public void onClick(DialogInterface dialog, int id) {
894993
mWebView.post(new Runnable() {
895994
@Override
896995
public void run() {
996+
AnalyticsTracker.track(Stat.EDITOR_UPLOAD_MEDIA_RETRIED);
897997
mWebView.execJavaScriptFromString("ZSSEditor.unmarkImageUploadFailed(" + mediaId + ");");
898998
mWebView.execJavaScriptFromString("ZSSEditor.setProgressOnImage(" + mediaId + ", " + 0 + ");");
899999
mFailedMediaIds.remove(mediaId);
@@ -908,17 +1008,34 @@ public void run() {
9081008
if (fragmentManager.findFragmentByTag(ImageSettingsDialogFragment.IMAGE_SETTINGS_DIALOG_TAG) != null) {
9091009
return;
9101010
}
911-
1011+
AnalyticsTracker.track(Stat.EDITOR_EDITED_IMAGE);
9121012
ImageSettingsDialogFragment imageSettingsDialogFragment = new ImageSettingsDialogFragment();
9131013
imageSettingsDialogFragment.setTargetFragment(this,
9141014
ImageSettingsDialogFragment.IMAGE_SETTINGS_DIALOG_REQUEST_CODE);
9151015

9161016
Bundle dialogBundle = new Bundle();
9171017

918-
dialogBundle.putString("imageMeta", meta.toString());
9191018
dialogBundle.putString("maxWidth", mBlogSettingMaxImageWidth);
9201019
dialogBundle.putBoolean("featuredImageSupported", mFeaturedImageSupported);
9211020

1021+
// Request and add an authorization header for HTTPS images
1022+
// Use https:// when requesting the auth header, in case the image is incorrectly using http://.
1023+
// If an auth header is returned, force https:// for the actual HTTP request.
1024+
HashMap<String, String> headerMap = new HashMap<>(mCustomHttpHeaders);
1025+
try {
1026+
final String imageSrc = meta.getString("src");
1027+
String authHeader = mEditorFragmentListener.onAuthHeaderRequested(UrlUtils.makeHttps(imageSrc));
1028+
if (authHeader.length() > 0) {
1029+
meta.put("src", UrlUtils.makeHttps(imageSrc));
1030+
headerMap.put("Authorization", authHeader);
1031+
}
1032+
} catch (JSONException e) {
1033+
AppLog.e(T.EDITOR, "Could not retrieve image url from JSON metadata");
1034+
}
1035+
dialogBundle.putSerializable("headerMap", headerMap);
1036+
1037+
dialogBundle.putString("imageMeta", meta.toString());
1038+
9221039
String imageId = JSONUtils.getString(meta, "attachment_id");
9231040
if (!imageId.isEmpty()) {
9241041
dialogBundle.putBoolean("isFeatured", mFeaturedImageId == Integer.parseInt(imageId));
@@ -933,6 +1050,8 @@ public void run() {
9331050
ImageSettingsDialogFragment.IMAGE_SETTINGS_DIALOG_TAG)
9341051
.addToBackStack(null)
9351052
.commit();
1053+
1054+
mWebView.notifyVisibilityChanged(false);
9361055
break;
9371056
}
9381057
}
@@ -950,6 +1069,11 @@ public void onLinkTapped(String url, String title) {
9501069
linkDialogFragment.show(getFragmentManager(), "LinkDialogFragment");
9511070
}
9521071

1072+
@Override
1073+
public void onVideoPressInfoRequested(final String videoId) {
1074+
mEditorFragmentListener.onVideoPressInfoRequested(videoId);
1075+
}
1076+
9531077
public void onGetHtmlResponse(Map<String, String> inputArgs) {
9541078
String functionId = inputArgs.get("function");
9551079

@@ -1051,14 +1175,31 @@ private void clearFormatBarButtons() {
10511175

10521176
private void onFormattingButtonClicked(ToggleButton toggleButton) {
10531177
String tag = toggleButton.getTag().toString();
1054-
1178+
trackFormattingButtonClicked(toggleButton);
10551179
if (mWebView.getVisibility() == View.VISIBLE) {
10561180
mWebView.execJavaScriptFromString("ZSSEditor.set" + StringUtils.capitalize(tag) + "();");
10571181
} else {
10581182
applyFormattingHtmlMode(toggleButton, tag);
10591183
}
10601184
}
10611185

1186+
private void trackFormattingButtonClicked(ToggleButton toggleButton) {
1187+
int id = toggleButton.getId();
1188+
if (id == R.id.format_bar_button_bold) {
1189+
AnalyticsTracker.track(Stat.EDITOR_TAPPED_BOLD);
1190+
} else if (id == R.id.format_bar_button_italic) {
1191+
AnalyticsTracker.track(Stat.EDITOR_TAPPED_ITALIC);
1192+
} else if (id == R.id.format_bar_button_ol) {
1193+
AnalyticsTracker.track(Stat.EDITOR_TAPPED_ORDERED_LIST);
1194+
} else if (id == R.id.format_bar_button_ul) {
1195+
AnalyticsTracker.track(Stat.EDITOR_TAPPED_UNORDERED_LIST);
1196+
} else if (id == R.id.format_bar_button_quote) {
1197+
AnalyticsTracker.track(Stat.EDITOR_TAPPED_BLOCKQUOTE);
1198+
} else if (id == R.id.format_bar_button_strikethrough) {
1199+
AnalyticsTracker.track(Stat.EDITOR_TAPPED_STRIKETHROUGH);
1200+
}
1201+
}
1202+
10621203
/**
10631204
* In HTML mode, applies formatting to selected text, or inserts formatting tag at current cursor position
10641205
* @param toggleButton format bar button which was clicked

0 commit comments

Comments
 (0)