diff --git a/.circleci/config.yml b/.circleci/config.yml
index bd9b0ae96cbf..6e4101d54ba2 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -5,44 +5,47 @@ orbs:
bundle-install: toshimaru/bundle-install@0.3.1
slack: circleci/slack@3.4.2
+parameters:
+ translation_review_build:
+ type: boolean
+ default: false
+ translation_review_lang_id:
+ type: string
+ default: all-lang
+
commands:
copy-gradle-properties:
steps:
- run:
name: Setup gradle.properties
command: cp gradle.properties-example gradle.properties
- yarn-install:
+ npm-install:
steps:
- restore_cache:
- name: Restore Yarn Cache
+ name: Restore NPM Cache
keys:
- - yarn-i18n-v4-cache-v{{ .Environment.CACHE_TRIGGER_VERSION }}-job-{{ .Environment.CIRCLE_JOB }}-{{ checksum "libs/gutenberg-mobile/yarn.lock" }}
+ - npm-i18n-v1-cache-v{{ .Environment.CACHE_TRIGGER_VERSION }}-job-{{ .Environment.CIRCLE_JOB }}-{{ checksum "libs/gutenberg-mobile/package-lock.json" }}
- run:
- name: Yarn Install
+ name: NPM Install
working_directory: libs/gutenberg-mobile
- command: yarn install --frozen-lockfile --prefer-offline --network-concurrency 1
+ command: npm ci --prefer-offline
- save_cache:
- name: Save Yarn Cache
- key: yarn-i18n-v4-cache-v{{ .Environment.CACHE_TRIGGER_VERSION }}-job-{{ .Environment.CIRCLE_JOB }}-{{ checksum "libs/gutenberg-mobile/yarn.lock" }}
+ name: Save NPM Cache
+ key: npm-i18n-v1-cache-v{{ .Environment.CACHE_TRIGGER_VERSION }}-job-{{ .Environment.CIRCLE_JOB }}-{{ checksum "libs/gutenberg-mobile/package-lock.json" }}
paths:
- - libs/gutenberg-mobile/node_modules
+ - ~/.npm
- libs/gutenberg-mobile/i18n-cache/data
checkout-submodules:
steps:
- run:
name: Checkout submodules
- command: git submodule update --init --recursive
- checkout-gutenberg-mobile-submodule-only:
- steps:
- - run:
- name: Checkout gutenberg-mobile submodule (no recursive)
- command: git submodule update --init
- yarn-bundle-android:
+ command: git submodule update --init --recursive --depth 1
+ npm-bundle-android:
steps:
- run:
- name: Yarn bundle Android
+ name: Npm bundle Android
working_directory: libs/gutenberg-mobile
- command: yarn bundle:android
+ command: npm run bundle:android
save-gutenberg-bundle-cache:
steps:
- run:
@@ -54,7 +57,7 @@ commands:
name: Cache JS Bundle
key: android-js-bundle-{{ checksum "gutenberg_submodule_hash" }}
paths:
- - libs/gutenberg-mobile/react-native-gutenberg-bridge/android/build/assets/index.android.bundle
+ - libs/gutenberg-mobile/gutenberg/packages/react-native-bridge/android/build/assets/index.android.bundle
restore-gutenberg-bundle-cache:
steps:
- run:
@@ -77,21 +80,21 @@ jobs:
- run:
name: Abort If JS Bundle Exists
command: |
- if [ -f "libs/gutenberg-mobile/react-native-gutenberg-bridge/android/build/assets/index.android.bundle" ]; then
+ if [ -f "libs/gutenberg-mobile/gutenberg/packages/react-native-bridge/android/build/assets/index.android.bundle" ]; then
echo "Gutenberg-Mobile bundle already in cache, no need to create a new one."
circleci-agent step halt
else
echo "Gutenberg-Mobile bundle not found in cache. Proceeding to generate new bundle"
fi
- checkout-submodules
- - yarn-install
- - yarn-bundle-android
+ - npm-install
+ - npm-bundle-android
- run:
name: Ensure assets folder exists
- command: mkdir -p libs/gutenberg-mobile/react-native-gutenberg-bridge/android/build/assets
+ command: mkdir -p libs/gutenberg-mobile/gutenberg/packages/react-native-bridge/android/build/assets
- run:
name: Move bundle to assets folder
- command: mv libs/gutenberg-mobile/bundle/android/App.js libs/gutenberg-mobile/react-native-gutenberg-bridge/android/build/assets/index.android.bundle
+ command: mv libs/gutenberg-mobile/bundle/android/App.js libs/gutenberg-mobile/gutenberg/packages/react-native-bridge/android/build/assets/index.android.bundle
- save-gutenberg-bundle-cache
test:
executor:
@@ -99,15 +102,15 @@ jobs:
api-version: "28"
steps:
- git/shallow-checkout
- - checkout-gutenberg-mobile-submodule-only
+ - checkout-submodules
- android/restore-gradle-cache
- copy-gradle-properties
- restore-gutenberg-bundle-cache
- run:
name: Ensure assets folder exists
- command: mkdir -p libs/gutenberg-mobile/react-native-gutenberg-bridge/android/src/main/assets
+ command: mkdir -p libs/gutenberg-mobile/gutenberg/packages/react-native-bridge/android/src/main/assets
- attach_workspace:
- at: libs/gutenberg-mobile/react-native-gutenberg-bridge/android/src/main/assets
+ at: libs/gutenberg-mobile/gutenberg/packages/react-native-bridge/android/src/main/assets
- run:
name: Test WordPress
environment:
@@ -126,15 +129,15 @@ jobs:
api-version: "28"
steps:
- git/shallow-checkout
- - checkout-gutenberg-mobile-submodule-only
+ - checkout-submodules
- android/restore-gradle-cache
- copy-gradle-properties
- restore-gutenberg-bundle-cache
- run:
name: Ensure assets folder exists
- command: mkdir -p libs/gutenberg-mobile/react-native-gutenberg-bridge/android/src/main/assets
+ command: mkdir -p libs/gutenberg-mobile/gutenberg/packages/react-native-bridge/android/src/main/assets
- attach_workspace:
- at: libs/gutenberg-mobile/react-native-gutenberg-bridge/android/src/main/assets
+ at: libs/gutenberg-mobile/gutenberg/packages/react-native-bridge/android/src/main/assets
- run:
name: Checkstyle
environment:
@@ -169,7 +172,7 @@ jobs:
api-version: "28"
steps:
- git/shallow-checkout
- - checkout-gutenberg-mobile-submodule-only
+ - checkout-submodules
- bundle-install/bundle-install:
cache_key_prefix: installable-build
- run:
@@ -179,9 +182,9 @@ jobs:
- restore-gutenberg-bundle-cache
- run:
name: Ensure assets folder exists
- command: mkdir -p libs/gutenberg-mobile/react-native-gutenberg-bridge/android/src/main/assets
+ command: mkdir -p libs/gutenberg-mobile/gutenberg/packages/react-native-bridge/android/src/main/assets
- attach_workspace:
- at: libs/gutenberg-mobile/react-native-gutenberg-bridge/android/src/main/assets
+ at: libs/gutenberg-mobile/gutenberg/packages/react-native-bridge/android/src/main/assets
- run:
name: Build APK
environment:
@@ -218,15 +221,15 @@ jobs:
api-version: "28"
steps:
- git/shallow-checkout
- - checkout-gutenberg-mobile-submodule-only
+ - checkout-submodules
- android/restore-gradle-cache
- copy-gradle-properties
- restore-gutenberg-bundle-cache
- run:
name: Ensure assets folder exists
- command: mkdir -p libs/gutenberg-mobile/react-native-gutenberg-bridge/android/src/main/assets
+ command: mkdir -p libs/gutenberg-mobile/gutenberg/packages/react-native-bridge/android/src/main/assets
- attach_workspace:
- at: libs/gutenberg-mobile/react-native-gutenberg-bridge/android/src/main/assets
+ at: libs/gutenberg-mobile/gutenberg/packages/react-native-bridge/android/src/main/assets
- run:
name: Build
environment:
@@ -261,15 +264,15 @@ jobs:
api-version: "28"
steps:
- git/shallow-checkout
- - checkout-gutenberg-mobile-submodule-only
+ - checkout-submodules
- android/restore-gradle-cache
- copy-gradle-properties
- restore-gutenberg-bundle-cache
- run:
name: Ensure assets folder exists
- command: mkdir -p libs/gutenberg-mobile/react-native-gutenberg-bridge/android/src/main/assets
+ command: mkdir -p libs/gutenberg-mobile/gutenberg/packages/react-native-bridge/android/src/main/assets
- attach_workspace:
- at: libs/gutenberg-mobile/react-native-gutenberg-bridge/android/src/main/assets
+ at: libs/gutenberg-mobile/gutenberg/packages/react-native-bridge/android/src/main/assets
- run:
name: Build
environment:
@@ -293,7 +296,7 @@ jobs:
- image: circleci/ruby:2.6.4
steps:
- git/shallow-checkout
- - checkout-gutenberg-mobile-submodule-only
+ - checkout-submodules
- run:
name: Install bundler
command: gem install bundler --version 2.0.2
@@ -302,9 +305,54 @@ jobs:
- run:
name: Validate login strings
command: bundle exec fastlane validate_login_strings pr_url:$CIRCLE_PULL_REQUEST
+ translation-review-build:
+ executor:
+ name: android/default
+ api-version: "28"
+ environment:
+ APP_VERSION_PREFIX: << pipeline.parameters.translation_review_lang_id >>
+ steps:
+ - git/shallow-checkout
+ - checkout-submodules
+ - bundle-install/bundle-install:
+ cache_key_prefix: installable-build
+ - run:
+ name: Copy Secrets
+ command: bundle exec fastlane run configure_apply
+ - android/restore-gradle-cache
+ - restore-gutenberg-bundle-cache
+ - run:
+ name: Ensure assets folder exists
+ command: mkdir -p libs/gutenberg-mobile/react-native-gutenberg-bridge/android/src/main/assets
+ - attach_workspace:
+ at: libs/gutenberg-mobile/react-native-gutenberg-bridge/android/src/main/assets
+ - run:
+ name: Build APK
+ environment:
+ SUPPRESS_GUTENBERG_MOBILE_JS_BUNDLE_BUILD: 1
+ command: |
+ TODAY_DATE=$(date +'%Y%m%d')
+ VERSION_NAME="${APP_VERSION_PREFIX}-build-${TODAY_DATE}-${CIRCLE_BUILD_NUM}"
+ echo "export VERSION_NAME=$VERSION_NAME" >> $BASH_ENV
+
+ bundle exec fastlane build_for_translation_review custom_version:"$VERSION_NAME"
+ - android/save-gradle-cache
+ - run:
+ name: Prepare APK
+ command: |
+ mkdir -p Artifacts
+ mv WordPress/build/outputs/apk/jalapeno/debug/org.wordpress.android-jalapeno-debug.apk "Artifacts/WordPress-${VERSION_NAME}.apk"
+ - run:
+ name: Upload APK
+ command: |
+ curl --http1.1 https://${APPET_TOKEN}@api.appetize.io/v1/apps/${APPET_APPID} -F "file=@Artifacts/WordPress-${VERSION_NAME}.apk" -F "platform=android"
+ - store_artifacts:
+ path: Artifacts
+ destination: Artifacts
workflows:
wordpress_android:
+ unless: << pipeline.parameters.translation_review_build >>
jobs:
- gutenberg-bundle-build
- strings-check
@@ -329,7 +377,7 @@ workflows:
- Connected Tests:
requires:
- gutenberg-bundle-build
- post-to-slack: true
+ post-to-slack: false
# Always run connected tests on develop and release branches
filters:
branches:
@@ -337,6 +385,7 @@ workflows:
- develop
- /^release.*/
Optional Tests:
+ unless: << pipeline.parameters.translation_review_build >>
#Optionally run connected tests on PRs
jobs:
- Hold:
@@ -349,3 +398,7 @@ workflows:
- /pull\/[0-9]+/
- Connected Tests:
requires: [Hold]
+ Translation Review Build:
+ when: << pipeline.parameters.translation_review_build >>
+ jobs:
+ - translation-review-build
diff --git a/Gemfile.lock b/Gemfile.lock
index f8d70d02446e..c9872a9048ba 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,9 +1,9 @@
GIT
remote: https://github.com/wordpress-mobile/release-toolkit
- revision: 6c1fa45f3beb216f4f5d03346f2981f1024b799e
- tag: 0.9.6
+ revision: b515c0b26b78bfffc3cbe5ceb3b51bb6eb979ab4
+ tag: 0.9.8
specs:
- fastlane-plugin-wpmreleasetoolkit (0.9.6)
+ fastlane-plugin-wpmreleasetoolkit (0.9.8)
activesupport (~> 4)
chroma (= 0.2.0)
diffy (~> 3.3)
@@ -169,7 +169,7 @@ GEM
optimist (3.0.1)
options (2.3.2)
os (1.1.0)
- parallel (1.19.1)
+ parallel (1.19.2)
plist (3.5.0)
progress_bar (1.3.1)
highline (>= 1.6, < 3)
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 62061087606b..1612e1e8c2e5 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -1,11 +1,19 @@
+15.3
+-----
+
15.2
-----
+* [**] Fixes tons of rendering issues in Reader post detail by changing the technical solution (shared CSS file).
+* [**] Block editor: Display content metrics information (blocks, words, characters count).
+* [**] Block Editor: Adds editor support for theme defined colors and theme defined gradients on cover and button blocks.
+* [**] Block Editor: Add support allowing Cover Block video uploads to complete after the editor has closed
+* [*] Block Editor: Fix handling of upload completion while re-opening the editor
+* [*] Fix crash when WordPress api response has an empty body
15.1
-----
* Fixes issue on Notifications tab when two screens were drawn on top of each other
* [**] Fix video thumbnails, settings and preview in Media section for private sites
-* [**] Block Editor: Adds editor support for theme defined colors and theme defined gradients on cover and button blocks.
* [*] Support for breaking out of captions/citation authors by pressing enter on the following blocks: image, video, gallery, quote, and pullquote.
15.0
diff --git a/WordPress/FEATURE_ANNOUNCEMENTS.json-example b/WordPress/FEATURE_ANNOUNCEMENTS.json-example
deleted file mode 100644
index cbabdcd67b86..000000000000
--- a/WordPress/FEATURE_ANNOUNCEMENTS.json-example
+++ /dev/null
@@ -1,61 +0,0 @@
-// FEATURE_ANNOUNCEMENT.json file should be located in WordPress/src/main/assets
-// comments in this file are ignored by Gson, so you can either remove or leave them
-
-{
- "announcements": [
- {
- "appVersionName": "14.7",
- // app version name that announcement targets. Used for display purposes only.
- "announcementVersion": 1,
- // unique version of announcement. Used to track if the specific announcement was shown or not.
- "detailsUrl": "http://wordpress.org",
- // URL that "Find out more" button will open. If empty, "Find out more" button will not be shown.
- "features": [
- {
- "localizedContent": {
- "en": {
- "title": "Super Publishing",
- "subtitle": "Super Publishing is here! Publish using the power of your mind."
- },
- "it": {
- "title": "Super Pubblicazione",
- "subtitle": "Pubblicare articoli incredibili..."
- }
- },
- // icon data represented as base64 string without prefix (data:image/png;base64,).
- "iconBase64": "iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAb1BMVEX///+ZzACTyQCRyQDi8MTk8cu+33f2+u3N5piczRSu2EnA4Hr+//rE4oTr9tXy+eOo1Dr1+unS6KOi0Sey2Fvt9tvd7ru322jb7bb6/PPE4oa73XHK5JLr9dfW6q32+uuo1DzQ6KC02mCv11LH4oz7JUSRAAAGUUlEQVR4nO2d63qiMBBASYK2eBcEReul6vs/4wJuEcJEg+Yy3Z3zc7fhyyEjl2TIBAFB/D/ELFmaOtZZrE0dyhy5YDwxdKytYGJj6Fjm+BCM8S8jh9oXh0JoGEx50a+5iSNFjDFT4WAUUx0zd6pMszcTp4YOYwUzJx9tjJaY6BzeGC0xEGCYY7Rk+/YAoI7Rknc7iDtGS6ogu77cfIY8RkuqOE07/5zPB+Hh+7qdlmyvn4dskH50W6OP0RKpk5PZYbtKuBBcpvin6DKNBw1R/DFakt7jdLkZJ6UaU1OasnW8r/6+itFPn53X42+chlMmHrm1PAVfb/KA/YYYLUmKjkbs4dCBlmU7/DFaEvZ0azL23XkN0vUbgowvMt8CT5ivxBt+JSLC7Jjv3vWrHBcz3yIq4r5XF6XjOvftArE8GfJj5W0S4TTNwUSA3hEr30Iya7OCxTAyVLfGPDEXoTUC0UV1bl6vUkTzjJpaGMCb4tS32g1rglgU5/YEC8XX5wuMkQP9Kt6GuPar0+MmIvYtWL0rSZ2NDsfiASDuJXhNi5OVLbqOYuBZsPsmweulv4v2MCY/Uxmf3dsqP3pSuwE8yTQeRka6hpO6yVf3rHh97V8Cp3zy8L8hRNg4JBD1Pi+op253W905axlGzSYH4Kfo720qBoaw9agFdBdg2GwyB8Y9CjyRA/3nrfOtNWXTnuSeAIbephiHkGHr4r7RMtw2m3xAv13h540YiifpdE+1ovTUbAIOO9+5FKuBLyOt34yOXzFCy6dHFcZSdXoADmFxuhuPWVfNW/7l3mSmuMH4GEToV1id7vrupj2xcb/jKd80BbBSZZlc2X0xra4Lxx5Ti3x0W5D7VjZ5Y1HyVWJ1BHIxGu4W/SZuRLIbXx7ORjo37D5d2YW7nrVJTc+tPcX19KLuZdIcYvK8VyaJXAsyx9PgipuhVdwm1T64ktrDqaHei59ZgCQWi3gQbD0PWsfHz9Dts2nm42fo9FUfmBFzgMs74tqHoNNLzcKLoctHUy+CLi+m0HyYC0N374hpN5fSBcLd7SIP/YA2k4ggCIL4L5mEAy+Yf/JOs/gzzro5gkc8zzT7TdnF11amZjt+ExFsLEmCq5gOnkuHUhfDc93Fa2/J+ahhwcW5tQiLw3CftLo47Pd+vJEceGv1GoWhnFzUL+UWWPlrZl9hMLwCeSD6intwhf6+RonAMAO7oG0IrpzxMyZD+A90s6cGsICoU+j8GypSkbimoSJHhB/wGCpWFXgICnW4KJrXMeDfULECzb/1DBVJk7z+dM6/oWL5Ujc9jAzJ0BZkSIZkSIb2IUMyJEMytA8ZkiEZkqF9yJAMyZAM7UOGZEiGZGgfMiRDMiRD+5AhGZIhGdqHDMmQDMnQPmRIhmRIhvYhQzIkQzK0DxmSIRmSoX3IUN9wpWhe79ns31D1ZZfmLkSKfS0xfZ2n2IpLt5CCYrfi+6ba/g3hj2S1v7CEtstnzT21/RsqvpLVLmEKbxp4/94dgSH4JW+PTfmAIOCNTSkQGEJVC1ifWh+htC1z+3N+DIbdTRGSflt+5+tGXRsuVSVCYRgsL80uvlBGeH6N/m6rkXxKpQlwGAZBWlZzLXsoTvFru0Z+DDabbNatvIDFsOBYdnFvfFNMRIaWIEMyJEMyJEMyJEMyJMPfYKiuhGTXcPy8a6bwsq1+j2mY9/Ei6LTSjKfKAQ5rkWvVNTSPO0E/P0SXP8Mg2HpQdFvIcuLe0HX1PHm20j7OC8ruHI9ij+1XTbFyquil6vHQXaBy5rReV03GHA2jWDmuDFgz+WLCuiQXI82VXTuE4xMXP6gHAUZ5dhqHXH37KAUsM7mhKgPFpz9/0SZIFYp8Vh8SGWpDGFWBOo62ZA4ZypAhPshQhgzxQYYyZIgPMpQhQ3yQoQwZ4oMMZcgQH2QoQ4b4IEMZMsQHGcqQIT7IUIYM8UGGMr/PUFXAVPWVvCoT12USYj++4FVroazwCgu6TULshSrqlA3gL/9dZjv3BcyTEuoStnACoOjzxbJjcmhETg8aQAmAQnPrBz/MO4PCk4cpI3IN+EJQt7i2J45J21GsnjTYSN/F4x7Bivie7cZF8rxQdr4TjQZrDBlQT8l2UZWxlUz1EtLy+FLlhbERigwvPSbHZc8dDpZLp2nc/xJ/AMM4nGaREA01AAAAAElFTkSuQmCC",
- "iconUrl": ""
- // If icon is missing, we will try to load content of iconBase64
- },
- {
- "localizedContent": {
- "en": {
- "title": "Amazing Feature",
- "subtitle": "That's right! They are right in the app! They require pets right now. Are you going to look for them or what?"
- },
- "it": { // localized feature description for Italian language
- "title": "Amazing Feature in Italian",
- "subtitle": "Description in Italian 2"
- }
- },
- "iconBase64": "",
- "iconUrl": "https://s0.wordpress.com/i/store/mobile/plans-personal.png"
- },
- {
- "localizedContent": {
- "en": {
- "title": "We like long feature announcements that why this one is going to be extra long and span multiple lines",
- "subtitle": "Here we run out of budget."
- },
- "it": { // localized feature description for Italian language
- "title": "Long feature title in Italian",
- "subtitle": "Description in Italian 3"
- }
- },
- "iconBase64": "",
- "iconUrl": "https://s0.wordpress.com/i/store/mobile/plans-premium.png"
- }
- ]
- }
- ]
-}
diff --git a/WordPress/build.gradle b/WordPress/build.gradle
index 0020d47d80ea..a08ce1c55cfb 100644
--- a/WordPress/build.gradle
+++ b/WordPress/build.gradle
@@ -22,9 +22,9 @@ repositories {
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
-apply plugin: 'kotlin-kapt'
apply plugin: 'se.bjurr.violations.violation-comments-to-github-gradle-plugin'
apply plugin: 'kotlin-allopen'
+apply plugin: 'kotlin-kapt'
allOpen {
// allows mocking for classes w/o directly opening them for release builds
@@ -55,9 +55,9 @@ android {
if (project.hasProperty("versionName")) {
versionName project.property("versionName")
} else {
- versionName "alpha-230"
+ versionName "alpha-231"
}
- versionCode 884
+ versionCode 887
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
@@ -69,6 +69,8 @@ android {
buildConfigField "boolean", "OFFER_GUTENBERG", "true"
buildConfigField "boolean", "TENOR_AVAILABLE", "true"
buildConfigField "boolean", "READER_IMPROVEMENTS_PHASE_2", "true"
+ buildConfigField "long", "REMOTE_CONFIG_FETCH_INTERVAL", "10"
+ buildConfigField "boolean", "FEATURE_ANNOUNCEMENT_AVAILABLE", "false"
}
// Gutenberg's dependency - react-native-video is using
@@ -85,12 +87,13 @@ android {
dimension "buildType"
// Only set the release version if one isn't provided
if (!project.hasProperty("versionName")) {
- versionName "15.1"
+ versionName "15.2-rc-1"
}
- versionCode 885
+ versionCode 886
buildConfigField "boolean", "ME_ACTIVITY_AVAILABLE", "false"
buildConfigField "boolean", "TENOR_AVAILABLE", "false"
buildConfigField "boolean", "READER_IMPROVEMENTS_PHASE_2", "false"
+ buildConfigField "long", "REMOTE_CONFIG_FETCH_INTERVAL", "3600"
}
zalpha { // alpha version - enable experimental features
@@ -147,6 +150,9 @@ android {
exclude '**/libjscexecutor.so'
exclude '**/libhermes-inspector.so'
exclude '**/libhermes-executor-debug.so'
+
+ pickFirst 'META-INF/-no-jdk.kotlin_module'
+
}
bundle {
@@ -202,9 +208,9 @@ dependencies {
})
implementation 'com.android.volley:volley:1.1.1'
+ implementation 'com.google.firebase:firebase-messaging:20.1.5'
implementation 'com.google.android.gms:play-services-auth:18.0.0'
implementation 'com.google.android.gms:play-services-places:17.0.0'
- implementation 'com.google.firebase:firebase-messaging:20.1.5'
implementation 'com.android.installreferrer:installreferrer:1.0'
implementation 'com.github.chrisbanes.photoview:library:1.2.4'
implementation 'org.greenrobot:eventbus:3.1.1'
@@ -233,7 +239,7 @@ dependencies {
testImplementation "junit:junit:$jUnitVersion"
testImplementation 'org.robolectric:robolectric:4.3'
testImplementation 'org.robolectric:shadows-multidex:4.3'
- testImplementation 'org.mockito:mockito-core:2.23.0'
+ testImplementation "org.mockito:mockito-core:$mockitoCoreVersion"
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:$nhaarmanMockitoVersion"
testImplementation "org.assertj:assertj-core:$assertJVersion"
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.2.1'
@@ -265,7 +271,7 @@ dependencies {
androidTestImplementation "androidx.test:runner:$androidxTestVersion"
androidTestImplementation "androidx.test:rules:$androidxTestVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxTestVersion"
- androidTestImplementation 'tools.fastlane:screengrab:1.2.0', {
+ androidTestImplementation 'tools.fastlane:screengrab:2.0.0', {
exclude group: 'com.android.support.test.uiautomator', module: 'uiautomator-v18'
}
androidTestImplementation "androidx.work:work-testing:$androidxWorkVersion"
@@ -310,6 +316,12 @@ dependencies {
implementation 'io.sentry:sentry-android:2.1.3'
implementation 'org.slf4j:slf4j-nop:1.7.25'
+ // Firebase
+ implementation 'com.google.firebase:firebase-config:19.1.3'
+
+ compileOnly project(path:':libs:WordPressAnnotations')
+ kapt project(':libs:WordPressProcessors')
+
// Encrypted Logging
implementation "com.goterl.lazycode:lazysodium-android:4.1.0@aar"
implementation "net.java.dev.jna:jna:4.5.1@aar"
diff --git a/WordPress/lint-baseline.xml b/WordPress/lint-baseline.xml
index 42c2fd629297..023d9ff9aabc 100644
--- a/WordPress/lint-baseline.xml
+++ b/WordPress/lint-baseline.xml
@@ -57,7 +57,7 @@
errorLine1=" view.setPadding("
errorLine2=" ^">
@@ -3927,7 +3927,7 @@
errorLine1=" Layout.BREAK_STRATEGY_SIMPLE : Layout.BREAK_STRATEGY_HIGH_QUALITY;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3943,7 +3943,7 @@
errorLine1=" Layout.BREAK_STRATEGY_SIMPLE : Layout.BREAK_STRATEGY_HIGH_QUALITY;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3959,7 +3959,7 @@
errorLine1=" view.setJustificationMode(Layout.JUSTIFICATION_MODE_INTER_WORD);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3975,7 +3975,7 @@
errorLine1=" view.setJustificationMode(Layout.JUSTIFICATION_MODE_NONE);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3991,7 +3991,7 @@
errorLine1=" classpath 'com.android.tools.build:gradle:3.4.2'"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -4007,7 +4007,7 @@
errorLine1=" implementation 'androidx.appcompat:appcompat:1.0.0'"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -4023,7 +4023,7 @@
errorLine1=" implementation 'androidx.appcompat:appcompat:1.0.0'"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -4039,7 +4039,7 @@
errorLine1=" implementation 'androidx.recyclerview:recyclerview:1.0.0'"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -4055,7 +4055,7 @@
errorLine1=" @ReactProp(name = PROP_TEXT)"
errorLine2=" ~~~~~~~~~">
@@ -4071,7 +4071,7 @@
errorLine1=" private HashMap<Integer, Media> mMediaToAddAfterMounting = new HashMap<>();"
errorLine2=" ~~~~~~~~~~~~~~~">
@@ -4087,7 +4087,7 @@
url="http://developer.android.com/design/style/iconography.html"
urls="http://developer.android.com/design/style/iconography.html">
+ file="../libs/gutenberg-mobile/gutenberg/packages/react-native-aztec/android/src/main/res/drawable-hdpi/ic_launcher.png"/>
+ file="../libs/gutenberg-mobile/gutenberg/packages/react-native-aztec/android/src/main/res/drawable-mdpi/ic_launcher.png"/>
+ file="../libs/gutenberg-mobile/gutenberg/packages/react-native-aztec/android/src/main/res/drawable-xhdpi/ic_launcher.png"/>
+ file="../libs/gutenberg-mobile/gutenberg/packages/react-native-aztec/android/src/main/res/drawable-xxhdpi/ic_launcher.png"/>
@@ -4159,7 +4159,7 @@
errorLine1=" (int) update.getPaddingLeft(),"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
@@ -4175,7 +4175,7 @@
errorLine1=" (int) update.getPaddingRight(),"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
diff --git a/WordPress/metadata/full_description.txt b/WordPress/metadata/full_description.txt
index 0203cc20e3f6..8f6b87705ee0 100644
--- a/WordPress/metadata/full_description.txt
+++ b/WordPress/metadata/full_description.txt
@@ -40,4 +40,6 @@ WordPress is an open source website creator, meaning anyone can see how it's mad
Whether you need a website builder to create your website, or a simple blog maker, WordPress can help. It gives you beautiful designs, powerful features, and the freedom to build anything you want.
-WordPress for Android supports self-hosted sites running WordPress 4.0 and later, and all sites at WordPress.com. Just like WordPress, WordPress for Android is open source. Learn more at https://apps.wordpress.com/contribute/. Need help with the app? Send us a tweet at @WPAndroid, or visit our forums at https://android.forums.wordpress.org/forum/troubleshooting.
\ No newline at end of file
+WordPress for Android supports self-hosted sites running WordPress 4.0 and later, and all sites at WordPress.com. Just like WordPress, WordPress for Android is open source. Learn more at https://wp.me/P7jrAc-tJ. Need help with the app? Send us a tweet at @WPAndroid, or visit our forums at https://android.forums.wordpress.org/forum/troubleshooting.
+
+California users privacy notice: https://wp.me/Pe4R-d/#california-consumer-privacy-act-ccpa.
\ No newline at end of file
diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/BlockEditorTests.java b/WordPress/src/androidTest/java/org/wordpress/android/e2e/BlockEditorTests.java
index dd6e299d4636..6157274feaa6 100644
--- a/WordPress/src/androidTest/java/org/wordpress/android/e2e/BlockEditorTests.java
+++ b/WordPress/src/androidTest/java/org/wordpress/android/e2e/BlockEditorTests.java
@@ -1,72 +1,74 @@
package org.wordpress.android.e2e;
-//
-//import android.Manifest.permission;
-//
-//import androidx.test.rule.ActivityTestRule;
-//import androidx.test.rule.GrantPermissionRule;
-//
-//import org.junit.Before;
-//import org.junit.Rule;
-//import org.junit.Test;
-//import org.wordpress.android.e2e.components.MasterbarComponent;
-//import org.wordpress.android.e2e.pages.BlockEditorPage;
-//import org.wordpress.android.e2e.pages.EditorPage;
-//import org.wordpress.android.e2e.pages.MySitesPage;
-//import org.wordpress.android.e2e.pages.PostPreviewPage;
-//import org.wordpress.android.e2e.pages.SiteSettingsPage;
-//import org.wordpress.android.support.BaseTest;
-//import org.wordpress.android.ui.WPLaunchActivity;
-//
-//import static androidx.test.espresso.Espresso.pressBack;
-//import static org.wordpress.android.support.WPSupportUtils.sleep;
-//
-//public class BlockEditorTests extends BaseTest {
-// @Rule
-// public ActivityTestRule mActivityTestRule = new ActivityTestRule<>(WPLaunchActivity.class);
-//
-// @Rule
-// public GrantPermissionRule mRuntimeImageAccessRule = GrantPermissionRule.grant(permission.WRITE_EXTERNAL_STORAGE);
-//
-// @Before
-// public void setUp() {
-// logoutIfNecessary();
-// wpLogin();
-// }
-//
-// @Test
-// public void testSwitchToClassicAndPreview() {
-// String title = "Hello Espresso!";
-//
-// MasterbarComponent mb = new MasterbarComponent().goToMySitesTab();
-// sleep();
-//
-// MySitesPage mySitesPage = new MySitesPage();
-// mySitesPage.gotoSiteSettings();
-//
-// // Set to Gutenberg. Apparently the site is defaulting to Aztec still.
-// new SiteSettingsPage().toggleGutenbergSetting();
-//
-// // exit the Settings page
-// pressBack();
-//
-// mb.clickBlogPosts();
-//
-// new MySitesPage()
-// .startNewPost();
-//
-// BlockEditorPage blockEditorPage = new BlockEditorPage();
-// blockEditorPage.waitForTitleDisplayed();
-//
-// blockEditorPage.enterTitle(title);
-//
-// blockEditorPage.switchToClassic();
-//
-// EditorPage editorPage = new EditorPage();
-// editorPage.hasTitle(title);
-//
-// editorPage.previewPost();
-// sleep();
-//
-// new PostPreviewPage();
-// }
-//}
+
+import android.Manifest.permission;
+
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.rule.GrantPermissionRule;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.wordpress.android.e2e.components.MasterbarComponent;
+import org.wordpress.android.e2e.pages.BlockEditorPage;
+import org.wordpress.android.e2e.pages.EditorPage;
+import org.wordpress.android.e2e.pages.MySitesPage;
+import org.wordpress.android.e2e.pages.PostPreviewPage;
+import org.wordpress.android.e2e.pages.SiteSettingsPage;
+import org.wordpress.android.support.BaseTest;
+import org.wordpress.android.ui.WPLaunchActivity;
+
+import static androidx.test.espresso.Espresso.pressBack;
+import static org.wordpress.android.support.WPSupportUtils.sleep;
+
+public class BlockEditorTests extends BaseTest {
+ @Rule
+ public ActivityTestRule mActivityTestRule = new ActivityTestRule<>(WPLaunchActivity.class);
+
+ @Rule
+ public GrantPermissionRule mRuntimeImageAccessRule = GrantPermissionRule.grant(permission.WRITE_EXTERNAL_STORAGE);
+
+ @Before
+ public void setUp() {
+ logoutIfNecessary();
+ wpLogin();
+ }
+
+ @Ignore("until startup times are improved or idling resources are made more reliable")
+ @Test
+ public void testSwitchToClassicAndPreview() {
+ String title = "Hello Espresso!";
+
+ MasterbarComponent mb = new MasterbarComponent().goToMySitesTab();
+ sleep();
+
+ MySitesPage mySitesPage = new MySitesPage();
+ mySitesPage.gotoSiteSettings();
+
+ // Set to Gutenberg. Apparently the site is defaulting to Aztec still.
+ new SiteSettingsPage().setEditorToGutenberg();
+
+ // exit the Settings page
+ pressBack();
+
+ mb.clickBlogPosts();
+
+ new MySitesPage()
+ .startNewPost();
+
+ BlockEditorPage blockEditorPage = new BlockEditorPage();
+ blockEditorPage.waitForTitleDisplayed();
+
+ blockEditorPage.enterTitle(title);
+
+ blockEditorPage.switchToClassic();
+
+ EditorPage editorPage = new EditorPage();
+ editorPage.hasTitle(title);
+
+ editorPage.previewPost();
+ sleep();
+
+ new PostPreviewPage();
+ }
+}
diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/EditorTests.java b/WordPress/src/androidTest/java/org/wordpress/android/e2e/EditorTests.java
index 56b298ef2dc8..cf76c61a3ac6 100644
--- a/WordPress/src/androidTest/java/org/wordpress/android/e2e/EditorTests.java
+++ b/WordPress/src/androidTest/java/org/wordpress/android/e2e/EditorTests.java
@@ -1,107 +1,111 @@
package org.wordpress.android.e2e;
-//
-//import android.Manifest.permission;
-//
-//import androidx.test.rule.ActivityTestRule;
-//import androidx.test.rule.GrantPermissionRule;
-//
-//import org.junit.Before;
-//import org.junit.Rule;
-//import org.junit.Test;
-//import org.wordpress.android.R;
-//import org.wordpress.android.e2e.components.MasterbarComponent;
-//import org.wordpress.android.e2e.pages.EditorPage;
-//import org.wordpress.android.e2e.pages.MySitesPage;
-//import org.wordpress.android.support.BaseTest;
-//import org.wordpress.android.ui.WPLaunchActivity;
-//
-//import java.time.Instant;
-//
-//import static androidx.test.espresso.Espresso.onView;
-//import static androidx.test.espresso.Espresso.pressBack;
-//import static androidx.test.espresso.matcher.ViewMatchers.withId;
-//import static androidx.test.espresso.matcher.ViewMatchers.withText;
-//import static junit.framework.TestCase.assertTrue;
-//import static org.wordpress.android.support.WPSupportUtils.checkViewHasText;
-//import static org.wordpress.android.support.WPSupportUtils.sleep;
-//import static org.wordpress.android.support.WPSupportUtils.waitForElementToNotBeDisplayed;
-//
-//public class EditorTests extends BaseTest {
-// @Rule
-// public ActivityTestRule mActivityTestRule = new ActivityTestRule<>(WPLaunchActivity.class);
-//
-// @Rule
-// public GrantPermissionRule mRuntimeImageAccessRule = GrantPermissionRule.grant(permission.WRITE_EXTERNAL_STORAGE);
-//
-// @Before
-// public void setUp() {
-// logoutIfNecessary();
-// wpLogin();
-// }
-//
-// @Test
-// public void testPublishSimplePost() {
-// String title = "Hello Espresso!";
-// String content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
-//
-// MasterbarComponent mb = new MasterbarComponent().goToMySitesTab();
-// sleep();
-// mb.clickBlogPosts();
-//
-// new MySitesPage()
-// .startNewPost();
-//
-// EditorPage editorPage = new EditorPage();
-// editorPage.enterTitle(title);
-// editorPage.enterContent(content);
-// boolean isPublished = editorPage.publishPost();
-// assertTrue(isPublished);
-// }
-//
-// @Test
-// public void testPublishFullPost() {
-// String title = "Hello Espresso!";
-// String content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
-// + "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis "
-// + "exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";
-// String category = "Wedding";
-// long now = Instant.now().toEpochMilli();
-// String tag = "Tag " + now;
-//
-// MasterbarComponent mb = new MasterbarComponent().goToMySitesTab();
-// sleep();
-// mb.clickBlogPosts();
-//
-// new MySitesPage()
-// .startNewPost();
-//
-// EditorPage editorPage = new EditorPage();
-// editorPage.enterTitle(title);
-// editorPage.enterContent(content);
-// editorPage.enterImage();
-// editorPage.openSettings();
-//
-// editorPage.addACategory(category);
-// editorPage.addATag(tag);
-// editorPage.setFeaturedImage();
-//
-// // ----------------------------
-// // Verify post settings data
-// // ----------------------------
-// // Verify Category added
-// checkViewHasText(onView(withId(R.id.post_categories)), category);
-//
-// // Verify tag added
-// checkViewHasText(onView(withId(R.id.post_tags)), tag);
-//
-// // Verify the featured image added
-// waitForElementToNotBeDisplayed(onView(withText(R.string.post_settings_set_featured_image)));
-//
-// // head back to the post
-// pressBack();
-//
-// // publish
-// boolean isPublished = editorPage.publishPost();
-// assertTrue(isPublished);
-// }
-//}
+
+import android.Manifest.permission;
+
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.rule.GrantPermissionRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.wordpress.android.R;
+import org.wordpress.android.e2e.components.MasterbarComponent;
+import org.wordpress.android.e2e.pages.EditorPage;
+import org.wordpress.android.e2e.pages.MySitesPage;
+import org.wordpress.android.e2e.pages.SiteSettingsPage;
+import org.wordpress.android.support.BaseTest;
+import org.wordpress.android.ui.WPLaunchActivity;
+
+import java.time.Instant;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.Espresso.pressBack;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static junit.framework.TestCase.assertTrue;
+import static org.wordpress.android.support.WPSupportUtils.checkViewHasText;
+import static org.wordpress.android.support.WPSupportUtils.sleep;
+import static org.wordpress.android.support.WPSupportUtils.waitForElementToNotBeDisplayed;
+
+public class EditorTests extends BaseTest {
+ @Rule
+ public ActivityTestRule mActivityTestRule = new ActivityTestRule<>(WPLaunchActivity.class);
+
+ @Rule
+ public GrantPermissionRule mRuntimeImageAccessRule = GrantPermissionRule.grant(permission.WRITE_EXTERNAL_STORAGE);
+
+ @Before
+ public void setUp() {
+ logoutIfNecessary();
+ wpLogin();
+
+ MasterbarComponent mb = new MasterbarComponent().goToMySitesTab();
+ sleep();
+
+ MySitesPage mySitesPage = new MySitesPage();
+ mySitesPage.gotoSiteSettings();
+
+ // Set to Classic.
+ new SiteSettingsPage().setEditorToClassic();
+
+ // exit the Settings page
+ pressBack();
+
+ mb.clickBlogPosts();
+
+ new MySitesPage()
+ .startNewPost();
+ }
+
+ @Test
+ public void testPublishSimplePost() {
+ String title = "Hello Espresso!";
+ String content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
+
+ EditorPage editorPage = new EditorPage();
+ editorPage.enterTitle(title);
+ editorPage.enterContent(content);
+ boolean isPublished = editorPage.publishPost();
+ assertTrue(isPublished);
+ }
+
+ @Test
+ public void testPublishFullPost() {
+ String title = "Hello Espresso!";
+ String content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
+ + "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud "
+ + "exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";
+ String category = "Wedding";
+ long now = Instant.now().toEpochMilli();
+ String tag = "Tag " + now;
+
+ EditorPage editorPage = new EditorPage();
+ editorPage.enterTitle(title);
+ editorPage.enterContent(content);
+ editorPage.enterImage();
+ editorPage.openSettings();
+
+ editorPage.addACategory(category);
+ editorPage.addATag(tag);
+ editorPage.setFeaturedImage();
+
+ // ----------------------------
+ // Verify post settings data
+ // ----------------------------
+ // Verify Category added
+ checkViewHasText(onView(withId(R.id.post_categories)), category);
+
+ // Verify tag added
+ checkViewHasText(onView(withId(R.id.post_tags)), tag);
+
+ // Verify the featured image added
+ waitForElementToNotBeDisplayed(onView(withText(R.string.post_settings_set_featured_image)));
+
+ // head back to the post
+ pressBack();
+
+ // publish
+ boolean isPublished = editorPage.publishPost();
+ assertTrue(isPublished);
+ }
+}
diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/pages/SiteSettingsPage.java b/WordPress/src/androidTest/java/org/wordpress/android/e2e/pages/SiteSettingsPage.java
index 05fb76c9f7e1..3bbf986c6902 100644
--- a/WordPress/src/androidTest/java/org/wordpress/android/e2e/pages/SiteSettingsPage.java
+++ b/WordPress/src/androidTest/java/org/wordpress/android/e2e/pages/SiteSettingsPage.java
@@ -4,12 +4,14 @@
import org.wordpress.android.R;
+import static androidx.test.espresso.Espresso.onData;
import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.scrollTo;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.PreferenceMatchers.withTitle;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static org.wordpress.android.support.WPSupportUtils.ensureSwitchPreferenceIsChecked;
public class SiteSettingsPage {
private static ViewInteraction settings = onView(withText(R.string.site_settings_title_title));
@@ -18,9 +20,15 @@ public SiteSettingsPage() {
settings.check(matches(isDisplayed()));
}
- public void toggleGutenbergSetting() {
- onView(withText(R.string.site_settings_gutenberg_default_for_new_posts))
+ public void setEditorToClassic() {
+ onData(withTitle(R.string.site_settings_gutenberg_default_for_new_posts))
.perform(scrollTo())
- .perform((click()));
+ .perform(ensureSwitchPreferenceIsChecked(false));
+ }
+
+ public void setEditorToGutenberg() {
+ onData(withTitle(R.string.site_settings_gutenberg_default_for_new_posts))
+ .perform(scrollTo())
+ .perform(ensureSwitchPreferenceIsChecked(true));
}
}
diff --git a/WordPress/src/androidTest/java/org/wordpress/android/support/WPSupportUtils.java b/WordPress/src/androidTest/java/org/wordpress/android/support/WPSupportUtils.java
index cc866c4da881..8dc8e239135d 100644
--- a/WordPress/src/androidTest/java/org/wordpress/android/support/WPSupportUtils.java
+++ b/WordPress/src/androidTest/java/org/wordpress/android/support/WPSupportUtils.java
@@ -6,22 +6,26 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
+import android.widget.Checkable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.test.espresso.AmbiguousViewMatcherException;
import androidx.test.espresso.Espresso;
+import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.action.GeneralClickAction;
import androidx.test.espresso.action.GeneralLocation;
import androidx.test.espresso.action.Press;
import androidx.test.espresso.action.Tap;
+import androidx.test.espresso.action.ViewActions;
import androidx.test.espresso.matcher.ViewMatchers.Visibility;
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObjectNotFoundException;
import androidx.test.uiautomator.UiSelector;
+import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
@@ -55,6 +59,7 @@
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isA;
public class WPSupportUtils {
@@ -134,6 +139,38 @@ public static ViewAction click(ViewAction rollbackAction) {
rollbackAction);
}
+ /** Ensures that a SwitchPreference isChecked is true or false regardless of the current value. This is useful to
+ * guarantee a particular resulting state where a previously toggled state may not be known.
+ * @param isChecked the value to set the preference to
+ * @return the ViewAction
+ */
+ public static ViewAction ensureSwitchPreferenceIsChecked(final boolean isChecked) {
+ return new ViewAction() {
+ @Override public BaseMatcher getConstraints() {
+ return new BaseMatcher() {
+ @Override public boolean matches(Object item) {
+ return isA(Checkable.class).matches(((View) item).findViewById(android.R.id.switch_widget));
+ }
+
+ @Override public void describeTo(Description description) {
+ description.appendText("checkable");
+ }
+ };
+ }
+
+ @Override public String getDescription() {
+ return "setChecked(" + isChecked + ")";
+ }
+
+ @Override public void perform(UiController uiController, View view) {
+ // perform click only if necessary
+ if (((Checkable) view.findViewById(android.R.id.switch_widget)).isChecked() != isChecked) {
+ ViewActions.click().perform(uiController, view);
+ }
+ }
+ };
+ }
+
private static boolean isResourceId(String text) {
diff --git a/WordPress/src/androidTest/java/org/wordpress/android/util/FeatureAnnouncementJsonValidityTest.kt b/WordPress/src/androidTest/java/org/wordpress/android/util/FeatureAnnouncementJsonValidityTest.kt
deleted file mode 100644
index d70213c52ea0..000000000000
--- a/WordPress/src/androidTest/java/org/wordpress/android/util/FeatureAnnouncementJsonValidityTest.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.wordpress.android.util
-
-import android.content.Context
-import androidx.test.platform.app.InstrumentationRegistry
-import com.google.gson.Gson
-import com.google.gson.JsonSyntaxException
-import org.junit.Assert.fail
-import org.junit.Test
-import org.wordpress.android.ui.whatsnew.FeatureAnnouncements
-import java.io.FileNotFoundException
-
-class FeatureAnnouncementJsonValidityTest {
- @Test
- fun testValidityOfFeatureAnnouncementJsonFile() {
- var featureAnnouncementFileContent: String? = null
-
- val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
- try {
- featureAnnouncementFileContent = context.assets.open("FEATURE_ANNOUNCEMENTS.json")
- .bufferedReader().use { it.readText() }
- } catch (fileNotFound: FileNotFoundException) {
- fail("FEATURE_ANNOUNCEMENTS.json is missing")
- }
-
- try {
- Gson().fromJson(
- featureAnnouncementFileContent,
- FeatureAnnouncements::class.java
- )
- } catch (jsonSyntaxException: JsonSyntaxException) {
- fail(
- "JsonSyntaxException when parsing FEATURE_ANNOUNCEMENTS.json:" +
- " ${jsonSyntaxException.message}"
- )
- }
- }
-}
diff --git a/WordPress/src/main/assets/FEATURE_ANNOUNCEMENTS.json b/WordPress/src/main/assets/FEATURE_ANNOUNCEMENTS.json
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/WordPress/src/main/java/org/wordpress/android/WordPress.java b/WordPress/src/main/java/org/wordpress/android/WordPress.java
index 1021996f6e03..ab2626db5fa6 100644
--- a/WordPress/src/main/java/org/wordpress/android/WordPress.java
+++ b/WordPress/src/main/java/org/wordpress/android/WordPress.java
@@ -104,6 +104,7 @@
import org.wordpress.android.util.UploadWorkerKt;
import org.wordpress.android.util.VolleyUtils;
import org.wordpress.android.util.analytics.AnalyticsUtils;
+import org.wordpress.android.util.config.AppConfig;
import org.wordpress.android.util.image.ImageManager;
import org.wordpress.android.widgets.AppRatingDialog;
@@ -162,6 +163,7 @@ public class WordPress extends MultiDexApplication implements HasServiceInjector
@Inject PrivateAtomicCookie mPrivateAtomicCookie;
@Inject ImageEditorTracker mImageEditorTracker;
@Inject CrashLogging mCrashLogging;
+ @Inject AppConfig mAppConfig;
// For development and production `AnalyticsTrackerNosara`, for testing a mocked `Tracker` will be injected.
@Inject Tracker mTracker;
@@ -790,6 +792,7 @@ public AndroidInjector serviceInjector() {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
void onAppComesFromBackground() {
mApplicationLifecycleMonitor.onAppComesFromBackground();
+ mAppConfig.refresh();
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
diff --git a/WordPress/src/main/java/org/wordpress/android/models/ReaderCardType.java b/WordPress/src/main/java/org/wordpress/android/models/ReaderCardType.java
index e9df3ca472dc..a35d83737512 100644
--- a/WordPress/src/main/java/org/wordpress/android/models/ReaderCardType.java
+++ b/WordPress/src/main/java/org/wordpress/android/models/ReaderCardType.java
@@ -4,7 +4,6 @@
import org.wordpress.android.ui.reader.ReaderConstants;
import org.wordpress.android.ui.reader.utils.ReaderImageScanner;
-import org.wordpress.android.ui.reader.views.ReaderThumbnailStrip;
import org.wordpress.android.util.HtmlUtils;
/**
@@ -39,7 +38,8 @@ public static ReaderCardType fromReaderPost(ReaderPost post) {
if (!post.hasFeaturedImage()
&& post.hasImages()
&& new ReaderImageScanner(post.getText(), post.isPrivate)
- .hasUsableImageCount(ReaderThumbnailStrip.IMAGE_COUNT, ReaderConstants.MIN_GALLERY_IMAGE_WIDTH)) {
+ .hasUsableImageCount(ReaderConstants.THUMBNAIL_STRIP_IMG_COUNT,
+ ReaderConstants.MIN_GALLERY_IMAGE_WIDTH)) {
return GALLERY;
}
diff --git a/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java b/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java
index 1f3604038648..bf9ca9e3c944 100644
--- a/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java
+++ b/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java
@@ -75,6 +75,7 @@
import org.wordpress.android.ui.people.RoleChangeDialogFragment;
import org.wordpress.android.ui.people.RoleSelectDialogFragment;
import org.wordpress.android.ui.photopicker.PhotoPickerActivity;
+import org.wordpress.android.ui.photopicker.PhotoPickerFragment;
import org.wordpress.android.ui.plans.PlanDetailsFragment;
import org.wordpress.android.ui.plans.PlansActivity;
import org.wordpress.android.ui.plans.PlansListAdapter;
@@ -542,6 +543,8 @@ public interface AppComponent extends AndroidInjector {
void inject(AztecVideoLoader object);
+ void inject(PhotoPickerFragment object);
+
// Allows us to inject the application without having to instantiate any modules, and provides the Application
// in the app graph
@Component.Builder
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java
index 5da4ff020d06..8f767aa2423c 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java
@@ -216,6 +216,7 @@ public static void showGifPickerForResult(Activity activity, @NonNull SiteModel
Intent intent = new Intent(activity, GifPickerActivity.class);
intent.putExtra(WordPress.SITE, site);
+ intent.putExtra(GifPickerActivity.KEY_REQUEST_CODE, requestCode);
activity.startActivityForResult(intent, requestCode);
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/FullScreenDialogFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/FullScreenDialogFragment.java
index 266beaa5fbfa..c44cf7388477 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/FullScreenDialogFragment.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/FullScreenDialogFragment.java
@@ -64,7 +64,7 @@ public interface FullScreenDialogContent {
boolean onDismissClicked(FullScreenDialogController controller);
- void onViewCreated(FullScreenDialogController controller);
+ void setController(FullScreenDialogController controller);
}
public interface FullScreenDialogController {
@@ -181,7 +181,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- ((FullScreenDialogContent) getContent()).onViewCreated(mController);
+ ((FullScreenDialogContent) getContent()).setController(mController);
}
@Override
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java b/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java
index f1cbaf40e95d..246e40fb2f3f 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java
@@ -48,7 +48,8 @@ public class RequestCodes {
// QuickStart
public static final int QUICK_START_REMINDER_RECEIVER = 4000;
- public static final int GIF_PICKER = 3200;
+ public static final int GIF_PICKER_SINGLE_SELECT = 3200;
+ public static final int GIF_PICKER_MULTI_SELECT = 3201;
// Domain Registration
public static final int DOMAIN_REGISTRATION = 5000;
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/BaseUsernameChangerFullScreenDialogFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/BaseUsernameChangerFullScreenDialogFragment.java
index b144c03b91e2..7ffb56f2ab76 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/BaseUsernameChangerFullScreenDialogFragment.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/BaseUsernameChangerFullScreenDialogFragment.java
@@ -14,6 +14,7 @@
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -135,20 +136,26 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
}
@Override
- public void onViewCreated(final FullScreenDialogController controller) {
+ public void setController(final FullScreenDialogController controller) {
mDialogController = controller;
}
@Override
- public void onActivityCreated(@Nullable final Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
if (savedInstanceState != null) {
mIsShowingDismissDialog = savedInstanceState.getBoolean(KEY_IS_SHOWING_DISMISS_DIALOG);
mShouldWatchText = savedInstanceState.getBoolean(KEY_SHOULD_WATCH_TEXT);
mUsernameSelected = savedInstanceState.getString(KEY_USERNAME_SELECTED);
mUsernameSelectedIndex = savedInstanceState.getInt(KEY_USERNAME_SELECTED_INDEX);
- setUsernameSuggestions(savedInstanceState.getStringArrayList(KEY_USERNAME_SUGGESTIONS));
+ ArrayList suggestions = savedInstanceState.getStringArrayList(KEY_USERNAME_SUGGESTIONS);
+ if (suggestions != null) {
+ setUsernameSuggestions(suggestions);
+ } else {
+ mUsernameSuggestionInput = getUsernameQueryFromDisplayName();
+ getUsernameSuggestions(mUsernameSuggestionInput);
+ }
if (mIsShowingDismissDialog) {
showDismissDialog();
@@ -209,6 +216,12 @@ public void onStop() {
mDispatcher.unregister(this);
}
+ @Override
+ public void onDestroy() {
+ mGetSuggestionsHandler.removeCallbacksAndMessages(null);
+ super.onDestroy();
+ }
+
@Override
public boolean onConfirmClicked(FullScreenDialogController controller) {
ActivityUtils.hideKeyboard(getActivity());
@@ -244,7 +257,9 @@ public void onSaveInstanceState(@NotNull Bundle outState) {
outState.putBoolean(KEY_SHOULD_WATCH_TEXT, false);
outState.putString(KEY_USERNAME_SELECTED, mUsernameSelected);
outState.putInt(KEY_USERNAME_SELECTED_INDEX, mUsernameSelectedIndex);
- outState.putStringArrayList(KEY_USERNAME_SUGGESTIONS, new ArrayList<>(mUsernamesAdapter.mItems));
+ if (mUsernamesAdapter != null) {
+ outState.putStringArrayList(KEY_USERNAME_SUGGESTIONS, new ArrayList<>(mUsernamesAdapter.mItems));
+ }
}
@Override
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SettingsUsernameChangerFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SettingsUsernameChangerFragment.kt
index 82693315b4ec..d0f5253391d5 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SettingsUsernameChangerFragment.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SettingsUsernameChangerFragment.kt
@@ -46,8 +46,8 @@ class SettingsUsernameChangerFragment : BaseUsernameChangerFullScreenDialogFragm
), HtmlCompat.FROM_HTML_MODE_LEGACY
)
- override fun onViewCreated(controller: FullScreenDialogController) {
- super.onViewCreated(controller)
+ override fun setController(controller: FullScreenDialogController) {
+ super.setController(controller)
dialogController = controller
dialogController.setActionEnabled(false)
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/gif/GifMediaViewHolder.kt b/WordPress/src/main/java/org/wordpress/android/ui/gif/GifMediaViewHolder.kt
index 610256836843..d3fb031176e0 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/gif/GifMediaViewHolder.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/gif/GifMediaViewHolder.kt
@@ -10,6 +10,7 @@ import androidx.lifecycle.Observer
import kotlinx.android.synthetic.main.media_picker_thumbnail.view.*
import org.wordpress.android.R
import org.wordpress.android.util.AniUtils
+import org.wordpress.android.util.AniUtils.Duration.MEDIUM
import org.wordpress.android.util.getDistinct
import org.wordpress.android.util.image.ImageManager
import org.wordpress.android.util.image.ImageType.PHOTO
@@ -47,7 +48,8 @@ class GifMediaViewHolder(
/**
* The dimensions used for the ImageView
*/
- thumbnailViewDimensions: ThumbnailViewDimensions
+ thumbnailViewDimensions: ThumbnailViewDimensions,
+ private val isMultiSelectEnabled: Boolean
) : LifecycleOwnerViewHolder(itemView) {
data class ThumbnailViewDimensions(val width: Int, val height: Int)
@@ -85,14 +87,14 @@ class GifMediaViewHolder(
// Immediately update the selection number and scale the thumbnail when a bind happens
val isSelected = mediaViewModel?.isSelected?.value ?: false
- updateNumberTextOnSelectionChange(isSelected = isSelected, animated = false)
+ updateSelectionIndicatorOnSelectionChange(isSelected = isSelected, animated = false)
updateThumbnailOnSelectionChange(isSelected = isSelected, animated = false)
// When the [isSelected] property changes later, update the selection number and scale the thumbnail
mediaViewModel?.isSelected?.observe(this, Observer {
val selected = it ?: false
- updateNumberTextOnSelectionChange(isSelected = selected, animated = true)
+ updateSelectionIndicatorOnSelectionChange(isSelected = selected, animated = true)
updateThumbnailOnSelectionChange(isSelected = selected, animated = true)
})
@@ -106,12 +108,27 @@ class GifMediaViewHolder(
imageManager.load(thumbnailView, PHOTO, mediaViewModel?.thumbnailUri.toString(), CENTER_CROP)
}
- private fun updateNumberTextOnSelectionChange(isSelected: Boolean, animated: Boolean) {
+ private fun updateSelectionIndicatorOnSelectionChange(isSelected: Boolean, animated: Boolean) {
// The `isSelected` here changes the color of the text. It will be blue when selected.
selectionNumberTextView.isSelected = isSelected
+ if (!isMultiSelectEnabled) {
+ selectionNumberTextView.visibility = if (isSelected) {
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
+ }
if (animated) {
- AniUtils.startAnimation(selectionNumberTextView, R.anim.pop)
+ if (!isMultiSelectEnabled) {
+ if (isSelected) {
+ AniUtils.scaleIn(selectionNumberTextView, MEDIUM)
+ } else {
+ AniUtils.scaleOut(selectionNumberTextView, MEDIUM)
+ }
+ } else {
+ AniUtils.startAnimation(selectionNumberTextView, R.anim.pop)
+ }
}
}
@@ -144,7 +161,8 @@ class GifMediaViewHolder(
onClickListener: (GifMediaViewModel?) -> Unit,
onLongClickListener: (GifMediaViewModel) -> Unit,
parent: ViewGroup,
- thumbnailViewDimensions: ThumbnailViewDimensions
+ thumbnailViewDimensions: ThumbnailViewDimensions,
+ isMultiSelectEnabled: Boolean
): GifMediaViewHolder {
// We are intentionally reusing this layout since the UI is very similar.
val view = LayoutInflater.from(parent.context)
@@ -154,7 +172,8 @@ class GifMediaViewHolder(
onClickListener = onClickListener,
onLongClickListener = onLongClickListener,
itemView = view,
- thumbnailViewDimensions = thumbnailViewDimensions
+ thumbnailViewDimensions = thumbnailViewDimensions,
+ isMultiSelectEnabled = isMultiSelectEnabled
)
}
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/gif/GifPickerActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/gif/GifPickerActivity.kt
index babf2dc45000..1887d66270a9 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/gif/GifPickerActivity.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/gif/GifPickerActivity.kt
@@ -19,8 +19,9 @@ import org.wordpress.android.WordPress
import org.wordpress.android.analytics.AnalyticsTracker
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.ui.ActionableEmptyView
-import org.wordpress.android.ui.gif.GifMediaViewHolder.ThumbnailViewDimensions
import org.wordpress.android.ui.LocaleAwareActivity
+import org.wordpress.android.ui.RequestCodes
+import org.wordpress.android.ui.gif.GifMediaViewHolder.ThumbnailViewDimensions
import org.wordpress.android.ui.media.MediaPreviewActivity
import org.wordpress.android.util.AniUtils
import org.wordpress.android.util.DisplayUtils
@@ -48,6 +49,8 @@ class GifPickerActivity : LocaleAwareActivity() {
private lateinit var viewModel: GifPickerViewModel
+ private var isMultiSelectEnabled: Boolean = false
+
private val gridColumnCount: Int by lazy { if (DisplayUtils.isLandscape(this)) 4 else 3 }
/**
@@ -63,9 +66,11 @@ class GifPickerActivity : LocaleAwareActivity() {
(application as WordPress).component().inject(this)
val site = intent.getSerializableExtra(WordPress.SITE) as SiteModel
+ val requestCode = intent.getIntExtra(KEY_REQUEST_CODE, 0)
+ isMultiSelectEnabled = requestCode == RequestCodes.GIF_PICKER_MULTI_SELECT
viewModel = ViewModelProviders.of(this, viewModelFactory).get(GifPickerViewModel::class.java)
- viewModel.setup(site)
+ viewModel.start(site, isMultiSelectEnabled)
// We are intentionally reusing this layout since the UI is very similar.
setContentView(R.layout.media_picker_activity)
@@ -106,7 +111,8 @@ class GifPickerActivity : LocaleAwareActivity() {
viewModel.retryAllFailedRangeLoads()
}
},
- onMediaViewLongClickListener = { showPreview(listOf(it)) }
+ onMediaViewLongClickListener = { showPreview(listOf(it)) },
+ isMultiSelectEnabled = isMultiSelectEnabled
)
recycler.apply {
@@ -153,10 +159,26 @@ class GifPickerActivity : LocaleAwareActivity() {
* Configure the selection bar and its labels when the [GifPickerViewModel] selected items change
*/
private fun initializeSelectionBar() {
- viewModel.selectionBarIsVisible.observe(this, Observer {
- // Do nothing if the [viewModel.selectionBarIsVisible] has not been initialized with a value yet. The
- // selection bar is hidden by default anyway so we don't need to worry in this case.
- val isVisible = it ?: return@Observer
+ viewModel.selectionBarUiModel.observe(this, Observer { uiModel ->
+ if (uiModel.isMultiselectEnabled) {
+ // Update the "Add" and "Preview" labels to include the number of items. For example, "Add 7" and "Preview 7".
+ //
+ // We do not change to labels back to the original text if the number of items go back to zero because that
+ // causes a weird UX. The selection bar is animated to disappear at that time and it looks weird if the labels
+ // change to just "Add" and "Preview" too.
+ val selectedCount = uiModel.numberOfSelectedImages
+ if (selectedCount > 0) {
+ text_preview.text = getString(R.string.preview_count, selectedCount)
+ text_add.text = getString(R.string.add_count, selectedCount)
+ }
+ } else {
+ // When in single selection mode we only show ADD label
+ text_add.text = getString(R.string.photo_picker_use_gif)
+ text_add.visibility = View.VISIBLE
+ text_preview.visibility = View.GONE
+ }
+
+ val isVisible = uiModel.isVisible
val selectionBar: ViewGroup = container_selection_bar
// Do nothing if the selection bar is already in the visibility state that we want it to be
@@ -178,19 +200,6 @@ class GifPickerActivity : LocaleAwareActivity() {
recyclerViewLayoutParams.addRule(RelativeLayout.ABOVE, 0)
}
})
-
- // Update the "Add" and "Preview" labels to include the number of items. For example, "Add 7" and "Preview 7".
- //
- // We do not change to labels back to the original text if the number of items go back to zero because that
- // causes a weird UX. The selection bar is animated to disappear at that time and it looks weird if the labels
- // change to just "Add" and "Preview" too.
- viewModel.selectedMediaViewModelList.observe(this, Observer {
- val selectedCount = it?.size ?: 0
- if (selectedCount > 0) {
- text_preview.text = getString(R.string.preview_count, selectedCount)
- text_add.text = getString(R.string.add_count, selectedCount)
- }
- })
}
/**
@@ -366,5 +375,6 @@ class GifPickerActivity : LocaleAwareActivity() {
* Added to this Activity's result as an Int array [org.wordpress.android.fluxc.model.MediaModel] `id` values.
*/
const val KEY_SAVED_MEDIA_MODEL_LOCAL_IDS = "saved_media_model_local_ids"
+ const val KEY_REQUEST_CODE = "gif_picker_key_request_code"
}
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/gif/GifPickerPagedListAdapter.kt b/WordPress/src/main/java/org/wordpress/android/ui/gif/GifPickerPagedListAdapter.kt
index 29350ba3c590..daf0ea175567 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/gif/GifPickerPagedListAdapter.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/gif/GifPickerPagedListAdapter.kt
@@ -14,7 +14,8 @@ class GifPickerPagedListAdapter(
private val imageManager: ImageManager,
private val thumbnailViewDimensions: ThumbnailViewDimensions,
private val onMediaViewClickListener: (GifMediaViewModel?) -> Unit,
- private val onMediaViewLongClickListener: (GifMediaViewModel) -> Unit
+ private val onMediaViewLongClickListener: (GifMediaViewModel) -> Unit,
+ private val isMultiSelectEnabled: Boolean
) : PagedListAdapter(DIFF_CALLBACK) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GifMediaViewHolder {
return GifMediaViewHolder.create(
@@ -22,7 +23,8 @@ class GifPickerPagedListAdapter(
onClickListener = onMediaViewClickListener,
onLongClickListener = onMediaViewLongClickListener,
parent = parent,
- thumbnailViewDimensions = thumbnailViewDimensions
+ thumbnailViewDimensions = thumbnailViewDimensions,
+ isMultiSelectEnabled = isMultiSelectEnabled
)
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/MySiteFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/MySiteFragment.kt
index 8bcab019a61b..ed03b719f5ac 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/main/MySiteFragment.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/main/MySiteFragment.kt
@@ -1213,16 +1213,25 @@ class MySiteFragment : Fragment(),
val focusPointSize = resources.getDimensionPixelOffset(R.dimen.quick_start_focus_point_size)
val horizontalOffset: Int
val verticalOffset: Int
- if (isTargetingBottomNavBar(activeTutorialPrompt!!.task)) {
- horizontalOffset = quickStartTarget.width / 2 - focusPointSize + resources
- .getDimensionPixelOffset(R.dimen.quick_start_focus_point_bottom_nav_offset)
- verticalOffset = 0
- } else if (activeTutorialPrompt!!.task == UPLOAD_SITE_ICON) {
- horizontalOffset = focusPointSize
- verticalOffset = -focusPointSize / 2
- } else {
- horizontalOffset = resources.getDimensionPixelOffset(R.dimen.quick_start_focus_point_my_site_right_offset)
- verticalOffset = (quickStartTarget.height - focusPointSize) / 2
+ when {
+ isTargetingBottomNavBar(activeTutorialPrompt!!.task) -> {
+ horizontalOffset = quickStartTarget.width / 2 - focusPointSize + resources
+ .getDimensionPixelOffset(R.dimen.quick_start_focus_point_bottom_nav_offset)
+ verticalOffset = 0
+ }
+ activeTutorialPrompt!!.task == UPLOAD_SITE_ICON -> {
+ horizontalOffset = focusPointSize
+ verticalOffset = -focusPointSize / 2
+ }
+ activeTutorialPrompt!!.task == VIEW_SITE -> { // focus point might be hidden behind FAB
+ horizontalOffset = (focusPointSize / 0.5).toInt()
+ verticalOffset = (quickStartTarget.height - focusPointSize) / 2
+ }
+ else -> {
+ horizontalOffset =
+ resources.getDimensionPixelOffset(R.dimen.quick_start_focus_point_my_site_right_offset)
+ verticalOffset = (quickStartTarget.height - focusPointSize) / 2
+ }
}
addQuickStartFocusPointAboveTheView(
parentView, quickStartTarget, horizontalOffset,
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java
index 2e2ec3dd046e..8970e95bb8ae 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java
@@ -182,6 +182,7 @@ public class WPMainActivity extends LocaleAwareActivity implements
private FloatingActionButton mFloatingActionButton;
private WPTooltipView mFabTooltip;
private static final String MAIN_BOTTOM_SHEET_TAG = "MAIN_BOTTOM_SHEET_TAG";
+ private final Handler mHandler = new Handler();
@Inject AccountStore mAccountStore;
@Inject SiteStore mSiteStore;
@@ -228,20 +229,14 @@ public void onCreate(Bundle savedInstanceState) {
mBottomNav.init(getSupportFragmentManager(), this);
mConnectionBar = findViewById(R.id.connection_bar);
- mConnectionBar.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // slide out the bar on click, then re-check connection after a brief delay
- AniUtils.animateBottomBar(mConnectionBar, false);
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- if (!isFinishing()) {
- checkConnection();
- }
- }
- }, 2000);
- }
+ mConnectionBar.setOnClickListener(v -> {
+ // slide out the bar on click, then re-check connection after a brief delay
+ AniUtils.animateBottomBar(mConnectionBar, false);
+ mHandler.postDelayed(() -> {
+ if (!isFinishing()) {
+ checkConnection();
+ }
+ }, 2000);
});
mIsMagicLinkLogin = getIntent().getBooleanExtra(ARG_IS_MAGIC_LINK_LOGIN, false);
@@ -693,6 +688,7 @@ private void launchWithPostId(int postId, boolean isPage) {
protected void onDestroy() {
EventBus.getDefault().unregister(this);
mDispatcher.unregister(this);
+ mHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainNavigationView.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainNavigationView.kt
index f5b0b5374ebb..cb6d2606aaf3 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainNavigationView.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainNavigationView.kt
@@ -27,7 +27,6 @@ import org.wordpress.android.ui.main.WPMainNavigationView.PageType.READER
import org.wordpress.android.ui.notifications.NotificationsListFragment
import org.wordpress.android.ui.prefs.AppPrefs
import org.wordpress.android.ui.reader.ReaderFragment
-import org.wordpress.android.ui.reader.discover.interests.ReaderInterestsFragment
import org.wordpress.android.util.AniUtils
import org.wordpress.android.util.AniUtils.Duration
import org.wordpress.android.util.getColorStateListFromAttribute
@@ -283,11 +282,7 @@ class WPMainNavigationView @JvmOverloads constructor(
private fun createFragment(pageType: PageType): Fragment {
val fragment = when (pageType) {
MY_SITE -> MySiteFragment.newInstance()
- READER -> if (AppPrefs.isReaderImprovementsPhase2Enabled()) {
- ReaderInterestsFragment() // TODO: Temporary entry point
- } else {
- ReaderFragment()
- }
+ READER -> ReaderFragment()
NOTIFS -> NotificationsListFragment.newInstance()
}
fragmentManager.beginTransaction()
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/media/MediaBrowserActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/media/MediaBrowserActivity.java
index 98756519419e..8a36512a8550 100755
--- a/WordPress/src/main/java/org/wordpress/android/ui/media/MediaBrowserActivity.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/media/MediaBrowserActivity.java
@@ -84,6 +84,7 @@
import org.wordpress.android.util.WPMediaUtils;
import org.wordpress.android.util.WPPermissionUtils;
import org.wordpress.android.util.analytics.AnalyticsUtils;
+import org.wordpress.android.util.config.TenorFeatureConfig;
import org.wordpress.android.widgets.AppRatingDialog;
import java.util.ArrayList;
@@ -113,6 +114,7 @@ public class MediaBrowserActivity extends LocaleAwareActivity implements MediaGr
@Inject MediaStore mMediaStore;
@Inject SiteStore mSiteStore;
@Inject UploadUtilsWrapper mUploadUtilsWrapper;
+ @Inject TenorFeatureConfig mTenorFeatureConfig;
private SiteModel mSite;
@@ -487,7 +489,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
reloadMediaGrid();
}
break;
- case RequestCodes.GIF_PICKER:
+ case RequestCodes.GIF_PICKER_MULTI_SELECT:
if (resultCode == RESULT_OK
&& data.hasExtra(GifPickerActivity.KEY_SAVED_MEDIA_MODEL_LOCAL_IDS)) {
int[] mediaLocalIds = data.getIntArrayExtra(GifPickerActivity.KEY_SAVED_MEDIA_MODEL_LOCAL_IDS);
@@ -915,7 +917,7 @@ public void showAddMediaPopup() {
});
}
- if (mBrowserType.isBrowser() && BuildConfig.TENOR_AVAILABLE) {
+ if (mBrowserType.isBrowser() && mTenorFeatureConfig.isEnabled()) {
popup.getMenu().add(R.string.photo_picker_gif).setOnMenuItemClickListener(
item -> {
doAddMediaItemClicked(AddMenuItem.ITEM_CHOOSE_GIF);
@@ -961,7 +963,7 @@ private void doAddMediaItemClicked(@NonNull AddMenuItem item) {
mSite, RequestCodes.STOCK_MEDIA_PICKER_MULTI_SELECT);
break;
case ITEM_CHOOSE_GIF:
- ActivityLauncher.showGifPickerForResult(this, mSite, RequestCodes.GIF_PICKER);
+ ActivityLauncher.showGifPickerForResult(this, mSite, RequestCodes.GIF_PICKER_MULTI_SELECT);
break;
}
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/media/MediaSettingsActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/media/MediaSettingsActivity.java
index a28ee1a1b72e..0c327bfbc988 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/media/MediaSettingsActivity.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/media/MediaSettingsActivity.java
@@ -773,7 +773,14 @@ public void onLoadFailed(@Nullable Exception e, @Nullable Object model) {
AppLog.e(T.MEDIA, e);
}
showProgress(false);
- delayedFinishWithError();
+ if (isVideo()) {
+ // for videos it's ok if we fail to load the thumbnail - can happen.
+ // let's show a toast but let the user edit the media settings!
+ ToastUtils.showToast(MediaSettingsActivity.this,
+ R.string.error_media_thumbnail_not_loaded);
+ } else {
+ delayedFinishWithError();
+ }
}
}
});
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/pages/PageItem.kt b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageItem.kt
index a8a99d8d3d4f..24052fd633fd 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/pages/PageItem.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageItem.kt
@@ -26,7 +26,8 @@ sealed class PageItem(open val type: Type) {
open var actionsEnabled: Boolean,
open val tapActionEnabled: Boolean,
open val progressBarUiState: ProgressBarUiState,
- open val showOverlay: Boolean
+ open val showOverlay: Boolean,
+ open val author: String?
) : PageItem(PAGE)
data class PublishedPage(
@@ -42,7 +43,8 @@ sealed class PageItem(open val type: Type) {
override val actions: Set,
override var actionsEnabled: Boolean = true,
override val progressBarUiState: ProgressBarUiState,
- override val showOverlay: Boolean
+ override val showOverlay: Boolean,
+ override val author: String? = null
) : Page(
remoteId = remoteId,
localId = localId,
@@ -56,7 +58,8 @@ sealed class PageItem(open val type: Type) {
actionsEnabled = actionsEnabled,
tapActionEnabled = true,
progressBarUiState = progressBarUiState,
- showOverlay = showOverlay
+ showOverlay = showOverlay,
+ author = author
)
data class DraftPage(
@@ -71,7 +74,8 @@ sealed class PageItem(open val type: Type) {
override val actions: Set,
override var actionsEnabled: Boolean = true,
override val progressBarUiState: ProgressBarUiState,
- override val showOverlay: Boolean
+ override val showOverlay: Boolean,
+ override val author: String? = null
) : Page(
remoteId = remoteId,
localId = localId,
@@ -85,7 +89,8 @@ sealed class PageItem(open val type: Type) {
actionsEnabled = actionsEnabled,
tapActionEnabled = true,
progressBarUiState = progressBarUiState,
- showOverlay = showOverlay
+ showOverlay = showOverlay,
+ author = author
)
data class ScheduledPage(
@@ -100,7 +105,8 @@ sealed class PageItem(open val type: Type) {
override val actions: Set,
override var actionsEnabled: Boolean = true,
override val progressBarUiState: ProgressBarUiState,
- override val showOverlay: Boolean
+ override val showOverlay: Boolean,
+ override val author: String? = null
) : Page(
remoteId = remoteId,
localId = localId,
@@ -114,7 +120,8 @@ sealed class PageItem(open val type: Type) {
actionsEnabled = actionsEnabled,
tapActionEnabled = true,
progressBarUiState = progressBarUiState,
- showOverlay = showOverlay
+ showOverlay = showOverlay,
+ author = author
)
data class TrashedPage(
@@ -129,7 +136,8 @@ sealed class PageItem(open val type: Type) {
override val actions: Set,
override var actionsEnabled: Boolean = true,
override val progressBarUiState: ProgressBarUiState,
- override val showOverlay: Boolean
+ override val showOverlay: Boolean,
+ override val author: String? = null
) : Page(
remoteId = remoteId,
localId = localId,
@@ -143,7 +151,9 @@ sealed class PageItem(open val type: Type) {
actionsEnabled = actionsEnabled,
tapActionEnabled = false,
progressBarUiState = progressBarUiState,
- showOverlay = showOverlay
+ showOverlay = showOverlay,
+ author = author
+
)
data class ParentPage(
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/pages/PageItemViewHolder.kt b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageItemViewHolder.kt
index 3e7063f8bffe..82367387ccd1 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/pages/PageItemViewHolder.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageItemViewHolder.kt
@@ -80,20 +80,7 @@ sealed class PageItemViewHolder(internal val parent: ViewGroup, @LayoutRes layou
else
page.title
- val date = if (page.date == Date(0)) Date() else page.date
- val stringDate = DateTimeUtils.javaDateToTimeSpan(date, parent.context)
- .capitalizeWithLocaleWithoutLint(parent.context.currentLocale)
- val subtitle = page.subtitle
- pageSubtitle.text = if (subtitle == null) {
- stringDate
- } else {
- String.format(
- Locale.getDefault(),
- parent.context.getString(R.string.pages_item_subtitle),
- stringDate,
- parent.context.getString(subtitle)
- )
- }
+ showSubtitle(page.date, page.author, page.subtitle)
labels.text = page.labels.map { uiHelper.getTextOfUiString(parent.context, it) }.sorted()
.joinToString(separator = " · ")
@@ -176,6 +163,44 @@ sealed class PageItemViewHolder(internal val parent: ViewGroup, @LayoutRes layou
}
}
}
+
+ @ExperimentalStdlibApi
+ private fun showSubtitle(inputDate: Date, author: String?, subtitle: Int?) {
+ val date = if (inputDate == Date(0)) Date() else inputDate
+ val stringDate = DateTimeUtils.javaDateToTimeSpan(date, parent.context)
+ .capitalizeWithLocaleWithoutLint(parent.context.currentLocale)
+
+ /** The subtitle can use 2 or 3 placeholders
+ * Date - Only (author & subtitle are null)
+ * Date - Author (author != null && subtitle == null)
+ * Date - subtitle (author == null && subtitle != null)
+ * Date - Author - subtitle (all have values)
+ */
+ pageSubtitle.text = if (author == null && subtitle == null) {
+ stringDate
+ } else if (author != null && subtitle == null) {
+ String.format(
+ Locale.getDefault(),
+ parent.context.getString(R.string.pages_item_subtitle),
+ stringDate,
+ author)
+ } else if (author == null && subtitle != null) {
+ String.format(
+ Locale.getDefault(),
+ parent.context.getString(R.string.pages_item_subtitle),
+ stringDate,
+ parent.context.getString(subtitle))
+ } else {
+ subtitle?.let {
+ String.format(
+ Locale.getDefault(),
+ parent.context.getString(R.string.pages_item_subtitle_date_author),
+ stringDate,
+ author,
+ parent.context.getString(it))
+ }
+ }
+ }
}
class PageDividerViewHolder(parentView: ViewGroup) : PageItemViewHolder(parentView, R.layout.page_divider_item) {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/pages/PagesAuthorFilterUIState.kt b/WordPress/src/main/java/org/wordpress/android/ui/pages/PagesAuthorFilterUIState.kt
new file mode 100644
index 000000000000..aeeb266bd458
--- /dev/null
+++ b/WordPress/src/main/java/org/wordpress/android/ui/pages/PagesAuthorFilterUIState.kt
@@ -0,0 +1,10 @@
+package org.wordpress.android.ui.pages
+
+import org.wordpress.android.ui.posts.AuthorFilterListItemUIState
+import org.wordpress.android.ui.posts.AuthorFilterSelection
+
+data class PagesAuthorFilterUIState(
+ val isAuthorFilterVisible: Boolean,
+ val authorFilterSelection: AuthorFilterSelection,
+ val authorFilterItems: List
+)
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/pages/PagesFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/pages/PagesFragment.kt
index 3bc6a82ef505..668ccd53ef43 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/pages/PagesFragment.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/pages/PagesFragment.kt
@@ -14,6 +14,7 @@ import android.view.MenuItem.OnActionExpandListener
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
+import android.widget.AdapterView
import android.widget.LinearLayout
import android.widget.Toast
import androidx.appcompat.widget.SearchView
@@ -47,6 +48,7 @@ import org.wordpress.android.ui.posts.PostListAction.PreviewPost
import org.wordpress.android.ui.posts.PreviewStateHelper
import org.wordpress.android.ui.posts.ProgressDialogHelper
import org.wordpress.android.ui.posts.RemotePreviewLogicHelper
+import org.wordpress.android.ui.posts.adapters.AuthorSelectionAdapter
import org.wordpress.android.ui.quickstart.QuickStartEvent
import org.wordpress.android.ui.uploads.UploadActionUseCase
import org.wordpress.android.ui.uploads.UploadUtilsWrapper
@@ -94,6 +96,8 @@ class PagesFragment : Fragment() {
private var restorePreviousSearch = false
+ private lateinit var authorSelectionAdapter: AuthorSelectionAdapter
+
companion object {
fun newInstance(): PagesFragment {
return PagesFragment()
@@ -204,6 +208,16 @@ class PagesFragment : Fragment() {
}
return@setOnTouchListener false
}
+
+ authorSelectionAdapter = AuthorSelectionAdapter(activity)
+ pages_author_selection.adapter = authorSelectionAdapter
+ pages_author_selection.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
+ override fun onNothingSelected(parent: AdapterView<*>) {}
+
+ override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
+ viewModel.updateAuthorFilterSelection(id)
+ }
+ }
}
private fun initializeSearchView() {
@@ -264,6 +278,25 @@ class PagesFragment : Fragment() {
savedInstanceState.getSerializable(WordPress.SITE) as SiteModel
}
+ viewModel.authorUIState.observe(activity, Observer { state ->
+ state?.let {
+ uiHelpers.updateVisibility(pages_author_selection, state.isAuthorFilterVisible)
+ uiHelpers.updateVisibility(pages_tab_layout_fading_edge, state.isAuthorFilterVisible)
+
+ val tabLayoutPaddingStart =
+ if (state.isAuthorFilterVisible)
+ resources.getDimensionPixelSize(R.dimen.posts_list_tab_layout_fading_edge_width)
+ else 0
+ tabLayout.setPaddingRelative(tabLayoutPaddingStart, 0, 0, 0)
+
+ authorSelectionAdapter.updateItems(state.authorFilterItems)
+
+ authorSelectionAdapter.getIndexOfSelection(state.authorFilterSelection)?.let { selectionIndex ->
+ pages_author_selection.setSelection(selectionIndex)
+ }
+ }
+ })
+
viewModel.start(site)
}
@@ -428,6 +461,7 @@ class PagesFragment : Fragment() {
private fun hideSearchList(myActionMenuItem: MenuItem) {
pagesPager.visibility = View.VISIBLE
tabLayout.visibility = View.VISIBLE
+ tabContainer.visibility = View.VISIBLE
searchFrame.visibility = View.GONE
if (myActionMenuItem.isActionViewExpanded) {
myActionMenuItem.collapseActionView()
@@ -437,6 +471,7 @@ class PagesFragment : Fragment() {
private fun showSearchList(myActionMenuItem: MenuItem) {
pagesPager.visibility = View.GONE
tabLayout.visibility = View.GONE
+ tabContainer.visibility = View.GONE
searchFrame.visibility = View.VISIBLE
if (!myActionMenuItem.isActionViewExpanded) {
myActionMenuItem.expandActionView()
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/photopicker/PhotoPickerFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/photopicker/PhotoPickerFragment.java
index f9c379762c2d..ca1c23500d92 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/photopicker/PhotoPickerFragment.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/photopicker/PhotoPickerFragment.java
@@ -25,7 +25,6 @@
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import org.wordpress.android.BuildConfig;
import org.wordpress.android.R;
import org.wordpress.android.WordPress;
import org.wordpress.android.analytics.AnalyticsTracker;
@@ -45,12 +44,15 @@
import org.wordpress.android.util.WPMediaUtils;
import org.wordpress.android.util.WPPermissionUtils;
import org.wordpress.android.util.analytics.AnalyticsUtils;
+import org.wordpress.android.util.config.TenorFeatureConfig;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import javax.inject.Inject;
+
public class PhotoPickerFragment extends Fragment {
private static final String KEY_LAST_TAPPED_ICON = "last_tapped_icon";
private static final String KEY_SELECTED_POSITIONS = "selected_positions";
@@ -102,6 +104,8 @@ public interface PhotoPickerListener {
private SiteModel mSite;
private ArrayList mSelectedPositions;
+ @Inject TenorFeatureConfig mTenorFeatureConfig;
+
public static PhotoPickerFragment newInstance(@NonNull PhotoPickerListener listener,
@NonNull MediaBrowserType browserType,
@Nullable SiteModel site) {
@@ -120,6 +124,7 @@ public static PhotoPickerFragment newInstance(@NonNull PhotoPickerListener liste
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ ((WordPress) getActivity().getApplication()).component().inject(this);
mBrowserType = (MediaBrowserType) getArguments().getSerializable(ARG_BROWSER_TYPE);
mSite = (SiteModel) getArguments().getSerializable(WordPress.SITE);
@@ -334,7 +339,7 @@ public boolean onMenuItemClick(MenuItem item) {
}
});
- if (BuildConfig.TENOR_AVAILABLE) {
+ if (mTenorFeatureConfig.isEnabled()) {
MenuItem itemGif = popup.getMenu().add(R.string.photo_picker_gif);
itemGif.setOnMenuItemClickListener(item -> {
doIconClicked(PhotoPickerIcon.GIF);
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/plans/PlanDetailsFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/plans/PlanDetailsFragment.kt
index 49a87d2e1c7a..45a0cc87b1ae 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/plans/PlanDetailsFragment.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/plans/PlanDetailsFragment.kt
@@ -93,7 +93,7 @@ class PlanDetailsFragment : Fragment(), FullScreenDialogContent {
return true
}
- override fun onViewCreated(controller: FullScreenDialogController) {
+ override fun setController(controller: FullScreenDialogController) {
dialogController = controller
}
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java
index 725e74bda2c2..70d6e916a498 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java
@@ -63,17 +63,21 @@
import org.wordpress.android.editor.EditorImageSettingsListener;
import org.wordpress.android.editor.EditorMediaUploadListener;
import org.wordpress.android.editor.EditorMediaUtils;
+import org.wordpress.android.editor.EditorThemeUpdateListener;
import org.wordpress.android.editor.ExceptionLogger;
import org.wordpress.android.editor.GutenbergEditorFragment;
import org.wordpress.android.editor.ImageSettingsDialogFragment;
import org.wordpress.android.fluxc.Dispatcher;
import org.wordpress.android.fluxc.action.AccountAction;
import org.wordpress.android.fluxc.generated.AccountActionBuilder;
+import org.wordpress.android.fluxc.generated.EditorThemeActionBuilder;
import org.wordpress.android.fluxc.generated.PostActionBuilder;
import org.wordpress.android.fluxc.generated.SiteActionBuilder;
import org.wordpress.android.fluxc.model.AccountModel;
import org.wordpress.android.fluxc.model.CauseOfOnPostChanged;
import org.wordpress.android.fluxc.model.CauseOfOnPostChanged.RemoteAutoSavePost;
+import org.wordpress.android.fluxc.model.EditorTheme;
+import org.wordpress.android.fluxc.model.EditorThemeSupport;
import org.wordpress.android.fluxc.model.MediaModel;
import org.wordpress.android.fluxc.model.MediaModel.MediaUploadState;
import org.wordpress.android.fluxc.model.PostImmutableModel;
@@ -83,6 +87,9 @@
import org.wordpress.android.fluxc.network.rest.wpcom.site.PrivateAtomicCookie;
import org.wordpress.android.fluxc.store.AccountStore;
import org.wordpress.android.fluxc.store.AccountStore.OnAccountChanged;
+import org.wordpress.android.fluxc.store.EditorThemeStore;
+import org.wordpress.android.fluxc.store.EditorThemeStore.FetchEditorThemePayload;
+import org.wordpress.android.fluxc.store.EditorThemeStore.OnEditorThemeChanged;
import org.wordpress.android.fluxc.store.MediaStore;
import org.wordpress.android.fluxc.store.MediaStore.MediaError;
import org.wordpress.android.fluxc.store.MediaStore.MediaErrorType;
@@ -180,6 +187,7 @@
import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper;
import org.wordpress.android.util.analytics.AnalyticsUtils;
import org.wordpress.android.util.analytics.AnalyticsUtils.BlockEditorEnabledSource;
+import org.wordpress.android.util.config.TenorFeatureConfig;
import org.wordpress.android.util.helpers.MediaFile;
import org.wordpress.android.util.helpers.MediaGallery;
import org.wordpress.android.util.image.ImageManager;
@@ -322,6 +330,9 @@ enum RestartEditorOptions {
// For opening the context menu after permissions have been granted
private View mMenuView = null;
+ private Handler mShowPrepublishingBottomSheetHandler;
+ private Runnable mShowPrepublishingBottomSheetRunnable;
+
private boolean mHtmlModeMenuStateOn = false;
@Inject Dispatcher mDispatcher;
@@ -330,6 +341,7 @@ enum RestartEditorOptions {
@Inject PostStore mPostStore;
@Inject MediaStore mMediaStore;
@Inject UploadStore mUploadStore;
+ @Inject EditorThemeStore mEditorThemeStore;
@Inject FluxCImageLoader mImageLoader;
@Inject ShortcutUtils mShortcutUtils;
@Inject QuickStartStore mQuickStartStore;
@@ -354,6 +366,7 @@ enum RestartEditorOptions {
@Inject ReblogUtils mReblogUtils;
@Inject AnalyticsTrackerWrapper mAnalyticsTrackerWrapper;
@Inject PublishPostImmediatelyUseCase mPublishPostImmediatelyUseCase;
+ @Inject TenorFeatureConfig mTenorFeatureConfig;
private StorePostViewModel mViewModel;
@@ -588,6 +601,8 @@ protected void onCreate(Bundle savedInstanceState) {
setupViewPager();
}
ActivityId.trackLastActivity(ActivityId.POST_EDITOR);
+
+ setupPrepublishingBottomSheetRunnable();
}
@SuppressWarnings("unused")
@@ -690,7 +705,7 @@ private void startObserving() {
return null;
}));
mEditPostRepository.getPostChanged().observe(this, postEvent -> postEvent.applyIfNotHandled(post -> {
- mViewModel.savePostToDb(this, mEditPostRepository, mSite);
+ mViewModel.savePostToDb(mEditPostRepository, mSite);
return null;
}));
}
@@ -772,6 +787,10 @@ protected void onPause() {
mAztecImageLoader.clearTargets();
mAztecImageLoader = null;
}
+
+ if (mShowPrepublishingBottomSheetHandler != null && mShowPrepublishingBottomSheetRunnable != null) {
+ mShowPrepublishingBottomSheetHandler.removeCallbacks(mShowPrepublishingBottomSheetRunnable);
+ }
}
@Override
@@ -982,7 +1001,7 @@ public void onPhotoPickerIconClicked(@NonNull PhotoPickerIcon icon, boolean allo
ActivityLauncher.showStockMediaPickerForResult(this, mSite, requestCode);
break;
case GIF:
- ActivityLauncher.showGifPickerForResult(this, mSite, RequestCodes.GIF_PICKER);
+ ActivityLauncher.showGifPickerForResult(this, mSite, RequestCodes.GIF_PICKER_SINGLE_SELECT);
break;
}
} else {
@@ -1068,6 +1087,20 @@ public boolean onPrepareOptionsMenu(Menu menu) {
}
}
+ MenuItem contentInfo = menu.findItem(R.id.menu_content_info);
+ if (mEditorFragment instanceof GutenbergEditorFragment) {
+ contentInfo.setOnMenuItemClickListener((menuItem) -> {
+ try {
+ mEditorFragment.showContentInfo();
+ } catch (EditorFragmentNotAddedException e) {
+ ToastUtils.showToast(WordPress.getContext(), R.string.toast_content_info_failed);
+ }
+ return true;
+ });
+ } else {
+ contentInfo.setVisible(false); // only show the menu item when for Gutenberg
+ }
+
return super.onPrepareOptionsMenu(menu);
}
@@ -1775,17 +1808,24 @@ private void saveResult(boolean saved, boolean uploadNotStarted) {
setResult(RESULT_OK, i);
}
+ private void setupPrepublishingBottomSheetRunnable() {
+ mShowPrepublishingBottomSheetHandler = new Handler();
+ mShowPrepublishingBottomSheetRunnable = () -> {
+ Fragment fragment = getSupportFragmentManager().findFragmentByTag(
+ PrepublishingBottomSheetFragment.TAG);
+ if (fragment == null) {
+ PrepublishingBottomSheetFragment prepublishingFragment =
+ PrepublishingBottomSheetFragment.newInstance(getSite(), mIsPage);
+ prepublishingFragment.show(getSupportFragmentManager(), PrepublishingBottomSheetFragment.TAG);
+ }
+ };
+ }
+
private void showPrepublishingNudgeBottomSheet() {
mViewPager.setCurrentItem(PAGE_CONTENT);
ActivityUtils.hideKeyboard(this);
-
- Fragment fragment = getSupportFragmentManager().findFragmentByTag(
- PrepublishingBottomSheetFragment.TAG);
- if (fragment == null) {
- PrepublishingBottomSheetFragment prepublishingFragment =
- PrepublishingBottomSheetFragment.newInstance(getSite(), mIsPage);
- prepublishingFragment.show(getSupportFragmentManager(), PrepublishingBottomSheetFragment.TAG);
- }
+ long delayMs = 100;
+ mShowPrepublishingBottomSheetHandler.postDelayed(mShowPrepublishingBottomSheetRunnable, delayMs);
}
@Override public void onSubmitButtonClicked(boolean publishPost) {
@@ -1967,8 +2007,12 @@ public Fragment getItem(int position) {
String languageString = LocaleManager.getLanguage(EditPostActivity.this);
String wpcomLocaleSlug = languageString.replace("_", "-").toLowerCase(Locale.ENGLISH);
boolean supportsStockPhotos = mSite.isUsingWpComRestApi();
- boolean isWpCom = getSite().isWPCom();
+ boolean isWpCom = getSite().isWPCom() || mSite.isPrivateWPComAtomic() || mSite.isWPComAtomic();
boolean isSiteUsingWpComRestApi = mSite.isUsingWpComRestApi();
+
+ EditorTheme editorTheme = mEditorThemeStore.getEditorThemeForSite(mSite);
+ Bundle themeBundle = (editorTheme != null) ? editorTheme.getThemeSupport().toBundle() : null;
+
return GutenbergEditorFragment.newInstance(
"",
"",
@@ -1982,7 +2026,10 @@ public Fragment getItem(int position) {
isWpCom ? mAccountStore.getAccount().getUserName() : mSite.getUsername(),
isWpCom ? "" : mSite.getPassword(),
mAccountStore.getAccessToken(),
- isSiteUsingWpComRestApi);
+ isSiteUsingWpComRestApi,
+ themeBundle,
+ WordPress.getUserAgent(),
+ mTenorFeatureConfig.isEnabled());
} else {
// If gutenberg editor is not selected, default to Aztec.
return AztecEditorFragment.newInstance("", "", AppPrefs.isAztecEditorToolbarExpanded());
@@ -2322,7 +2369,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
.addExistingMediaToEditorAsync(AddExistingMediaSource.STOCK_PHOTO_LIBRARY, mediaIds);
}
break;
- case RequestCodes.GIF_PICKER:
+ case RequestCodes.GIF_PICKER_SINGLE_SELECT:
if (data.hasExtra(GifPickerActivity.KEY_SAVED_MEDIA_MODEL_LOCAL_IDS)) {
int[] localIds = data.getIntArrayExtra(GifPickerActivity.KEY_SAVED_MEDIA_MODEL_LOCAL_IDS);
mEditorMedia.addGifMediaToPostAsync(localIds);
@@ -2579,6 +2626,11 @@ public void onAddStockMediaClicked(boolean allowMultipleSelection) {
onPhotoPickerIconClicked(PhotoPickerIcon.STOCK_MEDIA, allowMultipleSelection);
}
+ @Override
+ public void onAddGifClicked(boolean allowMultipleSelection) {
+ onPhotoPickerIconClicked(PhotoPickerIcon.GIF, allowMultipleSelection);
+ }
+
@Override
public void onPerformFetch(String path, Consumer onResult, Consumer onError) {
if (mSite != null) {
@@ -2784,6 +2836,7 @@ private void onEditorFinalTouchesBeforeShowing() {
refreshEditorContent();
// probably here is best for Gutenberg to start interacting with
if (mShowGutenbergEditor && mEditorFragment instanceof GutenbergEditorFragment) {
+ refreshEditorTheme();
List failedMedia =
mMediaStore.getMediaForPostWithState(mEditPostRepository.getPost(), MediaUploadState.FAILED);
if (failedMedia != null && !failedMedia.isEmpty()) {
@@ -3005,6 +3058,24 @@ public void onEventMainThread(UploadService.UploadMediaRetryEvent event) {
}
}
+ private void refreshEditorTheme() {
+ FetchEditorThemePayload payload = new FetchEditorThemePayload(mSite);
+ mDispatcher.dispatch(EditorThemeActionBuilder.newFetchEditorThemeAction(payload));
+ }
+
+ @SuppressWarnings("unused")
+ @Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
+ public void onEditorThemeChanged(OnEditorThemeChanged event) {
+ if (!(mEditorFragment instanceof EditorThemeUpdateListener)) return;
+
+ if (mSite.getId() != event.getSiteId()) return;
+ EditorTheme editorTheme = event.getEditorTheme();
+
+ if (editorTheme == null) return;
+ EditorThemeSupport editorThemeSupport = editorTheme.getThemeSupport();
+ ((EditorThemeUpdateListener) mEditorFragment)
+ .onEditorThemeUpdated(editorThemeSupport.toBundle());
+ }
// EditPostActivityHook methods
@Override
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/PostListMainViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/PostListMainViewModel.kt
index 6c631d518810..24280fc38c03 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/posts/PostListMainViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/PostListMainViewModel.kt
@@ -1,11 +1,13 @@
package org.wordpress.android.ui.posts
+import android.content.Context
import android.content.Intent
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -78,6 +80,7 @@ class PostListMainViewModel @Inject constructor(
private val postListEventListenerFactory: PostListEventListener.Factory,
private val previewStateHelper: PreviewStateHelper,
private val analyticsTracker: AnalyticsTrackerWrapper,
+ private val savePostToDbUseCase: SavePostToDbUseCase,
@Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher,
@Named(BG_THREAD) private val bgDispatcher: CoroutineDispatcher,
private val uploadStarter: UploadStarter
@@ -211,7 +214,8 @@ class PostListMainViewModel @Inject constructor(
site: SiteModel,
initPreviewState: PostListRemotePreviewState,
currentBottomSheetPostId: LocalId,
- editPostRepository: EditPostRepository
+ editPostRepository: EditPostRepository,
+ context: Context
) {
this.site = site
this.editPostRepository = editPostRepository
@@ -273,6 +277,12 @@ class PostListMainViewModel @Inject constructor(
lifecycleRegistry.markState(Lifecycle.State.STARTED)
uploadStarter.queueUploadFromSite(site)
+
+ editPostRepository.run {
+ postChanged.observe(this@PostListMainViewModel, Observer {
+ savePostToDbUseCase.savePostToDb(editPostRepository, site)
+ })
+ }
}
override fun onCleared() {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/PostListMainViewState.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/PostListMainViewState.kt
index caaa5d23a9c4..90054b25fb43 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/posts/PostListMainViewState.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/PostListMainViewState.kt
@@ -34,14 +34,14 @@ sealed class AuthorFilterListItemUIState(
data class Everyone(override val isSelected: Boolean, @DrawableRes val imageRes: Int) :
AuthorFilterListItemUIState(
id = EVERYONE.id,
- text = UiStringRes(R.string.post_list_author_everyone),
+ text = UiStringRes(R.string.everyone),
isSelected = isSelected
)
data class Me(val avatarUrl: String?, override val isSelected: Boolean) :
AuthorFilterListItemUIState(
id = ME.id,
- text = UiStringRes(R.string.post_list_author_me),
+ text = UiStringRes(R.string.me),
isSelected = isSelected
)
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/PostsListActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/PostsListActivity.kt
index 9446391fd1b4..1cc5493e7dfd 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/posts/PostsListActivity.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/PostsListActivity.kt
@@ -247,7 +247,7 @@ class PostsListActivity : LocaleAwareActivity(),
private fun initViewModel(initPreviewState: PostListRemotePreviewState, currentBottomSheetPostId: LocalId) {
viewModel = ViewModelProviders.of(this, viewModelFactory).get(PostListMainViewModel::class.java)
- viewModel.start(site, initPreviewState, currentBottomSheetPostId, editPostRepository)
+ viewModel.start(site, initPreviewState, currentBottomSheetPostId, editPostRepository, this)
viewModel.viewState.observe(this, Observer { state ->
state?.let {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/SavePostToDbUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/SavePostToDbUseCase.kt
index dcd37c9955a6..eac150ca7c9e 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/posts/SavePostToDbUseCase.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/SavePostToDbUseCase.kt
@@ -15,10 +15,10 @@ class SavePostToDbUseCase
private val uploadUtils: UploadUtilsWrapper,
private val dateTimeUtils: DateTimeUtilsWrapper,
private val dispatcher: Dispatcher,
- private val pendingDraftsNotificationsUtils: PendingDraftsNotificationsUtilsWrapper
+ private val pendingDraftsNotificationsUtils: PendingDraftsNotificationsUtilsWrapper,
+ private val context: Context
) {
fun savePostToDb(
- context: Context,
postRepository: EditPostRepository,
site: SiteModel
) {
@@ -43,14 +43,13 @@ class SavePostToDbUseCase
post.setIsLocallyChanged(true)
}
post.setDateLocallyChanged(dateTimeUtils.currentTimeInIso8601())
- handlePendingDraftNotifications(context, postRepository)
+ handlePendingDraftNotifications(postRepository)
postRepository.savePostSnapshot()
dispatcher.dispatch(PostActionBuilder.newUpdatePostAction(post))
}
}
private fun handlePendingDraftNotifications(
- context: Context,
editPostRepository: EditPostRepository
) {
if (editPostRepository.status == PostStatus.DRAFT) {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/adapters/AuthorSelectionAdapter.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/adapters/AuthorSelectionAdapter.kt
index cf4f5a383eed..1118d2dac97f 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/posts/adapters/AuthorSelectionAdapter.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/adapters/AuthorSelectionAdapter.kt
@@ -37,7 +37,7 @@ class AuthorSelectionAdapter(context: Context) : BaseAdapter() {
if (view == null) {
val inflater = LayoutInflater.from(parent.context)
- view = inflater.inflate(R.layout.post_list_author_selection_dropdown, parent, false)
+ view = inflater.inflate(R.layout.author_selection_dropdown, parent, false)
holder = DropdownViewHolder(view)
view.tag = holder
} else {
@@ -67,7 +67,7 @@ class AuthorSelectionAdapter(context: Context) : BaseAdapter() {
if (view == null) {
val inflater = LayoutInflater.from(parent.context)
- view = inflater.inflate(R.layout.post_list_author_selection, parent, false)
+ view = inflater.inflate(R.layout.author_selection, parent, false)
holder = NormalViewHolder(view)
view.tag = holder
} else {
@@ -92,7 +92,7 @@ class AuthorSelectionAdapter(context: Context) : BaseAdapter() {
}
private open class NormalViewHolder(protected val itemView: View) {
- protected val image: AppCompatImageView = itemView.findViewById(R.id.post_list_author_selection_image)
+ protected val image: AppCompatImageView = itemView.findViewById(R.id.author_selection_image)
@CallSuper
open fun bind(
@@ -126,7 +126,7 @@ class AuthorSelectionAdapter(context: Context) : BaseAdapter() {
}
private class DropdownViewHolder(itemView: View) : NormalViewHolder(itemView) {
- private val text: AppCompatTextView = itemView.findViewById(R.id.post_list_author_selection_text)
+ private val text: AppCompatTextView = itemView.findViewById(R.id.author_selection_text)
override fun bind(
state: AuthorFilterListItemUIState,
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/StorePostViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/StorePostViewModel.kt
index 0ff0d22af4df..b9cac4e7af37 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/StorePostViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/StorePostViewModel.kt
@@ -53,7 +53,7 @@ class StorePostViewModel
editPostRepository: EditPostRepository,
site: SiteModel
): ActivityFinishState {
- savePostToDbUseCase.savePostToDb(context, editPostRepository, site)
+ savePostToDbUseCase.savePostToDb(editPostRepository, site)
return if (networkUtils.isNetworkAvailable()) {
postUtils.trackSavePostAnalytics(
editPostRepository.getPost(),
@@ -79,11 +79,10 @@ class StorePostViewModel
}
fun savePostToDb(
- context: Context,
postRepository: EditPostRepository,
site: SiteModel
) {
- savePostToDbUseCase.savePostToDb(context, postRepository, site)
+ savePostToDbUseCase.savePostToDb(postRepository, site)
}
fun updatePostObjectWithUIAsync(
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/mediauploadcompletionprocessors/CoverBlockProcessor.java b/WordPress/src/main/java/org/wordpress/android/ui/posts/mediauploadcompletionprocessors/CoverBlockProcessor.java
index cdb63163ad13..6897fd910d60 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/posts/mediauploadcompletionprocessors/CoverBlockProcessor.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/mediauploadcompletionprocessors/CoverBlockProcessor.java
@@ -11,6 +11,8 @@
import java.util.regex.Pattern;
public class CoverBlockProcessor extends BlockProcessor {
+ private boolean mHasVideoBackground = false;
+
/**
* Template pattern used to match and splice cover inner blocks
*/
@@ -55,6 +57,10 @@ public CoverBlockProcessor(String localId, MediaFile mediaFile,
if (id != null && id.getAsInt() == Integer.parseInt(mLocalId, 10)) {
jsonAttributes.addProperty("id", Integer.parseInt(mRemoteId, 10));
jsonAttributes.addProperty("url", mRemoteUrl);
+
+ // check if background type is video
+ JsonElement backgroundType = jsonAttributes.get("backgroundType");
+ mHasVideoBackground = backgroundType != null && "video".equals(backgroundType.getAsString());
return true;
}
@@ -63,14 +69,23 @@ public CoverBlockProcessor(String localId, MediaFile mediaFile,
@Override boolean processBlockContentDocument(Document document) {
// select cover block div
- Element targetDiv = document.select(".wp-block-cover").first();
+ Element targetDiv = document.selectFirst(".wp-block-cover");
// if a match is found, proceed with replacement
if (targetDiv != null) {
- // replace background-image url in style attribute
- String style = PATTERN_BACKGROUND_IMAGE_URL.matcher(targetDiv.attr("style"))
- .replaceFirst(String.format("background-image:url(%1$s)", mRemoteUrl));
- targetDiv.attr("style", style);
+ if (mHasVideoBackground) {
+ Element videoElement = targetDiv.selectFirst("video");
+ if (videoElement != null) {
+ videoElement.attr("src", mRemoteUrl);
+ } else {
+ return false;
+ }
+ } else {
+ // replace background-image url in style attribute
+ String style = PATTERN_BACKGROUND_IMAGE_URL.matcher(targetDiv.attr("style")).replaceFirst(
+ String.format("background-image:url(%1$s)", mRemoteUrl));
+ targetDiv.attr("style", style);
+ }
// return injected block
return true;
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java
index bfe7ae8ffdce..7e13ce37a115 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java
@@ -140,6 +140,9 @@ public enum DeletablePrefKey implements PrefKey {
// Keep the local_blog_id + local_post_id values that have HW Acc. turned off
AZTEC_EDITOR_DISABLE_HW_ACC_KEYS,
+
+ // timestamp of the last update of the reader css styles
+ READER_CSS_UPDATED_TIMESTAMP
}
/**
@@ -226,7 +229,7 @@ public enum UndeletablePrefKey implements PrefKey {
LAST_FEATURE_ANNOUNCEMENT_APP_VERSION_CODE,
// feature flag for Reader Improvements Phase 2
- FF_READER_IMPROVEMENTS_PHASE_2
+ FF_READER_IMPROVEMENTS_PHASE_2,
}
private static SharedPreferences prefs() {
@@ -1136,6 +1139,14 @@ public static void setReaderTagsUpdatedTimestamp(long timestamp) {
setLong(DeletablePrefKey.READER_TAGS_UPDATE_TIMESTAMP, timestamp);
}
+ public static long getReaderCssUpdatedTimestamp() {
+ return getLong(DeletablePrefKey.READER_CSS_UPDATED_TIMESTAMP, 0);
+ }
+
+ public static void setReaderCssUpdatedTimestamp(long timestamp) {
+ setLong(DeletablePrefKey.READER_CSS_UPDATED_TIMESTAMP, timestamp);
+ }
+
/*
* adds a local site ID to the top of list of recently chosen sites
*/
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefsWrapper.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefsWrapper.kt
index 70b618409f7a..5ed67d5e108f 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefsWrapper.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefsWrapper.kt
@@ -67,6 +67,10 @@ class AppPrefsWrapper @Inject constructor() {
get() = AppPrefs.getReaderTagsUpdatedTimestamp()
set(timestamp) = AppPrefs.setReaderTagsUpdatedTimestamp(timestamp)
+ var readerCssUpdatedTimestamp: Long
+ get() = AppPrefs.getReaderCssUpdatedTimestamp()
+ set(timestamp) = AppPrefs.setReaderCssUpdatedTimestamp(timestamp)
+
fun getAppWidgetSiteId(appWidgetId: Int) = AppPrefs.getStatsWidgetSelectedSiteId(appWidgetId)
fun setAppWidgetSiteId(siteId: Long, appWidgetId: Int) = AppPrefs.setStatsWidgetSelectedSiteId(siteId, appWidgetId)
fun removeAppWidgetSiteId(appWidgetId: Int) = AppPrefs.removeStatsWidgetSelectedSiteId(appWidgetId)
@@ -142,6 +146,8 @@ class AppPrefsWrapper @Inject constructor() {
fun setReaderTag(selectedTag: ReaderTag?) = AppPrefs.setReaderTag(selectedTag)
fun getReaderTag(): ReaderTag? = AppPrefs.getReaderTag()
+ fun isReaderImprovementsPhase2Enabled(): Boolean = AppPrefs.isReaderImprovementsPhase2Enabled()
+
companion object {
private const val LIGHT_MODE_ID = 0
private const val DARK_MODE_ID = 1
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppSettingsFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppSettingsFragment.java
index 39182bd4dea8..91f2dee1c74e 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppSettingsFragment.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppSettingsFragment.java
@@ -28,16 +28,21 @@
import org.wordpress.android.fluxc.Dispatcher;
import org.wordpress.android.fluxc.action.AccountAction;
import org.wordpress.android.fluxc.generated.AccountActionBuilder;
+import org.wordpress.android.fluxc.generated.WhatsNewActionBuilder;
+import org.wordpress.android.fluxc.model.whatsnew.WhatsNewAnnouncementModel;
import org.wordpress.android.fluxc.store.AccountStore;
import org.wordpress.android.fluxc.store.AccountStore.OnAccountChanged;
import org.wordpress.android.fluxc.store.SiteStore;
+import org.wordpress.android.fluxc.store.WhatsNewStore.OnWhatsNewFetched;
+import org.wordpress.android.fluxc.store.WhatsNewStore.WhatsNewAppId;
+import org.wordpress.android.fluxc.store.WhatsNewStore.WhatsNewFetchPayload;
import org.wordpress.android.ui.reader.services.update.ReaderUpdateLogic;
import org.wordpress.android.ui.reader.services.update.ReaderUpdateServiceStarter;
-import org.wordpress.android.ui.whatsnew.FeatureAnnouncement;
import org.wordpress.android.ui.whatsnew.FeatureAnnouncementDialogFragment;
import org.wordpress.android.ui.whatsnew.FeatureAnnouncementProvider;
import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.AppThemeUtils;
+import org.wordpress.android.util.BuildConfigWrapper;
import org.wordpress.android.util.LocaleManager;
import org.wordpress.android.util.NetworkUtils;
import org.wordpress.android.util.ToastUtils;
@@ -77,11 +82,13 @@ public class AppSettingsFragment extends PreferenceFragment
@Inject Dispatcher mDispatcher;
@Inject ContextProvider mContextProvider;
@Inject FeatureAnnouncementProvider mFeatureAnnouncementProvider;
+ @Inject BuildConfigWrapper mBuildConfigWrapper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((WordPress) getActivity().getApplication()).component().inject(this);
+ mDispatcher.register(this);
setRetainInstance(true);
@@ -168,13 +175,8 @@ public boolean onPreferenceChange(Preference preference, Object newValue) {
mWhatsNew = findPreference(getString(R.string.pref_key_whats_new));
- FeatureAnnouncement featureAnnouncement = mFeatureAnnouncementProvider.getLatestFeatureAnnouncement();
- if (featureAnnouncement != null) {
- mWhatsNew.setSummary(getString(R.string.version_with_name_param, featureAnnouncement.getAppVersionName()));
- mWhatsNew.setOnPreferenceClickListener(this);
- } else {
- removeWhatsNewPreference();
- }
+ removeWhatsNewPreference();
+ mDispatcher.dispatch(WhatsNewActionBuilder.newFetchCachedAnnouncementAction());
if (!BuildConfig.OFFER_GUTENBERG) {
removeExperimentalCategory();
@@ -196,6 +198,12 @@ private void removeWhatsNewPreference() {
aboutTheAppPreferenceCategory.removePreference(mWhatsNew);
}
+ private void addWhatsNewPreference() {
+ PreferenceCategory aboutTheAppPreferenceCategory =
+ (PreferenceCategory) findPreference(getString(R.string.pref_key_about_section));
+ aboutTheAppPreferenceCategory.addPreference(mWhatsNew);
+ }
+
@Override
public void onResume() {
super.onResume();
@@ -207,7 +215,6 @@ public void onResume() {
@Override
public void onStart() {
super.onStart();
- mDispatcher.register(this);
}
@Override
@@ -225,6 +232,26 @@ public void onActivityCreated(Bundle savedInstanceState) {
AnalyticsTracker.flush();
}
+ @SuppressWarnings("unused")
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onWhatsNewFetched(OnWhatsNewFetched event) {
+ if (event.isFromCache()) {
+ mDispatcher.dispatch(WhatsNewActionBuilder
+ .newFetchRemoteAnnouncementAction(
+ new WhatsNewFetchPayload(mBuildConfigWrapper.getAppVersionName(),
+ WhatsNewAppId.WP_ANDROID)));
+ }
+
+ if (event.error != null || event.getWhatsNewItems() == null || event.getWhatsNewItems().isEmpty()) {
+ return;
+ }
+
+ WhatsNewAnnouncementModel latestAnnouncement = event.getWhatsNewItems().get(0);
+ mWhatsNew.setSummary(getString(R.string.version_with_name_param, latestAnnouncement.getAppVersionName()));
+ mWhatsNew.setOnPreferenceClickListener(this);
+ addWhatsNewPreference();
+ }
+
@SuppressWarnings("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
public void onAccountChanged(OnAccountChanged event) {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/quickstart/QuickStartFullScreenDialogFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/quickstart/QuickStartFullScreenDialogFragment.java
index 7cbba24c12ed..084ca5421d93 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/quickstart/QuickStartFullScreenDialogFragment.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/quickstart/QuickStartFullScreenDialogFragment.java
@@ -122,7 +122,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
}
@Override
- public void onViewCreated(final FullScreenDialogController controller) {
+ public void setController(final FullScreenDialogController controller) {
mDialogController = controller;
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderConstants.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderConstants.java
index b4e47e2c9e58..ee618765ffac 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderConstants.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderConstants.java
@@ -26,6 +26,8 @@ public class ReaderConstants {
// the Calypso web reader
public static final int MIN_GALLERY_IMAGE_WIDTH = 144;
+ public static final int THUMBNAIL_STRIP_IMG_COUNT = 4;
+
// referrer url for reader posts opened in a browser
public static final String HTTP_REFERER_URL = "https://wordpress.com";
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderCssProvider.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderCssProvider.kt
new file mode 100644
index 000000000000..a2d4dee19193
--- /dev/null
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderCssProvider.kt
@@ -0,0 +1,33 @@
+package org.wordpress.android.ui.reader
+
+import org.wordpress.android.ui.prefs.AppPrefsWrapper
+import org.wordpress.android.ui.reader.utils.DateProvider
+import org.wordpress.android.util.NetworkUtilsWrapper
+import java.util.concurrent.TimeUnit
+import javax.inject.Inject
+
+const val EXPIRATION_IN_DAYS = 5L
+private const val BASE_CSS_URL = "https://wordpress.com/calypso/reader-mobile.css"
+
+class ReaderCssProvider @Inject constructor(
+ private val networkUtilsWrapper: NetworkUtilsWrapper,
+ private val appPrefsWrapper: AppPrefsWrapper,
+ private val dateProvider: DateProvider
+) {
+ fun getCssUrl(): String {
+ val lastUpdated = appPrefsWrapper.readerCssUpdatedTimestamp
+ val currentDate = dateProvider.getCurrentDate().time
+
+ val urlSuffix = if (networkUtilsWrapper.isNetworkAvailable() && isExpired(lastUpdated, currentDate)) {
+ appPrefsWrapper.readerCssUpdatedTimestamp = currentDate
+ currentDate
+ } else {
+ lastUpdated
+ }
+ return "$BASE_CSS_URL?$urlSuffix"
+ }
+
+ private fun isExpired(lastUpdated: Long, currentDate: Long): Boolean {
+ return lastUpdated < currentDate - TimeUnit.DAYS.toMillis(EXPIRATION_IN_DAYS)
+ }
+}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderFragment.kt
index e981eabdcfdb..d1e3822ad156 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderFragment.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderFragment.kt
@@ -20,18 +20,24 @@ import org.wordpress.android.R.string
import org.wordpress.android.WordPress
import org.wordpress.android.models.ReaderTagList
import org.wordpress.android.ui.WPWebViewActivity
+import org.wordpress.android.ui.prefs.AppPrefs
import org.wordpress.android.ui.reader.ReaderTypes.ReaderPostListType
+import org.wordpress.android.ui.reader.discover.ReaderDiscoverFragment
+import org.wordpress.android.ui.reader.discover.interests.ReaderInterestsFragment
import org.wordpress.android.ui.reader.services.update.ReaderUpdateLogic.UpdateTask.FOLLOWED_BLOGS
import org.wordpress.android.ui.reader.services.update.ReaderUpdateLogic.UpdateTask.TAGS
import org.wordpress.android.ui.reader.services.update.ReaderUpdateServiceStarter
import org.wordpress.android.ui.reader.viewmodels.NewsCardViewModel
import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel
-import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState
+import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState.ContentUiState
+import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState.InitialUiState
+import org.wordpress.android.ui.utils.UiHelpers
import java.util.EnumSet
import javax.inject.Inject
class ReaderFragment : Fragment(R.layout.reader_fragment_layout) {
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
+ @Inject lateinit var uiHelpers: UiHelpers
private lateinit var viewModel: ReaderViewModel
private lateinit var newsCardViewModel: NewsCardViewModel
@@ -41,7 +47,8 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout) {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
viewModel.uiState.value?.let {
- val selectedTag = it.readerTagList[position]
+ val currentUiState = it as ContentUiState
+ val selectedTag = currentUiState.readerTagList[position]
newsCardViewModel.onTagChanged(selectedTag)
viewModel.onTagChanged(selectedTag)
}
@@ -111,7 +118,15 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout) {
private fun startObserving() {
viewModel.uiState.observe(viewLifecycleOwner, Observer { uiState ->
uiState?.let {
- updateTabs(uiState)
+ when (it) {
+ is InitialUiState -> {
+ }
+ is ContentUiState -> {
+ updateTabs(it)
+ }
+ }
+ app_bar.setExpanded(uiState.appBarExpanded)
+ uiHelpers.updateVisibility(tab_layout, uiState.tabLayoutVisible)
searchMenuItem?.isVisible = uiState.searchIconVisible
}
})
@@ -124,7 +139,7 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout) {
viewModel.selectTab.observe(viewLifecycleOwner, Observer { selectTabAction ->
selectTabAction.getContentIfNotHandled()?.let { tabPosition ->
- view_pager.currentItem = tabPosition
+ view_pager.setCurrentItem(tabPosition, false)
}
})
@@ -134,6 +149,18 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout) {
}
})
+ viewModel.showReaderInterests.observe(viewLifecycleOwner, Observer { event ->
+ event?.getContentIfNotHandled()?.let {
+ showReaderInterests()
+ }
+ })
+
+ viewModel.closeReaderInterests.observe(viewLifecycleOwner, Observer { event ->
+ event?.getContentIfNotHandled()?.let {
+ closeReaderInterests()
+ }
+ })
+
newsCardViewModel.openUrlEvent.observe(viewLifecycleOwner, Observer {
it?.getContentIfNotHandled()?.let { url ->
val activity: Activity? = activity
@@ -146,7 +173,7 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout) {
viewModel.start()
}
- private fun updateTabs(uiState: ReaderUiState) {
+ private fun updateTabs(uiState: ContentUiState) {
val adapter = TabsAdapter(this, uiState.readerTagList)
view_pager.adapter = adapter
@@ -159,7 +186,33 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout) {
override fun getItemCount(): Int = tags.size
override fun createFragment(position: Int): Fragment {
- return ReaderPostListFragment.newInstanceForTag(tags[position], ReaderPostListType.TAG_FOLLOWED, true)
+ return if (AppPrefs.isReaderImprovementsPhase2Enabled() && tags[position].isDiscover) {
+ ReaderDiscoverFragment()
+ } else {
+ ReaderPostListFragment.newInstanceForTag(tags[position], ReaderPostListType.TAG_FOLLOWED, true)
+ }
+ }
+ }
+
+ private fun showReaderInterests() {
+ val readerInterestsFragment = childFragmentManager.findFragmentByTag(ReaderInterestsFragment.TAG)
+ if (readerInterestsFragment == null) {
+ childFragmentManager.beginTransaction()
+ .replace(
+ R.id.interests_fragment_container,
+ ReaderInterestsFragment(),
+ ReaderInterestsFragment.TAG
+ )
+ .commitNow()
+ }
+ }
+
+ private fun closeReaderInterests() {
+ val readerInterestsFragment = childFragmentManager.findFragmentByTag(ReaderInterestsFragment.TAG)
+ if (readerInterestsFragment?.isAdded == true) {
+ childFragmentManager.beginTransaction()
+ .remove(readerInterestsFragment)
+ .commitNow()
}
}
}
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostDetailFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostDetailFragment.kt
index f49d2548e227..6b7437942fc4 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostDetailFragment.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostDetailFragment.kt
@@ -183,6 +183,7 @@ class ReaderPostDetailFragment : Fragment(),
@Inject internal lateinit var readerFileDownloadManager: ReaderFileDownloadManager
@Inject internal lateinit var featuredImageUtils: FeaturedImageUtils
@Inject internal lateinit var privateAtomicCookie: PrivateAtomicCookie
+ @Inject internal lateinit var readerCssProvider: ReaderCssProvider
private val mSignInClickListener = View.OnClickListener {
EventBus.getDefault()
@@ -1351,7 +1352,7 @@ class ReaderPostDetailFragment : Fragment(),
scrollView.visibility = View.VISIBLE
// render the post in the webView
- renderer = ReaderPostRenderer(readerWebView, post, featuredImageUtils)
+ renderer = ReaderPostRenderer(readerWebView, post, featuredImageUtils, readerCssProvider)
// if the post is from private atomic site postpone render until we have a special access cookie
if (post!!.isPrivateAtomic && privateAtomicCookie.isCookieRefreshRequired()) {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java
index f8ed34053bb1..6d8626847fbf 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java
@@ -751,6 +751,7 @@ public void onStart() {
@Override
public void onStop() {
super.onStop();
+ mNewPostsBar.clearAnimation();
mDispatcher.unregister(this);
EventBus.getDefault().unregister(this);
}
@@ -1824,20 +1825,21 @@ private boolean isEmptyViewShowing() {
}
private void setCurrentTagFromEmptyViewButton(ActionableEmptyViewButtonType button) {
- ReaderTag tag;
+ ReaderTag tag = null;
- switch (button) {
- case DISCOVER:
- tag = ReaderUtils.getTagFromEndpoint(ReaderTag.DISCOVER_PATH);
- break;
- case FOLLOWED:
- tag = ReaderUtils.getTagFromEndpoint(ReaderTag.FOLLOWING_PATH);
- break;
- default:
+ switch (button) {
+ case DISCOVER:
+ tag = ReaderUtils.getTagFromEndpoint(ReaderTag.DISCOVER_PATH);
+ break;
+ case FOLLOWED:
+ tag = ReaderUtils.getTagFromEndpoint(ReaderTag.FOLLOWING_PATH);
+ break;
+ }
+ if (tag == null) {
tag = ReaderUtils.getDefaultTag();
- }
+ }
- mViewModel.onEmptyStateButtonTapped(tag);
+ mViewModel.onEmptyStateButtonTapped(tag);
}
/*
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostRenderer.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostRenderer.java
index e65457d6af97..ca46eecda9a3 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostRenderer.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostRenderer.java
@@ -50,9 +50,11 @@ public class ReaderPostRenderer {
private String mRenderedHtml;
private ImageSizeMap mAttachmentSizes;
private FeaturedImageUtils mFeaturedImageUtils;
+ private ReaderCssProvider mCssProvider;
@SuppressLint("SetJavaScriptEnabled")
- public ReaderPostRenderer(ReaderWebView webView, ReaderPost post, FeaturedImageUtils featuredImageUtils) {
+ public ReaderPostRenderer(ReaderWebView webView, ReaderPost post, FeaturedImageUtils featuredImageUtils,
+ ReaderCssProvider cssProvider) {
if (webView == null) {
throw new IllegalArgumentException("ReaderPostRenderer requires a webView");
}
@@ -64,6 +66,7 @@ public ReaderPostRenderer(ReaderWebView webView, ReaderPost post, FeaturedImageU
mWeakWebView = new WeakReference<>(webView);
mResourceVars = new ReaderResourceVars(webView.getContext());
mFeaturedImageUtils = featuredImageUtils;
+ mCssProvider = cssProvider;
mMinFullSizeWidthDp = pxToDp(mResourceVars.mFullSizeImageWidthPx / 3);
mMinMidSizeWidthDp = mMinFullSizeWidthDp / 2;
@@ -361,41 +364,48 @@ private String formatPostContentForWebView(final String content, final SetReader Post")
- // https://developers.google.com/chrome/mobile/docs/webview/pixelperfect
- .append("")
- .append("
diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml
index 39edf29b6594..4c785a556a48 100644
--- a/WordPress/src/main/res/values/strings.xml
+++ b/WordPress/src/main/res/values/strings.xml
@@ -117,6 +117,9 @@
%1$f, %2$f
\@%s
+ Me
+ Everyone
+
%d selected
@@ -1415,6 +1418,7 @@
Debug Menu
Switch to classic editor
Switch to block editor
+ Content structure
Title
@@ -1576,6 +1580,7 @@
There was an error uploading the media in this page: %s.
Read permission denied on device media
Media could not be found
+ Media thumbnail could not be loaded
You don\'t have permission to view or edit media
Unexpected response from server
An error occurred while uploading media
@@ -2304,6 +2309,7 @@
Use this photo
Use this video
Use this media
+ Use this GIF
Image Thumbnail
Image selected
,Selected
@@ -2671,8 +2677,10 @@
You don\'t have any trashed pages
The selected page is not available
Cancel upload
+ Page author
We cannot open pages at the moment. Please try again later
%1$s · %2$s
+ %1$s · %2$s · %3$s
@string/post_status_pending_review
@string/post_status_post_private
@@ -2715,8 +2723,6 @@
Post author
- Me
- Everyone
Published
Drafts
Scheduled
@@ -2868,6 +2874,7 @@
Current value is %s
CUSTOMIZE
+ Customize Gradient
Dismiss
Don’t cry because it’s over, smile because it happened.
@@ -2890,6 +2897,8 @@
Double tap to select a video
Double tap to select an image
Double tap to select layout
+
+ Double tap to select the option
Double tap to toggle setting
Double tap to undo last change
@@ -2910,6 +2919,8 @@ translators: sample content for "Portfolio" page template
translators: sample content for "Services" page template
translators: sample content for "Team" page template -->
Get in Touch
+ Go back
+ Gradient Type
Help icon
Here is the panel content!
Hide keyboard
@@ -2959,10 +2970,14 @@ translators: sample content for "Services" page template -->
My post status info
My pre publish panel
Navigate Up
+ Navigates to custom color picker
+ Navigates to customize the gradient
+ Navigates to the previous content sheet
No application can handle this request. Please install a Web browser.
Number of columns
Only show excerpt
Open Block Actions Menu
+ Open link in a browser
Open Settings
Page break block. %s
diff --git a/WordPress/src/main/res/values/styles.xml b/WordPress/src/main/res/values/styles.xml
index d2abc9a9a315..3cc7a7a3a524 100644
--- a/WordPress/src/main/res/values/styles.xml
+++ b/WordPress/src/main/res/values/styles.xml
@@ -27,6 +27,7 @@
- @style/WordPress.ToolBar
- @style/WordPress.TabLayout
- @style/WordPress.AppBarLayout
+ - @style/Widget.MaterialComponents.Snackbar
- @style/Widget.MaterialComponents.CardView
@@ -1124,7 +1125,7 @@
- false
- @style/FullScreenDialogFragmentAnimation
-
+