diff --git a/.aiexclude b/.aiexclude new file mode 100644 index 000000000000..05433f9baf75 --- /dev/null +++ b/.aiexclude @@ -0,0 +1,39 @@ +# OS X generated file +.DS_Store + +# Build-related files +fastlane/ + +# Key-related files +.jks +.keystore + +# Backup files +.bak + +# Generated files +bin/ +gen/ +build/ +build.log + +# Built application files +.apk +.ap_ +.aab + +# Dex VM files +.dex + +# Configuration files +.configure +.configure-files/ +google-services.json +google-upload-credentials.json +firebase.secrets.json +sentry.properties + +# Gradle files +gradle.properties +local.properties +local-builds.gradle diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 87d96c8032e3..c6c5356e0c4d 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -29,6 +29,16 @@ steps: ################# - group: "🕵️‍♂️ Linters" steps: + - label: "☢️ Danger - PR Check" + command: danger + key: danger + if: "build.pull_request.id != null" + retry: + manual: + permit_on_passed: true + agents: + queue: "linter" + - label: "🕵️ checkstyle" command: | cp gradle.properties-example gradle.properties diff --git a/.github/workflows/run-danger.yml b/.github/workflows/run-danger.yml index 856ab8cea46d..1031d64a7dd9 100644 --- a/.github/workflows/run-danger.yml +++ b/.github/workflows/run-danger.yml @@ -1,13 +1,17 @@ -name: ☢️ Danger +name: ☢️ Trigger Danger On Buildkite on: pull_request: - types: [opened, reopened, ready_for_review, synchronize, edited, labeled, unlabeled, milestoned, demilestoned] + types: [labeled, unlabeled, milestoned, demilestoned] jobs: dangermattic: - # runs on draft PRs only for opened / synchronize events - if: ${{ (github.event.pull_request.draft == false) || (github.event.pull_request.draft == true && contains(fromJSON('["opened", "synchronize"]'), github.event.action)) }} - uses: Automattic/dangermattic/.github/workflows/reusable-run-danger.yml@v1.0.0 + if: ${{ (github.event.pull_request.draft == false) }} + uses: Automattic/dangermattic/.github/workflows/reusable-retry-buildkite-step-on-events.yml@v1.1.0 + with: + org-slug: "automattic" + pipeline-slug: "wordpress-android" + retry-step-key: "danger" + build-commit-sha: "${{ github.event.pull_request.head.sha }}" secrets: - github-token: ${{ secrets.DANGERMATTIC_GITHUB_TOKEN }} + buildkite-api-token: ${{ secrets.TRIGGER_BK_BUILD_TOKEN }} diff --git a/Dangerfile b/Dangerfile index d439496653f9..ef55a1240632 100644 --- a/Dangerfile +++ b/Dangerfile @@ -3,7 +3,8 @@ github.dismiss_out_of_range_messages # `files: []` forces rubocop to scan all files, not just the ones modified in the PR -rubocop.lint(files: [], force_exclusion: true, inline_comment: true, fail_on_inline_comment: true, include_cop_names: true) +# Added a custom `rubocop_cmd` to prevent RuboCop from running using `bundle exec`, which we don't want on the linter agent +rubocop.lint(files: [], force_exclusion: true, inline_comment: true, fail_on_inline_comment: true, include_cop_names: true, rubocop_cmd: ': | rubocop') manifest_pr_checker.check_gemfile_lock_updated diff --git a/Gemfile.lock b/Gemfile.lock index 8d2a8b367b52..631493a457ee 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -69,29 +69,16 @@ GEM no_proxy_fix octokit (>= 4.0) terminal-table (>= 1, < 4) - danger-dangermattic (1.0.0) + danger-dangermattic (1.0.2) danger (~> 9.4) - danger-junit (~> 1.0) danger-plugin-api (~> 1.0) danger-rubocop (~> 0.12) - danger-swiftlint (~> 0.35) - danger-xcode_summary (~> 1.0) - rubocop (~> 1.60) - danger-junit (1.0.2) - danger (> 2.0) - ox (~> 2.0) + rubocop (~> 1.61) danger-plugin-api (1.0.0) danger (> 2.0) danger-rubocop (0.12.0) danger rubocop (~> 1.0) - danger-swiftlint (0.35.0) - danger - rake (> 10) - thor (~> 1.0.0) - danger-xcode_summary (1.3.0) - danger-plugin-api (~> 1.0) - xcresult (~> 0.2) declarative (0.0.20) diffy (3.4.2) digest-crc (0.6.5) @@ -241,7 +228,7 @@ GEM concurrent-ruby (~> 1.0) java-properties (0.3.0) jmespath (1.6.2) - json (2.7.1) + json (2.7.2) jwt (2.8.0) base64 kramdown (2.4.0) @@ -254,7 +241,7 @@ GEM mini_portile2 (2.8.5) minitest (5.22.2) multi_json (1.15.0) - multipart-post (2.4.0) + multipart-post (2.4.1) mutex_m (0.2.0) nanaimo (0.3.0) nap (1.1.0) @@ -271,23 +258,22 @@ GEM options (2.3.2) optparse (0.4.0) os (1.1.4) - ox (2.14.17) parallel (1.24.0) - parser (3.3.0.5) + parser (3.3.1.0) ast (~> 2.4.1) racc plist (3.7.1) progress_bar (1.3.3) highline (>= 1.6, < 3) options (~> 2.3.0) - public_suffix (5.0.4) + public_suffix (5.0.5) racc (1.7.3) rainbow (3.1.1) - rake (13.1.0) + rake (13.2.1) rake-compiler (1.2.7) rake rchardet (1.8.0) - regexp_parser (2.9.0) + regexp_parser (2.9.1) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) @@ -296,7 +282,7 @@ GEM rexml (3.2.6) rmagick (4.3.0) rouge (2.0.7) - rubocop (1.60.2) + rubocop (1.63.5) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) @@ -304,11 +290,11 @@ GEM rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.30.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.30.0) - parser (>= 3.2.1.0) + rubocop-ast (1.31.3) + parser (>= 3.3.1.0) ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) rubyzip (2.3.2) @@ -327,7 +313,6 @@ GEM terminal-notifier (2.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) - thor (1.0.1) trailblazer-option (0.1.2) tty-cursor (0.7.1) tty-screen (0.8.2) @@ -349,7 +334,6 @@ GEM rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) - xcresult (0.2.1) PLATFORMS ruby diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 27b1202ae4ba..a3128872399b 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -1,9 +1,21 @@ *** PLEASE FOLLOW THIS FORMAT: [] [] +25.0 +----- +* [*] Fixed a rare crash on Posts List screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20813] +* [*] Fixed a rare crash on the Login screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20821] +* [*] Fix a crash that occurs when remove a user [https://github.com/wordpress-mobile/WordPress-Android/pull/20837] +* [*] Fixed a rare crash on the featured image confirmation dialog [https://github.com/wordpress-mobile/WordPress-Android/pull/20836] +* [*] Fixed an ANR issue on the Post List screen [https://github.com/wordpress-mobile/WordPress-Android/pull/20833] +* [*] Fixed a crash that occurs with Blogging Reminders [https://github.com/wordpress-mobile/WordPress-Android/pull/20845] +* [*] [internal] Block Editor: Upgrade target sdk version to Android API 34 [https://github.com/wordpress-mobile/WordPress-Android/pull/20841] +* [*] [internal] In-app updates feature [https://github.com/wordpress-mobile/WordPress-Android/pull/20822] + 24.9 ----- * [*] [Jetpack-only] Removed Social section from the detail screen of Total Followers card [https://github.com/wordpress-mobile/WordPress-Android/pull/20763] * [***] [Jetpack-only] Reorganized Stats to include updated Traffic and Insights tabs, along with a newly added Subscribers tab to improve subscriber metrics analysis [https://github.com/wordpress-mobile/WordPress-Android/pull/20756] +* [*] Site picker: Fixed the UI alignment issue in RTL [https://github.com/wordpress-mobile/WordPress-Android/pull/20804] 24.8 ----- diff --git a/WordPress/build.gradle b/WordPress/build.gradle index 338b108e59fb..5d2535b7266d 100644 --- a/WordPress/build.gradle +++ b/WordPress/build.gradle @@ -147,10 +147,11 @@ android { buildConfigField "boolean", "READER_IMPROVEMENTS", "false" buildConfigField "boolean", "BLOGANUARY_DASHBOARD_NUDGE", "false" buildConfigField "boolean", "DYNAMIC_DASHBOARD_CARDS", "false" - buildConfigField "boolean", "STATS_TRAFFIC_SUBSCRIBERS_TAB", "false" + buildConfigField "boolean", "STATS_TRAFFIC_SUBSCRIBERS_TABS", "false" buildConfigField "boolean", "READER_DISCOVER_NEW_ENDPOINT", "false" buildConfigField "boolean", "READER_READING_PREFERENCES", "false" buildConfigField "boolean", "READER_READING_PREFERENCES_FEEDBACK", "false" + buildConfigField "boolean", "VOICE_TO_CONTENT", "false" // Override these constants in jetpack product flavor to enable/ disable features buildConfigField "boolean", "ENABLE_SITE_CREATION", "true" @@ -166,6 +167,7 @@ android { buildConfigField "boolean", "DASHBOARD_PERSONALIZATION", "false" buildConfigField "boolean", "ENABLE_SITE_MONITORING", "false" buildConfigField "boolean", "SYNC_PUBLISHING", "false" + buildConfigField "boolean", "ENABLE_IN_APP_UPDATES", "false" manifestPlaceholders = [magicLinkScheme:"wordpress"] } @@ -391,6 +393,9 @@ dependencies { implementation "org.wordpress:persistentedittext:$wordPressPersistentEditTextVersion" implementation "$gradle.ext.gravatarBinaryPath:$gravatarVersion" + implementation "com.google.android.play:app-update:$googlePlayInAppUpdateVersion" + implementation "com.google.android.play:app-update-ktx:$googlePlayInAppUpdateVersion" + implementation "androidx.arch.core:core-common:$androidxArchCoreVersion" implementation "androidx.arch.core:core-runtime:$androidxArchCoreVersion" implementation "com.google.code.gson:gson:$googleGsonVersion" diff --git a/WordPress/jetpack_metadata/PlayStoreStrings.po b/WordPress/jetpack_metadata/PlayStoreStrings.po index b20ae0e436d8..70e831a3281d 100644 --- a/WordPress/jetpack_metadata/PlayStoreStrings.po +++ b/WordPress/jetpack_metadata/PlayStoreStrings.po @@ -10,6 +10,15 @@ msgstr "" "X-Generator: VsCode\n" "Project-Id-Version: Jetpack - Apps - Android - Release Notes\n" +msgctxt "release_note_249" +msgid "" +"24.9:\n" +"- We reorganized the Stats screen to show data about traffic and insights.\n" +"- We added a Subscribers tab to show data about site subscribers.\n" +"- We removed social subscribers from the Total Followers card.\n" +"- Site names and URLs are properly positioned for right-to-left language users.\n" +msgstr "" + msgctxt "release_note_248" msgid "" "24.8:\n" @@ -17,13 +26,6 @@ msgid "" "\n" msgstr "" -msgctxt "release_note_247" -msgid "" -"24.7:\n" -"We redesigned the site picker screen. You can now pin your favorite sites, see recently accessed sites, and more. We also removed the ability to show and hide sites.\n" -"(Did we say the word “sites” enough? We think so.)\n" -msgstr "" - #. translators: Release notes for this version to be displayed in the Play Store. Limit to 500 characters including spaces and commas! #. translators: Title to be displayed in the Play Store. Limit to 30 characters including spaces and commas! msgctxt "play_store_app_title" diff --git a/WordPress/jetpack_metadata/release_notes.txt b/WordPress/jetpack_metadata/release_notes.txt index 9f2bf4ba3ae2..1e2fd6c021ad 100644 --- a/WordPress/jetpack_metadata/release_notes.txt +++ b/WordPress/jetpack_metadata/release_notes.txt @@ -1,2 +1,4 @@ -On the Stats screen, percentages now appear in their correct position for right-to-left language users. Right on. - +- We reorganized the Stats screen to show data about traffic and insights. +- We added a Subscribers tab to show data about site subscribers. +- We removed social subscribers from the Total Followers card. +- Site names and URLs are properly positioned for right-to-left language users. diff --git a/WordPress/metadata/PlayStoreStrings.po b/WordPress/metadata/PlayStoreStrings.po index e8d084a6434d..bbbc05be18df 100644 --- a/WordPress/metadata/PlayStoreStrings.po +++ b/WordPress/metadata/PlayStoreStrings.po @@ -10,17 +10,16 @@ msgstr "" "X-Generator: VsCode\n" "Project-Id-Version: Release Notes & Play Store Descriptions\n" -msgctxt "release_note_248" +msgctxt "release_note_249" msgid "" -"24.8:\n" -"April showers bring May flowers, but they unfortunately don’t bring release notes. Stay Juned for the next update.\n" +"24.9:\n" +"Site names and URLs are now properly positioned for right-to-left language users. Right on.\n" msgstr "" -msgctxt "release_note_247" +msgctxt "release_note_248" msgid "" -"24.7:\n" -"We redesigned the site picker screen. You can now pin your favorite sites, see recently accessed sites, and more. We also removed the ability to show and hide sites.\n" -"(Did we say the word “sites” enough? We think so.)\n" +"24.8:\n" +"April showers bring May flowers, but they unfortunately don’t bring release notes. Stay Juned for the next update.\n" msgstr "" #. translators: Release notes for this version to be displayed in the Play Store. Limit to 500 characters including spaces and commas! diff --git a/WordPress/metadata/release_notes.txt b/WordPress/metadata/release_notes.txt index 68ef433035e3..040fd0b337b1 100644 --- a/WordPress/metadata/release_notes.txt +++ b/WordPress/metadata/release_notes.txt @@ -1 +1 @@ -April showers bring May flowers, but they unfortunately don’t bring release notes. Stay Juned for the next update. +Site names and URLs are now properly positioned for right-to-left language users. Right on. diff --git a/WordPress/src/androidTest/resources/new-stats-feature-response.json b/WordPress/src/androidTest/resources/new-stats-feature-response.json index 07567a1926ac..de0234285090 100644 --- a/WordPress/src/androidTest/resources/new-stats-feature-response.json +++ b/WordPress/src/androidTest/resources/new-stats-feature-response.json @@ -1,3 +1,3 @@ { - "stats_traffic_subscribers_tab": true + "stats_traffic_subscribers_tabs": true } diff --git a/WordPress/src/jetpack/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfigConstants.kt b/WordPress/src/jetpack/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfigConstants.kt new file mode 100644 index 000000000000..3d539360338c --- /dev/null +++ b/WordPress/src/jetpack/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfigConstants.kt @@ -0,0 +1,5 @@ +package org.wordpress.android.util.config + +const val IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD = "jp_in_app_update_blocking_version_android" + + diff --git a/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt b/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt index 2ad4a34dbaa2..bfe756467ab2 100644 --- a/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt +++ b/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt @@ -286,7 +286,6 @@ class AppInitializer @Inject constructor( crashLogging.initialize() dispatcher.register(this) appConfig.init(appScope) - // Upload any encrypted logs that were queued but not yet uploaded encryptedLogging.start() diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt new file mode 100644 index 000000000000..fc1135f274c8 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/IInAppUpdateManager.kt @@ -0,0 +1,15 @@ +package org.wordpress.android.inappupdate + +import android.app.Activity + +interface IInAppUpdateManager { + fun checkForAppUpdate(activity: Activity, listener: InAppUpdateListener) + fun completeAppUpdate() + fun cancelAppUpdate(updateType: Int) + fun onUserAcceptedAppUpdate(updateType: Int) + + companion object { + const val APP_UPDATE_IMMEDIATE_REQUEST_CODE = 1001 + const val APP_UPDATE_FLEXIBLE_REQUEST_CODE = 1002 + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt new file mode 100644 index 000000000000..abf15b094bfd --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTracker.kt @@ -0,0 +1,40 @@ +package org.wordpress.android.inappupdate + +import com.google.android.play.core.install.model.AppUpdateType +import org.wordpress.android.analytics.AnalyticsTracker +import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper +import javax.inject.Inject + +class InAppUpdateAnalyticsTracker @Inject constructor( + private val tracker: AnalyticsTrackerWrapper +) { + fun trackUpdateShown(updateType: Int) { + tracker.track(AnalyticsTracker.Stat.IN_APP_UPDATE_SHOWN, createPropertyMap(updateType)) + } + + fun trackUpdateAccepted(updateType: Int) { + tracker.track(AnalyticsTracker.Stat.IN_APP_UPDATE_ACCEPTED, createPropertyMap(updateType)) + } + + fun trackUpdateDismissed(updateType: Int) { + tracker.track(AnalyticsTracker.Stat.IN_APP_UPDATE_DISMISSED, createPropertyMap(updateType)) + } + + fun trackAppRestartToCompleteUpdate() { + tracker.track(AnalyticsTracker.Stat.IN_APP_UPDATE_COMPLETED_WITH_APP_RESTART) + } + + private fun createPropertyMap(updateType: Int): Map { + return when (updateType) { + AppUpdateType.FLEXIBLE -> mapOf(PROPERTY_UPDATE_TYPE to UPDATE_TYPE_FLEXIBLE) + AppUpdateType.IMMEDIATE -> mapOf(PROPERTY_UPDATE_TYPE to UPDATE_TYPE_BLOCKING) + else -> emptyMap() + } + } + + companion object { + const val PROPERTY_UPDATE_TYPE = "type" + const val UPDATE_TYPE_FLEXIBLE = "flexible" + const val UPDATE_TYPE_BLOCKING = "blocking" + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateListener.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateListener.kt new file mode 100644 index 000000000000..e002395f3cd9 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateListener.kt @@ -0,0 +1,34 @@ +package org.wordpress.android.inappupdate + +/** + * Abstract class for handling callbacks related to in-app update events. + * + * Each method provides a default implementation that does nothing, allowing + * implementers to only override the necessary methods without implementing + * all callback methods. + */ +abstract class InAppUpdateListener { + open fun onAppUpdateStarted(type: Int) { + // Default empty implementation + } + + open fun onAppUpdateDownloaded() { + // Default empty implementation + } + + open fun onAppUpdateInstalled() { + // Default empty implementation + } + + open fun onAppUpdateFailed() { + // Default empty implementation + } + + open fun onAppUpdateCancelled() { + // Default empty implementation + } + + open fun onAppUpdatePending() { + // Default empty implementation + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt new file mode 100644 index 000000000000..11ac7fc132e1 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerImpl.kt @@ -0,0 +1,230 @@ +package org.wordpress.android.inappupdate + +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Context +import android.util.Log +import com.google.android.play.core.appupdate.AppUpdateInfo +import com.google.android.play.core.appupdate.AppUpdateManager +import com.google.android.play.core.appupdate.AppUpdateOptions +import com.google.android.play.core.install.InstallState +import com.google.android.play.core.install.InstallStateUpdatedListener +import com.google.android.play.core.install.model.AppUpdateType +import com.google.android.play.core.install.model.InstallStatus +import com.google.android.play.core.install.model.InstallStatus.CANCELED +import com.google.android.play.core.install.model.InstallStatus.DOWNLOADED +import com.google.android.play.core.install.model.InstallStatus.DOWNLOADING +import com.google.android.play.core.install.model.InstallStatus.FAILED +import com.google.android.play.core.install.model.InstallStatus.INSTALLED +import com.google.android.play.core.install.model.InstallStatus.INSTALLING +import com.google.android.play.core.install.model.InstallStatus.PENDING +import com.google.android.play.core.install.model.UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS +import com.google.android.play.core.install.model.UpdateAvailability.UPDATE_AVAILABLE +import com.google.android.play.core.install.model.UpdateAvailability.UPDATE_NOT_AVAILABLE +import dagger.hilt.android.qualifiers.ApplicationContext +import org.wordpress.android.inappupdate.IInAppUpdateManager.Companion.APP_UPDATE_FLEXIBLE_REQUEST_CODE +import org.wordpress.android.inappupdate.IInAppUpdateManager.Companion.APP_UPDATE_IMMEDIATE_REQUEST_CODE + +import org.wordpress.android.util.BuildConfigWrapper +import org.wordpress.android.util.config.RemoteConfigWrapper +import javax.inject.Singleton + +@Singleton +@Suppress("TooManyFunctions") +class InAppUpdateManagerImpl( + @ApplicationContext private val applicationContext: Context, + private val appUpdateManager: AppUpdateManager, + private val remoteConfigWrapper: RemoteConfigWrapper, + private val buildConfigWrapper: BuildConfigWrapper, + private val inAppUpdateAnalyticsTracker: InAppUpdateAnalyticsTracker, + private val currentTimeProvider: () -> Long = {System.currentTimeMillis()} +): IInAppUpdateManager { + private var updateListener: InAppUpdateListener? = null + + override fun checkForAppUpdate(activity: Activity, listener: InAppUpdateListener) { + updateListener = listener + appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo -> + handleUpdateInfoSuccess(appUpdateInfo, activity) + }.addOnFailureListener { exception -> + Log.e(TAG, "Failed to check for update: ${exception.message}") + } + } + + override fun completeAppUpdate() { + inAppUpdateAnalyticsTracker.trackAppRestartToCompleteUpdate() + appUpdateManager.completeUpdate() + } + + override fun cancelAppUpdate(updateType: Int) { + appUpdateManager.unregisterListener(installStateListener) + inAppUpdateAnalyticsTracker.trackUpdateDismissed(updateType) + } + + override fun onUserAcceptedAppUpdate(updateType: Int) { + inAppUpdateAnalyticsTracker.trackUpdateAccepted(updateType) + } + + private fun handleUpdateInfoSuccess(appUpdateInfo: AppUpdateInfo, activity: Activity) { + when (appUpdateInfo.updateAvailability()) { + UPDATE_NOT_AVAILABLE -> { + /* do nothing */ + } + UPDATE_AVAILABLE -> { + handleUpdateAvailable(appUpdateInfo, activity) + } + DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS -> { + handleUpdateInProgress(appUpdateInfo, activity) + } + else -> { /* do nothing */ } + } + } + + private fun handleUpdateAvailable(appUpdateInfo: AppUpdateInfo, activity: Activity) { + if (appUpdateInfo.installStatus() == DOWNLOADED) { + updateListener?.onAppUpdateDownloaded() + return + } + + val updateVersion = getAvailableUpdateAppVersion(appUpdateInfo) + if (updateVersion != getLastUpdateRequestedVersion()) { + resetLastUpdateRequestInfo() + } + + if (isImmediateUpdateNecessary()) { + if (shouldRequestImmediateUpdate()) { + requestImmediateUpdate(appUpdateInfo, activity) + } + } else if (shouldRequestFlexibleUpdate()) { + requestFlexibleUpdate(appUpdateInfo, activity) + } + } + + private fun handleUpdateInProgress(appUpdateInfo: AppUpdateInfo, activity: Activity) { + if (isImmediateUpdateInProgress(appUpdateInfo)) { + requestImmediateUpdate(appUpdateInfo, activity) + } else { + requestFlexibleUpdate(appUpdateInfo, activity) + } + } + + private fun requestImmediateUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { + updateListener?.onAppUpdateStarted(AppUpdateType.IMMEDIATE) + requestUpdate(AppUpdateType.IMMEDIATE, appUpdateInfo, activity) + } + + private fun requestFlexibleUpdate(appUpdateInfo: AppUpdateInfo, activity: Activity) { + appUpdateManager.registerListener(installStateListener) + updateListener?.onAppUpdateStarted(AppUpdateType.FLEXIBLE) + requestUpdate(AppUpdateType.FLEXIBLE, appUpdateInfo, activity) + } + + @Suppress("TooGenericExceptionCaught") + private fun requestUpdate(updateType: Int, appUpdateInfo: AppUpdateInfo, activity: Activity) { + saveLastUpdateRequestInfo(appUpdateInfo) + val requestCode = if (updateType == AppUpdateType.IMMEDIATE) { + APP_UPDATE_IMMEDIATE_REQUEST_CODE + } else { + APP_UPDATE_FLEXIBLE_REQUEST_CODE + } + try { + appUpdateManager.startUpdateFlowForResult( + appUpdateInfo, + activity, + AppUpdateOptions.newBuilder(updateType).build(), + requestCode + ) + inAppUpdateAnalyticsTracker.trackUpdateShown(updateType) + } catch (e: Exception) { + Log.e(TAG, "requestUpdate for type: $updateType, exception occurred") + Log.e(TAG, e.message.toString()) + appUpdateManager.unregisterListener(installStateListener) + } + } + + private val installStateListener = object : InstallStateUpdatedListener { + @SuppressLint("SwitchIntDef") + override fun onStateUpdate(state: InstallState) { + when (state.installStatus()) { + DOWNLOADED -> { + updateListener?.onAppUpdateDownloaded() + } + INSTALLED -> { + updateListener?.onAppUpdateInstalled() + appUpdateManager.unregisterListener(this) // 'this' refers to the listener object + } + CANCELED -> { + updateListener?.onAppUpdateCancelled() + appUpdateManager.unregisterListener(this) + } + FAILED -> { + updateListener?.onAppUpdateFailed() + appUpdateManager.unregisterListener(this) + } + PENDING -> { + updateListener?.onAppUpdatePending() + } + DOWNLOADING, INSTALLING, InstallStatus.UNKNOWN -> { + /* do nothing */ + } + } + } + } + + private fun isImmediateUpdateInProgress(appUpdateInfo: AppUpdateInfo) = + appUpdateInfo.updateAvailability() == DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS + && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) + && isImmediateUpdateNecessary() + + private fun saveLastUpdateRequestInfo(appUpdateInfo: AppUpdateInfo) { + val sharedPref = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + sharedPref.edit().apply { + putInt(KEY_LAST_APP_UPDATE_CHECK_VERSION, getAvailableUpdateAppVersion(appUpdateInfo)) + putLong(KEY_LAST_APP_UPDATE_CHECK_TIME, currentTimeProvider.invoke()) + apply() + } + } + + private fun resetLastUpdateRequestInfo() { + val sharedPref = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + sharedPref.edit().apply { + putInt(KEY_LAST_APP_UPDATE_CHECK_VERSION, -1) + putLong(KEY_LAST_APP_UPDATE_CHECK_TIME, -1L) + apply() + } + } + + private fun getLastUpdateRequestedVersion() = + applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + .getInt(KEY_LAST_APP_UPDATE_CHECK_VERSION, -1) + + private fun getLastUpdateRequestedTime() = + applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + .getLong(KEY_LAST_APP_UPDATE_CHECK_TIME, -1L) + + private fun shouldRequestFlexibleUpdate() = + currentTimeProvider.invoke() - getLastUpdateRequestedTime() >= getFlexibleUpdateIntervalInMillis() + + private fun shouldRequestImmediateUpdate() = + currentTimeProvider.invoke() - getLastUpdateRequestedTime() >= IMMEDIATE_UPDATE_INTERVAL_IN_MILLIS + + @Suppress("MagicNumber") + private fun getFlexibleUpdateIntervalInMillis(): Long = + 1000 * 60 * 60 * 24 * remoteConfigWrapper.getInAppUpdateFlexibleIntervalInDays().toLong() + + private fun getCurrentAppVersion() = buildConfigWrapper.getAppVersionCode() + + private fun getLastBlockingAppVersion(): Int = remoteConfigWrapper.getInAppUpdateBlockingVersion() + + private fun getAvailableUpdateAppVersion(appUpdateInfo: AppUpdateInfo) = appUpdateInfo.availableVersionCode() + + private fun isImmediateUpdateNecessary() = getCurrentAppVersion() < getLastBlockingAppVersion() + + companion object { + const val IMMEDIATE_UPDATE_INTERVAL_IN_MILLIS = 1000 * 60 * 5 // 5 minutes + const val KEY_LAST_APP_UPDATE_CHECK_TIME = "last_app_update_check_time" + + private const val TAG = "AppUpdateChecker" + private const val PREF_NAME = "in_app_update_prefs" + private const val KEY_LAST_APP_UPDATE_CHECK_VERSION = "last_app_update_check_version" + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt new file mode 100644 index 000000000000..d732dc62d7e9 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/inappupdate/InAppUpdateManagerNoop.kt @@ -0,0 +1,21 @@ +package org.wordpress.android.inappupdate + +import android.app.Activity + +class InAppUpdateManagerNoop: IInAppUpdateManager { + override fun checkForAppUpdate(activity: Activity, listener: InAppUpdateListener) { + /* Empty implementation */ + } + + override fun completeAppUpdate() { + /* Empty implementation */ + } + + override fun cancelAppUpdate(updateType: Int) { + /* Empty implementation */ + } + + override fun onUserAcceptedAppUpdate(updateType: Int) { + /* Empty implementation */ + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java index 9814ec12f66a..ed712b9d32e3 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/ApplicationModule.java @@ -8,11 +8,17 @@ import androidx.lifecycle.LiveData; import androidx.preference.PreferenceManager; +import com.google.android.play.core.appupdate.AppUpdateManager; +import com.google.android.play.core.appupdate.AppUpdateManagerFactory; import com.tenor.android.core.network.ApiClient; import com.tenor.android.core.network.ApiService; import com.tenor.android.core.network.IApiClient; import org.wordpress.android.BuildConfig; +import org.wordpress.android.inappupdate.IInAppUpdateManager; +import org.wordpress.android.inappupdate.InAppUpdateAnalyticsTracker; +import org.wordpress.android.inappupdate.InAppUpdateManagerImpl; +import org.wordpress.android.inappupdate.InAppUpdateManagerNoop; import org.wordpress.android.ui.ActivityNavigator; import org.wordpress.android.ui.jetpack.backup.download.BackupDownloadStep; import org.wordpress.android.ui.jetpack.backup.download.BackupDownloadStepsProvider; @@ -21,6 +27,9 @@ import org.wordpress.android.ui.mediapicker.loader.TenorGifClient; import org.wordpress.android.ui.sitecreation.SiteCreationStep; import org.wordpress.android.ui.sitecreation.SiteCreationStepsProvider; +import org.wordpress.android.util.BuildConfigWrapper; +import org.wordpress.android.util.config.InAppUpdatesFeatureConfig; +import org.wordpress.android.util.config.RemoteConfigWrapper; import org.wordpress.android.util.wizard.WizardManager; import org.wordpress.android.viewmodel.helpers.ConnectionStatus; import org.wordpress.android.viewmodel.helpers.ConnectionStatusLiveData; @@ -76,6 +85,33 @@ public static WizardManager provideRestoreWizardManager( return new WizardManager<>(stepsProvider.getSteps()); } + @Provides + public static AppUpdateManager provideAppUpdateManager(@ApplicationContext Context context) { + return AppUpdateManagerFactory.create(context); + } + + @Provides + public static IInAppUpdateManager provideInAppUpdateManager( + @ApplicationContext Context context, + AppUpdateManager appUpdateManager, + RemoteConfigWrapper remoteConfigWrapper, + BuildConfigWrapper buildConfigWrapper, + InAppUpdatesFeatureConfig inAppUpdatesFeatureConfig, + InAppUpdateAnalyticsTracker inAppUpdateAnalyticsTracker + ) { + // Check if in-app updates feature is enabled + return inAppUpdatesFeatureConfig.isEnabled() + ? new InAppUpdateManagerImpl( + context, + appUpdateManager, + remoteConfigWrapper, + buildConfigWrapper, + inAppUpdateAnalyticsTracker, + System::currentTimeMillis + ) + : new InAppUpdateManagerNoop(); + } + @Provides public static ActivityNavigator provideActivityNavigator(@ApplicationContext Context context) { return new ActivityNavigator(); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/WebViewActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/WebViewActivity.java index 9bc91fe38fa8..c71e716e6f3d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/WebViewActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/WebViewActivity.java @@ -68,7 +68,9 @@ public void handleOnBackPressed() { mWebView = (WebView) findViewById(R.id.webView); mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); // Setting this user agent makes Calypso sites hide any WordPress UIs (e.g. Masterbar, banners, etc.). - mWebView.getSettings().setUserAgentString(mUserAgent.toString()); + if (mUserAgent != null) { + mWebView.getSettings().setUserAgentString(mUserAgent.toString()); + } configureWebView(); if (savedInstanceState == null) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java index 0bac7f2b6a63..e0d0b0fe1278 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java @@ -13,7 +13,6 @@ import com.google.android.gms.auth.api.Auth; import com.google.android.gms.auth.api.credentials.Credential; import com.google.android.gms.auth.api.credentials.CredentialRequest; -import com.google.android.gms.auth.api.credentials.CredentialRequestResult; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; import com.google.android.gms.common.api.CommonStatusCodes; @@ -82,33 +81,30 @@ public void smartLockAutoFill(@NonNull final Callback callback) { .setPasswordLoginSupported(true) .build(); Auth.CredentialsApi.request(mCredentialsClient, credentialRequest).setResultCallback( - new ResultCallback() { - @Override - public void onResult(@NonNull CredentialRequestResult result) { - Status status = result.getStatus(); - if (status.isSuccess()) { - Credential credential = result.getCredential(); - callback.onCredentialRetrieved(credential); - } else { - if (status.getStatusCode() == CommonStatusCodes.RESOLUTION_REQUIRED) { - try { - Activity activity = getActivityAndCheckAvailability(); - if (activity == null) { - return; - } - // Prompt the user to choose a saved credential - status.startResolutionForResult(activity, RequestCodes.SMART_LOCK_READ); - } catch (IntentSender.SendIntentException e) { - AppLog.d(T.NUX, "SmartLock: Failed to send resolution for credential request"); - - callback.onCredentialsUnavailable(); - } - } else { - // The user must create an account or log in manually. - AppLog.d(T.NUX, "SmartLock: Unsuccessful credential request."); + result -> { + Activity currentActivity = getActivityAndCheckAvailability(); + if (currentActivity == null) { + return; + } + Status status = result.getStatus(); + if (status.isSuccess()) { + Credential credential = result.getCredential(); + callback.onCredentialRetrieved(credential); + } else { + if (status.getStatusCode() == CommonStatusCodes.RESOLUTION_REQUIRED) { + try { + // Prompt the user to choose a saved credential + status.startResolutionForResult(currentActivity, RequestCodes.SMART_LOCK_READ); + } catch (IntentSender.SendIntentException e) { + AppLog.d(T.NUX, "SmartLock: Failed to send resolution for credential request"); callback.onCredentialsUnavailable(); } + } else { + // The user must create an account or log in manually. + AppLog.d(T.NUX, "SmartLock: Unsuccessful credential request."); + + callback.onCredentialsUnavailable(); } } }); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupEpilogueFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupEpilogueFragment.java index 568bb21ad430..4e7704cd2f76 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupEpilogueFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupEpilogueFragment.java @@ -735,7 +735,7 @@ protected void startGravatarUpload(final String filePath) { startProgress(false); mAvatarService.upload(file, new Email(mAccountStore.getAccount().getEmail()), Objects.requireNonNull(mAccountStore.getAccessToken()), - new GravatarListener() { + new GravatarListener() { @Override public void onSuccess(@NonNull Unit response) { endProgress(); @@ -836,7 +836,7 @@ public void run() { Uri uri = MediaUtils.downloadExternalMedia(getContext(), Uri.parse(mUrl)); File file = new File(new URI(uri.toString())); mAvatarService.upload(file, new Email(mEmail), mToken, - new GravatarListener() { + new GravatarListener() { @Override public void onSuccess(@NonNull Unit response) { AppLog.i(T.NUX, "Google avatar download and Gravatar upload succeeded."); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/blaze/blazecampaigns/BlazeCampaignParentActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/blaze/blazecampaigns/BlazeCampaignParentActivity.kt index ea2d7ff10373..c24cd976f6a4 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/blaze/blazecampaigns/BlazeCampaignParentActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/blaze/blazecampaigns/BlazeCampaignParentActivity.kt @@ -1,10 +1,10 @@ package org.wordpress.android.ui.blaze.blazecampaigns -import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.activity.viewModels import dagger.hilt.android.AndroidEntryPoint import org.wordpress.android.R +import org.wordpress.android.ui.LocaleAwareActivity import org.wordpress.android.ui.blaze.blazecampaigns.campaigndetail.CampaignDetailFragment import org.wordpress.android.ui.blaze.blazecampaigns.campaignlisting.CampaignListingFragment import org.wordpress.android.util.extensions.getParcelableExtraCompat @@ -12,7 +12,7 @@ import org.wordpress.android.util.extensions.getParcelableExtraCompat const val ARG_EXTRA_BLAZE_CAMPAIGN_PAGE = "blaze_campaign_page" @AndroidEntryPoint -class BlazeCampaignParentActivity : AppCompatActivity() { +class BlazeCampaignParentActivity : LocaleAwareActivity() { private val viewModel: CampaignViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/blaze/blazepromote/BlazePromoteParentActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/blaze/blazepromote/BlazePromoteParentActivity.kt index 799a9b2eded2..1e84224789cd 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/blaze/blazepromote/BlazePromoteParentActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/blaze/blazepromote/BlazePromoteParentActivity.kt @@ -2,9 +2,9 @@ package org.wordpress.android.ui.blaze.blazepromote import android.os.Bundle import androidx.activity.viewModels -import androidx.appcompat.app.AppCompatActivity import dagger.hilt.android.AndroidEntryPoint import org.wordpress.android.R +import org.wordpress.android.ui.LocaleAwareActivity import org.wordpress.android.ui.blaze.BlazeFlowSource import org.wordpress.android.ui.blaze.BlazeUIModel import org.wordpress.android.ui.blaze.BlazeUiState @@ -18,7 +18,7 @@ const val ARG_BLAZE_FLOW_SOURCE = "blaze_flow_source" const val ARG_BLAZE_SHOULD_SHOW_OVERLAY = "blaze_flow_should_show_overlay" @AndroidEntryPoint -class BlazePromoteParentActivity : AppCompatActivity() { +class BlazePromoteParentActivity : LocaleAwareActivity() { private val viewModel: BlazeViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugPrefs.kt b/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugPrefs.kt new file mode 100644 index 000000000000..7dda8cc7bf70 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugPrefs.kt @@ -0,0 +1,10 @@ +package org.wordpress.android.ui.debug.preferences + +import kotlin.reflect.KClass + +/** + * Class used to track debuggable shared preferences and will show up in [DebugSharedPreferenceFlagsActivity]. + */ +enum class DebugPrefs(val key: String, val type: KClass<*>) { + ALWAYS_SHOW_ANNOUNCEMENT("prefs_always_show_announcement", Boolean::class) +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModel.kt index b7c99e4487e6..a88c1cb1ed4f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModel.kt @@ -18,7 +18,13 @@ class DebugSharedPreferenceFlagsViewModel @Inject constructor( val flags = prefsWrapper.getAllPrefs().mapNotNull { (key, value) -> if (value is Boolean) key to value else null }.toMap() - _uiStateFlow.value = flags + + val explicitFlags = DebugPrefs.entries.mapNotNull { + // Only supporting boolean for now. + if (it.type == Boolean::class) it else null + }.associate { it.key to prefsWrapper.getDebugBooleanPref(it.key, false) } + + _uiStateFlow.value = flags + explicitFlags } fun setFlag(key: String, value: Boolean) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/history/HistoryDetailContainerFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/history/HistoryDetailContainerFragment.java index f51a98efabfa..61d2d2a59700 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/history/HistoryDetailContainerFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/history/HistoryDetailContainerFragment.java @@ -172,6 +172,9 @@ private ArrayList mapRevisions() { mRevision = getArguments().getParcelable(EXTRA_CURRENT_REVISION); final long[] previousRevisionsIds = getArguments().getLongArray(EXTRA_PREVIOUS_REVISIONS_IDS); + if (previousRevisionsIds == null) { + return null; + } final List revisionModels = new ArrayList<>(); final long postId = getArguments().getLong(EXTRA_POST_ID); final long siteId = getArguments().getLong(EXTRA_SITE_ID); @@ -192,7 +195,9 @@ private ArrayList mapRevisionModelsToRevisions(@Nullable final List revisions = new ArrayList<>(); for (int i = 0; i < revisionModels.size(); i++) { final RevisionModel current = revisionModels.get(i); - revisions.add(new Revision(current)); + if (current != null) { + revisions.add(new Revision(current)); + } } return revisions; } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/MeFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/MeFragment.kt index 0b823574eaf1..2129fb2e9d8e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/MeFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/MeFragment.kt @@ -680,7 +680,7 @@ class MeFragment : Fragment(R.layout.me_fragment), OnScrollToTopListener { } binding?.showGravatarProgressBar(true) avatarService.upload(file, Email(accountStore.account.email), accountStore.accessToken.orEmpty(), - object : GravatarListener { + object : GravatarListener { override fun onSuccess(response: Unit) { AnalyticsTracker.track(ME_GRAVATAR_UPLOADED) EventBus.getDefault().post(GravatarUploadFinished(filePath, true)) 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 397382275bb1..d79058f5b22d 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 @@ -10,6 +10,7 @@ import android.os.Handler; import android.os.Looper; import android.text.TextUtils; +import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.View; import android.view.ViewGroup; @@ -30,6 +31,7 @@ import com.google.android.gms.tasks.Task; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; +import com.google.android.play.core.install.model.AppUpdateType; import com.google.android.play.core.review.ReviewInfo; import com.google.android.play.core.review.ReviewManager; import com.google.android.play.core.review.ReviewManagerFactory; @@ -38,6 +40,8 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import org.wordpress.android.BuildConfig; +import org.wordpress.android.inappupdate.InAppUpdateListener; +import org.wordpress.android.inappupdate.IInAppUpdateManager; import org.wordpress.android.R; import org.wordpress.android.WordPress; import org.wordpress.android.analytics.AnalyticsTracker; @@ -174,6 +178,7 @@ import org.wordpress.android.viewmodel.mlp.ModalLayoutPickerViewModel; import org.wordpress.android.viewmodel.mlp.ModalLayoutPickerViewModel.CreatePageDashboardSource; import org.wordpress.android.widgets.AppRatingDialog; +import org.wordpress.android.widgets.WPSnackbar; import org.wordpress.android.workers.notification.createsite.CreateSiteNotificationScheduler; import org.wordpress.android.workers.weeklyroundup.WeeklyRoundupScheduler; @@ -296,6 +301,8 @@ public class WPMainActivity extends LocaleAwareActivity implements @Inject BuildConfigWrapper mBuildConfigWrapper; + @Inject IInAppUpdateManager mInAppUpdateManager; + @Inject GCMRegistrationScheduler mGCMRegistrationScheduler; @Inject ActivityNavigator mActivityNavigator; @@ -1196,9 +1203,30 @@ protected void onResume() { mSelectedSiteRepository.hasSelectedSite() && mBottomNav != null && mBottomNav.getCurrentSelectedPage() == PageType.MY_SITE ); + + checkForInAppUpdate(); + mIsChangingConfiguration = false; } + private void checkForInAppUpdate() { + mInAppUpdateManager.checkForAppUpdate(this, mInAppUpdateListener); + } + + @NonNull final InAppUpdateListener mInAppUpdateListener = new InAppUpdateListener() { + @Override public void onAppUpdateDownloaded() { + popupSnackbarForCompleteUpdate(); + } + }; + + private void popupSnackbarForCompleteUpdate() { + WPSnackbar.make(findViewById(R.id.coordinator), R.string.in_app_update_available, Snackbar.LENGTH_INDEFINITE) + .setAction(R.string.in_app_update_restart, v -> { + mInAppUpdateManager.completeAppUpdate(); + }) + .show(); + } + private void checkQuickStartNotificationStatus() { SiteModel selectedSite = getSelectedSite(); long selectedSiteLocalId = mSelectedSiteRepository.getSelectedSiteLocalId(); @@ -1350,6 +1378,7 @@ private void setSite(Intent data) { @Override @SuppressWarnings("deprecation") public void onActivityResult(int requestCode, int resultCode, Intent data) { + Log.e("WPMainActivity", "onActivityResult: " + requestCode + " " + resultCode); super.onActivityResult(requestCode, resultCode, data); if (!mSelectedSiteRepository.hasSelectedSite()) { initSelectedSite(); @@ -1463,6 +1492,23 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { case RequestCodes.DOMAIN_REGISTRATION: passOnActivityResultToMySiteFragment(requestCode, resultCode, data); break; + case IInAppUpdateManager.APP_UPDATE_FLEXIBLE_REQUEST_CODE: + handleUpdateResult(resultCode, AppUpdateType.FLEXIBLE); + break; + case IInAppUpdateManager.APP_UPDATE_IMMEDIATE_REQUEST_CODE: + handleUpdateResult(resultCode, AppUpdateType.IMMEDIATE); + break; + } + } + + // Handles the result of the app update request + private void handleUpdateResult(int resultCode, int updateType) { + if (resultCode == RESULT_OK) { + // The user accepted the update + mInAppUpdateManager.onUserAcceptedAppUpdate(updateType); + } else if (resultCode == RESULT_CANCELED) { + // The user denied the update + mInAppUpdateManager.cancelAppUpdate(updateType); } } @@ -1884,6 +1930,7 @@ public void onSetPromptReminderClick(final int siteId) { onActivityResult(RequestCodes.SITE_PICKER, resultCode, data); } + // We dismiss the QuickStart SnackBar every time activity is paused because // SnackBar sometimes do not appear when another SnackBar is still visible, even in other activities (weird) @Override diff --git a/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleManagementActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleManagementActivity.java index 0add5ecb5c0b..ca3a29e92275 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleManagementActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/people/PeopleManagementActivity.java @@ -604,7 +604,7 @@ private void refreshDetailFragment() { private boolean navigateBackToPeopleListFragment() { FragmentManager fragmentManager = getSupportFragmentManager(); - if (fragmentManager.getBackStackEntryCount() > 0) { + if (!fragmentManager.isStateSaved() && fragmentManager.getBackStackEntryCount() > 0) { fragmentManager.popBackStack(); ActionBar actionBar = getSupportActionBar(); 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 4da1601f9492..c89268b5853a 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 @@ -209,6 +209,7 @@ class PostsListActivity : LocaleAwareActivity(), setupActionBar() setupContent() initViewModel(initPreviewState, currentBottomSheetPostId) + initSearchFragment() initBloggingReminders() initInAppReviews() initTabLayout(tabIndex) @@ -492,7 +493,6 @@ class PostsListActivity : LocaleAwareActivity(), authorFilterMenuItem = menu.findItem(R.id.author_filter_menu_item) searchActionButton = menu.findItem(R.id.toggle_search) - initSearchFragment() binding.initSearchView() initAuthorFilter(authorFilterMenuItem) return true diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/PublishSettingsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/PublishSettingsViewModel.kt index d8672ec395f5..8d2bd9dc1f4d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/PublishSettingsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/PublishSettingsViewModel.kt @@ -23,6 +23,7 @@ import org.wordpress.android.util.LocaleManagerWrapper import org.wordpress.android.viewmodel.Event import org.wordpress.android.viewmodel.ResourceProvider import java.util.Calendar +import java.util.Date abstract class PublishSettingsViewModel constructor( @@ -212,7 +213,10 @@ constructor( val dateCreated = postRepository.dateCreated // Set the currently selected time if available if (!TextUtils.isEmpty(dateCreated)) { - calendar.time = DateTimeUtils.dateFromIso8601(dateCreated) + // Calendar.setTime(Date date) expects a non-null Date object + val maybeDate: Date? = DateTimeUtils.dateFromIso8601(dateCreated) + maybeDate?.let { date -> calendar.time = date } + calendar.timeZone = localeManagerWrapper.getTimeZone() } return calendar 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 0396fb813f35..5bf4bdb7824b 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 @@ -387,6 +387,10 @@ public static boolean getBoolean(PrefKey key, boolean def) { return Boolean.parseBoolean(value); } + public static boolean getRawBoolean(@NonNull final PrefKey key, boolean def) { + return prefs().getBoolean(key.name(), def); + } + public static void putBoolean(final PrefKey key, final boolean value) { prefs().edit().putBoolean(key.name(), value).apply(); } 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 e844020b246f..4a835daca578 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 @@ -19,6 +19,7 @@ import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsDa import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsDataTypeSelectionViewModel.DataType.VIEWS import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsDataTypeSelectionViewModel.DataType.VISITORS import org.wordpress.android.usecase.social.JetpackSocialFlow +import org.wordpress.android.util.BuildConfigWrapper import java.util.Date import javax.inject.Inject import javax.inject.Singleton @@ -31,7 +32,7 @@ import javax.inject.Singleton * */ @Singleton -class AppPrefsWrapper @Inject constructor() { +class AppPrefsWrapper @Inject constructor(val buildConfigWrapper: BuildConfigWrapper) { var featureAnnouncementShownVersion: Int get() = AppPrefs.getFeatureAnnouncementShownVersion() set(version) = AppPrefs.setFeatureAnnouncementShownVersion(version) @@ -451,6 +452,9 @@ class AppPrefsWrapper @Inject constructor() { fun getAllPrefs(): Map = AppPrefs.getAllPrefs() + fun getDebugBooleanPref(key: String, default: Boolean = false) = + buildConfigWrapper.isDebug() && AppPrefs.getRawBoolean({ key }, default) + fun setString(prefKey: PrefKey, value: String) { AppPrefs.setString(prefKey, value) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java index 4469e5763261..7c072d98a450 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java @@ -1301,7 +1301,7 @@ private void initBloggingReminders() { } private void setupBloggingRemindersBottomSheet() { - if (mBloggingRemindersPref == null || !isAdded()) { + if (mBloggingRemindersPref == null || !isAdded() || mSite == null || mBloggingRemindersViewModel == null) { return; } mBloggingRemindersViewModel.onBlogSettingsItemClicked(mSite.getId()); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderSiteHeaderView.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderSiteHeaderView.java index 3fd01fae0109..7516e164beae 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderSiteHeaderView.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderSiteHeaderView.java @@ -3,6 +3,8 @@ import android.content.Context; import android.icu.text.CompactDecimalFormat; import android.icu.text.NumberFormat; +import android.os.Handler; +import android.os.Looper; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; @@ -31,6 +33,9 @@ import org.wordpress.android.util.image.BlavatarShape; import org.wordpress.android.util.image.ImageManager; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + import javax.inject.Inject; /** @@ -55,6 +60,9 @@ public interface OnBlogInfoLoadedListener { private OnBlogInfoLoadedListener mBlogInfoListener; private OnFollowListener mFollowListener; + private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor(); + private final Handler mMainHandler = new Handler(Looper.getMainLooper()); + @Inject AccountStore mAccountStore; @Inject ImageManager mImageManager; @Inject ReaderTracker mReaderTracker; @@ -103,7 +111,6 @@ public void loadBlogInfo( mBlogId = blogId; mFeedId = feedId; - final ReaderBlog localBlogInfo; if (blogId == 0 && feedId == 0) { ToastUtils.showToast(getContext(), R.string.reader_toast_err_show_blog); return; @@ -111,33 +118,35 @@ public void loadBlogInfo( mIsFeed = ReaderUtils.isExternalFeed(mBlogId, mFeedId); - if (mIsFeed) { - localBlogInfo = ReaderBlogTable.getFeedInfo(mFeedId); - } else { - localBlogInfo = ReaderBlogTable.getBlogInfo(mBlogId); - } - - if (localBlogInfo != null) { - showBlogInfo(localBlogInfo, source); - } - - // then get from server if doesn't exist locally or is time to update it - if (localBlogInfo == null || ReaderBlogTable.isTimeToUpdateBlogInfo(localBlogInfo)) { - ReaderActions.UpdateBlogInfoListener listener = new ReaderActions.UpdateBlogInfoListener() { - @Override - public void onResult(ReaderBlog serverBlogInfo) { - if (isAttachedToWindow()) { - showBlogInfo(serverBlogInfo, source); - } - } - }; - + // run in background to avoid ANR + mExecutorService.execute(() -> { + final ReaderBlog localBlogInfo; if (mIsFeed) { - ReaderBlogActions.updateFeedInfo(mFeedId, null, listener); + localBlogInfo = ReaderBlogTable.getFeedInfo(mFeedId); } else { - ReaderBlogActions.updateBlogInfo(mBlogId, null, listener); + localBlogInfo = ReaderBlogTable.getBlogInfo(mBlogId); } - } + + mMainHandler.post(() -> { + if (localBlogInfo != null) { + showBlogInfo(localBlogInfo, source); + } + // then get from server if doesn't exist locally or is time to update it + if (localBlogInfo == null || ReaderBlogTable.isTimeToUpdateBlogInfo(localBlogInfo)) { + ReaderActions.UpdateBlogInfoListener listener = serverBlogInfo -> { + if (isAttachedToWindow()) { + showBlogInfo(serverBlogInfo, source); + } + }; + + if (mIsFeed) { + ReaderBlogActions.updateFeedInfo(mFeedId, null, listener); + } else { + ReaderBlogActions.updateBlogInfo(mBlogId, null, listener); + } + } + }); + }); } private void showBlogInfo(ReaderBlog blogInfo, String source) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/SiteCreationMainVM.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/SiteCreationMainVM.kt index a7f69cc2fde0..978fe2561f65 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/SiteCreationMainVM.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/SiteCreationMainVM.kt @@ -347,9 +347,10 @@ class SiteCreationMainVM @Inject constructor( _onCompleted.value = NotCreated to isSiteTitleTaskCompleted() } - fun onWizardFinished(result: Created) { - siteCreationState = siteCreationState.copy(result = result) - _onCompleted.value = result to isSiteTitleTaskCompleted() + fun onWizardFinished(result: Created?) { + val nullCheckedResult = result ?: NotCreated + siteCreationState = siteCreationState.copy(result = nullCheckedResult) + _onCompleted.value = nullCheckedResult to isSiteTitleTaskCompleted() } private fun isSiteTitleTaskCompleted() = !siteCreationState.siteName.isNullOrBlank() diff --git a/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModel.kt index 84f96ab53e6c..d3e9554b21e6 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModel.kt @@ -69,8 +69,7 @@ class SitePreviewViewModel @Inject constructor( private var siteDesign: String? = null private var isFree: Boolean = true - private lateinit var result: Created - private lateinit var domainName: String + private var result: Created? = null private val _uiState: MutableLiveData = MutableLiveData() val uiState: LiveData = _uiState @@ -78,8 +77,8 @@ class SitePreviewViewModel @Inject constructor( private val _preloadPreview: MutableLiveData = MutableLiveData() val preloadPreview: LiveData = _preloadPreview - private val _onOkButtonClicked = SingleLiveEvent() - val onOkButtonClicked: LiveData = _onOkButtonClicked + private val _onOkButtonClicked = SingleLiveEvent() + val onOkButtonClicked: LiveData = _onOkButtonClicked fun start(siteCreationState: SiteCreationState) { if (isStarted) return else isStarted = true @@ -90,12 +89,13 @@ class SitePreviewViewModel @Inject constructor( siteDesign = siteCreationState.siteDesign result = siteCreationState.result isFree = requireNotNull(siteCreationState.domain).isFree - domainName = getCleanUrl(result.site.url) ?: "" startPreLoadingWebView() - if (result is CreatedButNotFetched) { - launch { - fetchNewlyCreatedSiteModel(result.site.siteId)?.let { - result = Completed(it) + result?.let { + if (it is CreatedButNotFetched) { + launch { + fetchNewlyCreatedSiteModel(it.site.siteId)?.let { + result = Completed(it) + } } } } @@ -121,7 +121,7 @@ class SitePreviewViewModel @Inject constructor( } } // Load the newly created site in the webview - result.site.url?.let { url -> + result?.site?.url?.let { url -> val urlToLoad = urlUtils.addUrlSchemeIfNeeded( url = url, addHttps = isWordPressComSubDomain(url) @@ -172,7 +172,7 @@ class SitePreviewViewModel @Inject constructor( private fun getCleanUrl(url: String) = StringUtils.removeTrailingSlash(urlUtils.removeScheme(url)) private fun createSitePreviewData(): UrlData { - val url = domainName + val url = result?.let { getCleanUrl(it.site.url) ?: "" } ?: "" val subDomain = urlUtils.extractSubDomain(url) val fullUrl = urlUtils.addUrlSchemeIfNeeded(url, true) val subDomainIndices = 0 to subDomain.length diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsFragment.kt index faabf6c0f856..8a8823ae7077 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsFragment.kt @@ -46,7 +46,7 @@ import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider.SiteUpdate import org.wordpress.android.ui.utils.UiHelpers import org.wordpress.android.util.JetpackBrandingUtils import org.wordpress.android.util.WPSwipeToRefreshHelper -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import org.wordpress.android.util.helpers.SwipeToRefreshHelper import org.wordpress.android.viewmodel.observeEvent import org.wordpress.android.widgets.WPSnackbar @@ -65,7 +65,7 @@ class StatsFragment : Fragment(R.layout.stats_fragment), ScrollableViewInitializ lateinit var jetpackBrandingUtils: JetpackBrandingUtils @Inject - lateinit var mStatsTrafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + lateinit var mStatsTrafficSubscribersTabsFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig private val viewModel: StatsViewModel by activityViewModels() private lateinit var swipeToRefreshHelper: SwipeToRefreshHelper @@ -100,7 +100,7 @@ class StatsFragment : Fragment(R.layout.stats_fragment), ScrollableViewInitializ } private fun StatsFragmentBinding.initializeViews() { - statsTrafficTabEnabled = mStatsTrafficSubscribersTabFeatureConfig.isEnabled() + statsTrafficTabEnabled = mStatsTrafficSubscribersTabsFeatureConfig.isEnabled() val adapter = StatsPagerAdapter(this@StatsFragment) statsPager.adapter = adapter diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsModule.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsModule.kt index 4f1e0b036ecc..d895fbbf34f8 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsModule.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsModule.kt @@ -67,7 +67,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.subscribers.usecase import org.wordpress.android.ui.stats.refresh.lists.sections.subscribers.usecases.TotalSubscribersUseCase.TotalSubscribersUseCaseFactory import org.wordpress.android.ui.stats.refresh.utils.SelectedTrafficGranularityManager import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import javax.inject.Named import javax.inject.Singleton @@ -467,7 +467,7 @@ class StatsModule { @Named(WEEK_STATS_USE_CASE) weekStatsUseCase: BaseListUseCase, @Named(MONTH_STATS_USE_CASE) monthStatsUseCase: BaseListUseCase, @Named(YEAR_STATS_USE_CASE) yearStatsUseCase: BaseListUseCase, - trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig ): Map { return if (trafficSubscribersTabFeatureConfig.isEnabled()) { mapOf( diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewModel.kt index 6033c627f65e..5f6d1c028192 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewModel.kt @@ -56,7 +56,7 @@ import org.wordpress.android.ui.utils.UiString.UiStringRes import org.wordpress.android.util.JetpackBrandingUtils import org.wordpress.android.util.NetworkUtilsWrapper import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import org.wordpress.android.util.extensions.getSerializableExtraCompat import org.wordpress.android.util.mapNullable import org.wordpress.android.util.mergeNotNull @@ -83,7 +83,7 @@ class StatsViewModel private val notificationsTracker: SystemNotificationsTracker, private val jetpackBrandingUtils: JetpackBrandingUtils, private val jetpackFeatureRemovalOverlayUtil: JetpackFeatureRemovalOverlayUtil, - private val statsTrafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + private val statsTrafficSubscribersTabsFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig ) : ScopedViewModel(mainDispatcher) { private val _isRefreshing = MutableLiveData() val isRefreshing: LiveData = _isRefreshing @@ -150,7 +150,7 @@ class StatsViewModel } private fun getInitialTimeFrame(timeframe: StatsTimeframe?, launchedFrom: StatsLaunchedFrom?): StatsSection? { - if (statsTrafficSubscribersTabFeatureConfig.isEnabled() && launchedFrom == StatsLaunchedFrom.LINK) { + if (statsTrafficSubscribersTabsFeatureConfig.isEnabled() && launchedFrom == StatsLaunchedFrom.LINK) { setupDeeplinkForTrafficTab(timeframe) } @@ -202,7 +202,7 @@ class StatsViewModel statsSectionManager.setSelectedSection(it) val trafficGranularity = it.toStatsGranularity() - if (statsTrafficSubscribersTabFeatureConfig.isEnabled() && trafficGranularity != null) { + if (statsTrafficSubscribersTabsFeatureConfig.isEnabled() && trafficGranularity != null) { selectedTrafficGranularityManager.setSelectedTrafficGranularity(trafficGranularity) } } @@ -255,7 +255,7 @@ class StatsViewModel } private fun updateSelectedSectionByTrafficSubscribersTabFeatureConfig() { - if (statsTrafficSubscribersTabFeatureConfig.isEnabled()) { + if (statsTrafficSubscribersTabsFeatureConfig.isEnabled()) { val selectedSection = statsSectionManager.getSelectedSection() val isSelectedSectionRemoved = selectedSection == StatsSection.DAYS || selectedSection == StatsSection.WEEKS || diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListFragment.kt index b736abccf3be..ecf83655cb2d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListFragment.kt @@ -34,7 +34,7 @@ import org.wordpress.android.ui.stats.refresh.utils.StatsDateFormatter import org.wordpress.android.ui.stats.refresh.utils.StatsNavigator import org.wordpress.android.ui.stats.refresh.utils.drawDateSelector import org.wordpress.android.ui.stats.refresh.utils.toNameResource -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import org.wordpress.android.util.extensions.getParcelableCompat import org.wordpress.android.util.extensions.getSerializableCompat import org.wordpress.android.util.extensions.getSerializableExtraCompat @@ -58,7 +58,7 @@ class StatsListFragment : ViewPagerFragment(R.layout.stats_list_fragment) { lateinit var navigator: StatsNavigator @Inject - lateinit var statsTrafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + lateinit var statsTrafficSubscribersTabsFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig @Inject lateinit var selectedTrafficGranularityManager: SelectedTrafficGranularityManager @@ -155,7 +155,7 @@ class StatsListFragment : ViewPagerFragment(R.layout.stats_list_fragment) { } }) - if (statsTrafficSubscribersTabFeatureConfig.isEnabled()) { + if (statsTrafficSubscribersTabsFeatureConfig.isEnabled()) { dateSelector.granularitySpinner.adapter = ArrayAdapter( requireContext(), R.layout.filter_spinner_item, diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/WidgetBlockListProvider.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/WidgetBlockListProvider.kt index bae6db22493d..8af53e60635b 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/WidgetBlockListProvider.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/WidgetBlockListProvider.kt @@ -18,7 +18,7 @@ import org.wordpress.android.ui.stats.refresh.lists.widget.today.TodayWidgetBloc import org.wordpress.android.ui.stats.refresh.lists.widget.utils.getColorMode import org.wordpress.android.ui.stats.refresh.lists.widget.weeks.WeekWidgetBlockListViewModel import org.wordpress.android.ui.stats.refresh.utils.StatsLaunchedFrom -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import javax.inject.Inject class WidgetBlockListProvider( @@ -27,7 +27,7 @@ class WidgetBlockListProvider( intent: Intent ) : RemoteViewsFactory { @Inject - lateinit var trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + lateinit var trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig private val colorMode: Color = intent.getColorMode() private val siteId: Int = intent.getIntExtra(SITE_ID_KEY, -1) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/minified/MinifiedWidgetUpdater.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/minified/MinifiedWidgetUpdater.kt index 3c111a21a8e6..4ea99744346d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/minified/MinifiedWidgetUpdater.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/minified/MinifiedWidgetUpdater.kt @@ -34,7 +34,7 @@ import org.wordpress.android.ui.stats.refresh.utils.StatsUtils import org.wordpress.android.ui.stats.refresh.utils.trackMinifiedWidget import org.wordpress.android.util.NetworkUtilsWrapper import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import org.wordpress.android.viewmodel.ResourceProvider import javax.inject.Inject import javax.inject.Named @@ -51,7 +51,7 @@ class MinifiedWidgetUpdater private val todayInsightsStore: TodayInsightsStore, private val widgetUtils: WidgetUtils, private val analyticsTrackerWrapper: AnalyticsTrackerWrapper, - private val statsTrafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + private val statsTrafficSubscribersTabsFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig ) : WidgetUpdater { private val coroutineScope = CoroutineScope(defaultDispatcher) override fun updateAppWidget( @@ -79,8 +79,8 @@ class MinifiedWidgetUpdater views.setViewVisibility(R.id.widget_site_icon, View.VISIBLE) views.setViewVisibility(R.id.widget_retry_button, View.GONE) - val timeframe = if (statsTrafficSubscribersTabFeatureConfig.isEnabled()) TRAFFIC else INSIGHTS - val granularity = if (statsTrafficSubscribersTabFeatureConfig.isEnabled()) StatsGranularity.DAYS else null + val timeframe = if (statsTrafficSubscribersTabsFeatureConfig.isEnabled()) TRAFFIC else INSIGHTS + val granularity = if (statsTrafficSubscribersTabsFeatureConfig.isEnabled()) StatsGranularity.DAYS else null views.setOnClickPendingIntent( R.id.widget_container, diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetBlockListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetBlockListViewModel.kt index 9771e86a3958..b811c79a9c1a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetBlockListViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetBlockListViewModel.kt @@ -13,7 +13,7 @@ import org.wordpress.android.ui.stats.refresh.lists.widget.WidgetBlockListProvid import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsColorSelectionViewModel.Color import org.wordpress.android.ui.stats.refresh.utils.MILLION import org.wordpress.android.ui.stats.refresh.utils.StatsUtils -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import org.wordpress.android.viewmodel.ResourceProvider import javax.inject.Inject @@ -25,7 +25,7 @@ class TodayWidgetBlockListViewModel private val todayWidgetUpdater: TodayWidgetUpdater, private val appPrefsWrapper: AppPrefsWrapper, private val statsUtils: StatsUtils, - private val trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + private val trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig ) : WidgetBlockListViewModel { private var siteId: Int? = null private var colorMode: Color = Color.LIGHT diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetListProvider.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetListProvider.kt index 2094def7750b..e709214ab710 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetListProvider.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetListProvider.kt @@ -13,7 +13,7 @@ import org.wordpress.android.ui.stats.refresh.lists.widget.SITE_ID_KEY import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsColorSelectionViewModel.Color import org.wordpress.android.ui.stats.refresh.lists.widget.utils.getColorMode import org.wordpress.android.ui.stats.refresh.utils.StatsLaunchedFrom -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import javax.inject.Inject class TodayWidgetListProvider(val context: Context, intent: Intent) : RemoteViewsFactory { @@ -24,7 +24,7 @@ class TodayWidgetListProvider(val context: Context, intent: Intent) : RemoteView lateinit var widgetUpdater: TodayWidgetUpdater @Inject - lateinit var trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + lateinit var trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig private val colorMode: Color = intent.getColorMode() private val siteId: Int = intent.getIntExtra(SITE_ID_KEY, 0) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetUpdater.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetUpdater.kt index 7c8bb307b8b6..272fa3ce4bde 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetUpdater.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetUpdater.kt @@ -19,7 +19,7 @@ import org.wordpress.android.ui.stats.refresh.lists.widget.utils.WidgetUtils import org.wordpress.android.ui.stats.refresh.utils.trackWithWidgetType import org.wordpress.android.util.NetworkUtilsWrapper import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import org.wordpress.android.viewmodel.ResourceProvider import javax.inject.Inject @@ -32,7 +32,7 @@ class TodayWidgetUpdater private val resourceProvider: ResourceProvider, private val widgetUtils: WidgetUtils, private val analyticsTrackerWrapper: AnalyticsTrackerWrapper, - private val statsTrafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + private val statsTrafficSubscribersTabsFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig ) : WidgetUpdater { override fun updateAppWidget( context: Context, @@ -55,12 +55,12 @@ class TodayWidgetUpdater val widgetHasData = appPrefsWrapper.hasAppWidgetData(appWidgetId) if (networkAvailable && hasAccessToken && siteModel != null) { widgetUtils.setSiteIcon(siteModel, context, views, appWidgetId) - val timeframe = if (statsTrafficSubscribersTabFeatureConfig.isEnabled()) { + val timeframe = if (statsTrafficSubscribersTabsFeatureConfig.isEnabled()) { StatsTimeframe.TRAFFIC } else { StatsTimeframe.INSIGHTS } - val granularity = if (statsTrafficSubscribersTabFeatureConfig.isEnabled()) StatsGranularity.DAYS else null + val granularity = if (statsTrafficSubscribersTabsFeatureConfig.isEnabled()) StatsGranularity.DAYS else null siteModel.let { views.setOnClickPendingIntent( R.id.widget_title_container, diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/views/ViewsWidgetListProvider.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/views/ViewsWidgetListProvider.kt index cbdedb095be9..7bac89955264 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/views/ViewsWidgetListProvider.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/views/ViewsWidgetListProvider.kt @@ -16,7 +16,7 @@ import org.wordpress.android.ui.stats.refresh.lists.widget.IS_WIDE_VIEW_KEY import org.wordpress.android.ui.stats.refresh.lists.widget.SITE_ID_KEY import org.wordpress.android.ui.stats.refresh.lists.widget.utils.getColorMode import org.wordpress.android.ui.stats.refresh.utils.StatsLaunchedFrom -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import javax.inject.Inject class ViewsWidgetListProvider(val context: Context, intent: Intent) : RemoteViewsFactory { @@ -27,7 +27,7 @@ class ViewsWidgetListProvider(val context: Context, intent: Intent) : RemoteView lateinit var viewsWidgetUpdater: ViewsWidgetUpdater @Inject - lateinit var trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + lateinit var trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig private val isWideView: Boolean = intent.getBooleanExtra(IS_WIDE_VIEW_KEY, true) private val colorMode = intent.getColorMode() diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekViewsWidgetListProvider.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekViewsWidgetListProvider.kt index 6869787720cf..fc891c02ff9c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekViewsWidgetListProvider.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekViewsWidgetListProvider.kt @@ -15,7 +15,7 @@ import org.wordpress.android.ui.stats.refresh.lists.widget.SITE_ID_KEY import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsColorSelectionViewModel.Color import org.wordpress.android.ui.stats.refresh.lists.widget.utils.getColorMode import org.wordpress.android.ui.stats.refresh.utils.StatsLaunchedFrom -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import javax.inject.Inject class WeekViewsWidgetListProvider(val context: Context, intent: Intent) : RemoteViewsFactory { @@ -26,7 +26,7 @@ class WeekViewsWidgetListProvider(val context: Context, intent: Intent) : Remote lateinit var widgetUpdater: WeekViewsWidgetUpdater @Inject - lateinit var trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + lateinit var trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig private val colorMode: Color = intent.getColorMode() private val siteId: Int = intent.getIntExtra(SITE_ID_KEY, 0) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekViewsWidgetUpdater.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekViewsWidgetUpdater.kt index f72ac19a84d0..bba7fe38c37e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekViewsWidgetUpdater.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/weeks/WeekViewsWidgetUpdater.kt @@ -20,7 +20,7 @@ import org.wordpress.android.ui.stats.refresh.lists.widget.utils.WidgetUtils import org.wordpress.android.ui.stats.refresh.utils.trackWithWidgetType import org.wordpress.android.util.NetworkUtilsWrapper import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import org.wordpress.android.viewmodel.ResourceProvider import javax.inject.Inject @@ -32,7 +32,7 @@ class WeekViewsWidgetUpdater @Inject constructor( private val resourceProvider: ResourceProvider, private val widgetUtils: WidgetUtils, private val analyticsTrackerWrapper: AnalyticsTrackerWrapper, - private val statsTrafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + private val statsTrafficSubscribersTabsFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig ) : WidgetUpdater { override fun updateAppWidget( context: Context, @@ -53,8 +53,8 @@ class WeekViewsWidgetUpdater @Inject constructor( views.setTextViewText(R.id.widget_title, resourceProvider.getString(R.string.stats_widget_weekly_views_name)) val hasAccessToken = accountStore.hasAccessToken() val widgetHasData = appPrefsWrapper.hasAppWidgetData(appWidgetId) - val timeframe = if (statsTrafficSubscribersTabFeatureConfig.isEnabled()) TRAFFIC else INSIGHTS - val granularity = if (statsTrafficSubscribersTabFeatureConfig.isEnabled()) StatsGranularity.WEEKS else null + val timeframe = if (statsTrafficSubscribersTabsFeatureConfig.isEnabled()) TRAFFIC else INSIGHTS + val granularity = if (statsTrafficSubscribersTabsFeatureConfig.isEnabled()) StatsGranularity.WEEKS else null if (networkAvailable && hasAccessToken && siteModel != null) { widgetUtils.setSiteIcon(siteModel, context, views, appWidgetId) siteModel.let { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/SelectedSectionManager.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/SelectedSectionManager.kt index b5d37ece661c..1fbec9751f86 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/SelectedSectionManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/SelectedSectionManager.kt @@ -10,7 +10,7 @@ import org.wordpress.android.fluxc.network.utils.StatsGranularity.MONTHS import org.wordpress.android.fluxc.network.utils.StatsGranularity.WEEKS import org.wordpress.android.fluxc.network.utils.StatsGranularity.YEARS import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import javax.inject.Inject const val SELECTED_SECTION_KEY = "SELECTED_STATS_SECTION_KEY" @@ -18,7 +18,7 @@ const val SELECTED_SECTION_KEY = "SELECTED_STATS_SECTION_KEY" class SelectedSectionManager @Inject constructor( private val sharedPrefs: SharedPreferences, - private val statsTrafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + private val statsTrafficSubscribersTabsFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig ) { private val _liveSelectedSection = MutableLiveData() val liveSelectedSection: LiveData @@ -31,7 +31,7 @@ class SelectedSectionManager } fun getSelectedSection(): StatsSection { - val defaultValue = if (statsTrafficSubscribersTabFeatureConfig.isEnabled()) { + val defaultValue = if (statsTrafficSubscribersTabsFeatureConfig.isEnabled()) { StatsSection.TRAFFIC } else { StatsSection.INSIGHTS diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsDateFormatter.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsDateFormatter.kt index 81637114fee8..a2d88562cb5b 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsDateFormatter.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsDateFormatter.kt @@ -9,7 +9,7 @@ import org.wordpress.android.fluxc.network.utils.StatsGranularity.WEEKS import org.wordpress.android.fluxc.network.utils.StatsGranularity.YEARS import org.wordpress.android.fluxc.utils.SiteUtils import org.wordpress.android.util.LocaleManagerWrapper -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import org.wordpress.android.util.extensions.enforceWesternArabicNumerals import org.wordpress.android.viewmodel.ResourceProvider import java.text.DateFormat @@ -35,7 +35,7 @@ class StatsDateFormatter @Inject constructor( private val localeManagerWrapper: LocaleManagerWrapper, val resourceProvider: ResourceProvider, - val statsTrafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + val statsTrafficSubscribersTabsFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig ) { private val inputFormat: SimpleDateFormat get() { @@ -116,7 +116,7 @@ class StatsDateFormatter val startCalendar = Calendar.getInstance() startCalendar.time = endCalendar.time startCalendar.add(Calendar.DAY_OF_WEEK, -6) - return printWeek(startCalendar, endCalendar, statsTrafficSubscribersTabFeatureConfig.isEnabled()) + return printWeek(startCalendar, endCalendar, statsTrafficSubscribersTabsFeatureConfig.isEnabled()) } MONTHS -> outputMonthFormat.format(date) .replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfig.kt new file mode 100644 index 000000000000..c6df149a0f0a --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfig.kt @@ -0,0 +1,17 @@ +package org.wordpress.android.util.config + +import org.wordpress.android.annotation.RemoteFieldDefaultGenerater +import javax.inject.Inject + +const val IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT = "0" + +@RemoteFieldDefaultGenerater( + remoteField = IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD, + defaultValue = IN_APP_UPDATE_BLOCKING_VERSION_DEFAULT +) + +class InAppUpdateBlockingVersionConfig @Inject constructor(appConfig: AppConfig) : + RemoteConfigField( + appConfig, + IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD + ) diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateFlexibleIntervalConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateFlexibleIntervalConfig.kt new file mode 100644 index 000000000000..17541cc9dfea --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdateFlexibleIntervalConfig.kt @@ -0,0 +1,18 @@ +package org.wordpress.android.util.config + +import org.wordpress.android.annotation.RemoteFieldDefaultGenerater +import javax.inject.Inject + +const val IN_APP_UPDATE_FLEXIBLE_INTERVAL_REMOTE_FIELD = "in_app_update_flexible_interval_in_days_android" +const val IN_APP_UPDATE_FLEXIBLE_INTERVAL_DEFAULT = "5" + +@RemoteFieldDefaultGenerater( + remoteField = IN_APP_UPDATE_FLEXIBLE_INTERVAL_REMOTE_FIELD, + defaultValue = IN_APP_UPDATE_FLEXIBLE_INTERVAL_DEFAULT +) + +class InAppUpdateFlexibleIntervalConfig @Inject constructor(appConfig: AppConfig) : + RemoteConfigField( + appConfig, + IN_APP_UPDATE_FLEXIBLE_INTERVAL_REMOTE_FIELD + ) diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdatesFeatureConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdatesFeatureConfig.kt new file mode 100644 index 000000000000..3836cb91ff64 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/util/config/InAppUpdatesFeatureConfig.kt @@ -0,0 +1,16 @@ +package org.wordpress.android.util.config + +import org.wordpress.android.BuildConfig +import org.wordpress.android.annotation.Feature +import javax.inject.Inject + +private const val IN_APP_UPDATES_FEATURE_REMOTE_FIELD = "in_app_updates" + +@Feature(IN_APP_UPDATES_FEATURE_REMOTE_FIELD, false) +class InAppUpdatesFeatureConfig @Inject constructor( + appConfig: AppConfig +) : FeatureConfig( + appConfig, + BuildConfig.ENABLE_IN_APP_UPDATES, + IN_APP_UPDATES_FEATURE_REMOTE_FIELD +) diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt b/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt index 3113d9b84bfa..eae8223994d6 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt +++ b/WordPress/src/main/java/org/wordpress/android/util/config/RemoteConfigWrapper.kt @@ -7,9 +7,13 @@ import javax.inject.Singleton class RemoteConfigWrapper @Inject constructor( private val openWebLinksWithJetpackFlowFrequencyConfig: OpenWebLinksWithJetpackFlowFrequencyConfig, private val codeableGetFreeEstimateUrlConfig: CodeableGetFreeEstimateUrlConfig, - private val performanceMonitoringSampleRateConfig: PerformanceMonitoringSampleRateConfig + private val performanceMonitoringSampleRateConfig: PerformanceMonitoringSampleRateConfig, + private val inAppUpdateBlockingVersionConfig: InAppUpdateBlockingVersionConfig, + private val inAppUpdateFlexibleIntervalConfig: InAppUpdateFlexibleIntervalConfig, ) { fun getOpenWebLinksWithJetpackFlowFrequency() = openWebLinksWithJetpackFlowFrequencyConfig.getValue() fun getPerformanceMonitoringSampleRate() = performanceMonitoringSampleRateConfig.getValue() fun getCodeableGetFreeEstimateUrl() = codeableGetFreeEstimateUrlConfig.getValue() + fun getInAppUpdateBlockingVersion() = inAppUpdateBlockingVersionConfig.getValue() + fun getInAppUpdateFlexibleIntervalInDays() = inAppUpdateFlexibleIntervalConfig.getValue() } diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/StatsTrafficSubscribersTabFeatureConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/StatsTrafficSubscribersTabFeatureConfig.kt deleted file mode 100644 index ad1ed2c72b08..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/util/config/StatsTrafficSubscribersTabFeatureConfig.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.wordpress.android.util.config - -import org.wordpress.android.BuildConfig -import org.wordpress.android.annotation.Feature -import javax.inject.Inject - -private const val STATS_TRAFFIC_SUBSCRIBERS_TAB_REMOTE_FIELD = "stats_traffic_subscribers_tab" - -@Feature(STATS_TRAFFIC_SUBSCRIBERS_TAB_REMOTE_FIELD, false) -class StatsTrafficSubscribersTabFeatureConfig @Inject constructor( - appConfig: AppConfig -) : FeatureConfig( - appConfig, - BuildConfig.STATS_TRAFFIC_SUBSCRIBERS_TAB, - STATS_TRAFFIC_SUBSCRIBERS_TAB_REMOTE_FIELD -) diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/StatsTrafficSubscribersTabsFeatureConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/StatsTrafficSubscribersTabsFeatureConfig.kt new file mode 100644 index 000000000000..c548dc44617b --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/util/config/StatsTrafficSubscribersTabsFeatureConfig.kt @@ -0,0 +1,16 @@ +package org.wordpress.android.util.config + +import org.wordpress.android.BuildConfig +import org.wordpress.android.annotation.Feature +import javax.inject.Inject + +private const val STATS_TRAFFIC_SUBSCRIBERS_TABS_REMOTE_FIELD = "stats_traffic_subscribers_tabs" + +@Feature(STATS_TRAFFIC_SUBSCRIBERS_TABS_REMOTE_FIELD, false) +class StatsTrafficSubscribersTabsFeatureConfig @Inject constructor( + appConfig: AppConfig +) : FeatureConfig( + appConfig, + BuildConfig.STATS_TRAFFIC_SUBSCRIBERS_TABS, + STATS_TRAFFIC_SUBSCRIBERS_TABS_REMOTE_FIELD +) diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/VoiceToContentFeatureConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/VoiceToContentFeatureConfig.kt new file mode 100644 index 000000000000..75f5f9099108 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/util/config/VoiceToContentFeatureConfig.kt @@ -0,0 +1,16 @@ +package org.wordpress.android.util.config + +import org.wordpress.android.BuildConfig +import org.wordpress.android.annotation.Feature +import javax.inject.Inject + +private const val VOICE_TO_CONTENT_REMOTE_FIELD = "voice_to_content" + +@Feature(remoteField = VOICE_TO_CONTENT_REMOTE_FIELD, defaultValue = false) +class VoiceToContentFeatureConfig @Inject constructor( + appConfig: AppConfig +) : FeatureConfig( + appConfig, + BuildConfig.VOICE_TO_CONTENT, + VOICE_TO_CONTENT_REMOTE_FIELD, +) diff --git a/WordPress/src/main/java/org/wordpress/android/viewmodel/main/WPMainActivityViewModel.kt b/WordPress/src/main/java/org/wordpress/android/viewmodel/main/WPMainActivityViewModel.kt index af5e5e430673..5b53ed60f895 100644 --- a/WordPress/src/main/java/org/wordpress/android/viewmodel/main/WPMainActivityViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/viewmodel/main/WPMainActivityViewModel.kt @@ -19,6 +19,7 @@ import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.fluxc.store.bloggingprompts.BloggingPromptsStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.bloggingprompts.BloggingPromptsSettingsHelper +import org.wordpress.android.ui.debug.preferences.DebugPrefs import org.wordpress.android.ui.main.MainActionListItem import org.wordpress.android.ui.main.MainActionListItem.ActionType import org.wordpress.android.ui.main.MainActionListItem.ActionType.ANSWER_BLOGGING_PROMPT @@ -297,9 +298,12 @@ class WPMainActivityViewModel @Inject constructor( launch { val currentVersionCode = buildConfigWrapper.getAppVersionCode() val previousVersionCode = appPrefsWrapper.lastFeatureAnnouncementAppVersionCode + val alwaysShowAnnouncement = appPrefsWrapper.getDebugBooleanPref( + DebugPrefs.ALWAYS_SHOW_ANNOUNCEMENT.key + ) // only proceed to feature announcement logic if we are upgrading the app - if (previousVersionCode != 0 && previousVersionCode < currentVersionCode) { + if (alwaysShowAnnouncement || previousVersionCode != 0 && previousVersionCode < currentVersionCode) { if (canShowFeatureAnnouncement()) { analyticsTracker.track(Stat.FEATURE_ANNOUNCEMENT_SHOWN_ON_APP_UPGRADE) _onFeatureAnnouncementRequested.call() @@ -337,9 +341,11 @@ class WPMainActivityViewModel @Inject constructor( private suspend fun canShowFeatureAnnouncement(): Boolean { val cachedAnnouncement = featureAnnouncementProvider.getLatestFeatureAnnouncement(true) + val alwaysShowAnnouncement = appPrefsWrapper.getDebugBooleanPref(DebugPrefs.ALWAYS_SHOW_ANNOUNCEMENT.key) return cachedAnnouncement != null && - cachedAnnouncement.canBeDisplayedOnAppUpgrade(buildConfigWrapper.getAppVersionName()) && - appPrefsWrapper.featureAnnouncementShownVersion < cachedAnnouncement.announcementVersion + (alwaysShowAnnouncement || + cachedAnnouncement.canBeDisplayedOnAppUpgrade(buildConfigWrapper.getAppVersionName()) && + appPrefsWrapper.featureAnnouncementShownVersion < cachedAnnouncement.announcementVersion) } private fun getExternalFocusPointInfo(task: QuickStartTask?): List { diff --git a/WordPress/src/main/res/layout/item_choose_site.xml b/WordPress/src/main/res/layout/item_choose_site.xml index ceca730a5e12..9ffc791af9dd 100644 --- a/WordPress/src/main/res/layout/item_choose_site.xml +++ b/WordPress/src/main/res/layout/item_choose_site.xml @@ -69,6 +69,7 @@ style="@style/Widget.LoginFlow.TextView.List.Line1" android:layout_width="0dp" android:layout_height="wrap_content" + android:textAlignment="viewStart" android:layout_marginStart="@dimen/margin_extra_large" android:ellipsize="end" android:maxLines="1" @@ -77,6 +78,7 @@ app:layout_constraintEnd_toStartOf="@+id/pin" app:layout_constraintStart_toEndOf="@+id/avatar_container" app:layout_constraintTop_toTopOf="@+id/avatar_container" + app:layout_goneMarginEnd="@dimen/margin_extra_large" tools:text="Around the World with Pam" /> diff --git a/WordPress/src/main/res/values-ro/strings.xml b/WordPress/src/main/res/values-ro/strings.xml index 0997eb482100..94bd29249c17 100644 --- a/WordPress/src/main/res/values-ro/strings.xml +++ b/WordPress/src/main/res/values-ro/strings.xml @@ -1,6 +1,6 @@ - Найдена более свежая редакция страницы. - Обновление контента + Найдена более свежая редакция страницы + Обновление содержимого Подписчики Подписчики За последнюю неделю вы получили: просмотров — %1$s, комментариев — 1 @@ -36,12 +36,14 @@ Language: ru Шрифт Цветовая схема отправить отзыв + <Экспериментальный> Очистить выбранный цвет Нет подписок на метки Вы уже подписаны на эту метку Читательские предпочтения Отслеживаемые метки Карамель + h4x0r OLED Вечер Сепия @@ -54,14 +56,14 @@ Language: ru Подписаться на метку Читать Вы можете копировать текст своей записи на случай, если ваш контент окажется повреждён. Скопируйте данные об ошибке, чтобы сообщить их инженерам поддержки и устранить неполадку. - Произошла непредвиденная ошибка редактора. + Произошла непредвиденная ошибка редактора Нажмите здесь, чтобы скопировать текст записи Нажмите здесь, чтобы скопировать данные об ошибке Скопировать текст записи Скопировать данные об ошибке Кнопка для копирования текста записи Кнопка для копирования данных об ошибке - Не удалось обновить контент + Не удалось обновить содержимое Подпись к видео. %s Подпись к видео. Пустое значение Редактировать видео @@ -82,7 +84,7 @@ Language: ru Мониторинг сайтов Используйте раздел <b>Поиск</b>, чтобы искать сайты и теги. Перейдите в раздел <b>Подписки</b>, чтобы просмотреть подписной контент и управлять своими подписками. Перейти в «Подписки» - В блогах, на которые вы подписаны, в последнее время ничего нового не публиковалось. + В блогах, на которые вы подписаны, в последнее время ничего нового не публиковалось Подписывайтесь на блоги с помощью раздела «Поиск» или найдите блог, который вам уже понравился. Нет рекомендуемых блогов Нет записей с этой меткой diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index ea3875c997c2..105e868b313d 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -4820,6 +4820,9 @@ translators: %s: Select control option value e.g: "Auto, 25%". --> There was some trouble with the Security key login Please provide your security key to continue. + Update downloaded. Restart to apply. + Restart + Alternatively, you can flatten the content by ungrouping the block. For this reason, we recommend editing the block using the web editor. For this reason, we recommend editing the block using your web browser. diff --git a/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTrackerTest.kt b/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTrackerTest.kt new file mode 100644 index 000000000000..12932813e511 --- /dev/null +++ b/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateAnalyticsTrackerTest.kt @@ -0,0 +1,133 @@ +package org.wordpress.android.inappupdate + +import com.google.android.play.core.install.model.AppUpdateType +import org.assertj.core.api.Assertions +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnitRunner +import org.mockito.kotlin.mock +import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.wordpress.android.analytics.AnalyticsTracker +import org.wordpress.android.inappupdate.InAppUpdateAnalyticsTracker.Companion.PROPERTY_UPDATE_TYPE +import org.wordpress.android.inappupdate.InAppUpdateAnalyticsTracker.Companion.UPDATE_TYPE_BLOCKING +import org.wordpress.android.inappupdate.InAppUpdateAnalyticsTracker.Companion.UPDATE_TYPE_FLEXIBLE + +@RunWith(MockitoJUnitRunner::class) +class InAppUpdateAnalyticsTrackerTest { + private val analyticsTracker: AnalyticsTrackerWrapper = mock() + lateinit var tracker: InAppUpdateAnalyticsTracker + + private val flexibleProps = mapOf( + PROPERTY_UPDATE_TYPE to UPDATE_TYPE_FLEXIBLE + ) + private val blockingProps = mapOf( + PROPERTY_UPDATE_TYPE to UPDATE_TYPE_BLOCKING + ) + private val emptyProps = emptyMap() + + @Before + fun setUp() { + tracker = InAppUpdateAnalyticsTracker(analyticsTracker) + } + + @Test + fun `trackUpdateShown tracks flexible update shown`() { + tracker.trackUpdateShown(AppUpdateType.FLEXIBLE) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_SHOWN, + expectedProps = flexibleProps + ) + } + + @Test + fun `trackUpdateShown tracks immediate update shown`() { + tracker.trackUpdateShown(AppUpdateType.IMMEDIATE) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_SHOWN, + expectedProps = blockingProps + ) + } + + @Test + fun `trackUpdateShown tracks invalid update shown`() { + tracker.trackUpdateShown(-1) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_SHOWN, + expectedProps = emptyProps + ) + } + + @Test + fun `trackUpdateAccepted tracks flexible update accepted`() { + tracker.trackUpdateAccepted(AppUpdateType.FLEXIBLE) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_ACCEPTED, + expectedProps = flexibleProps + ) + } + + @Test + fun `trackUpdateAccepted tracks immediate update accepted`() { + tracker.trackUpdateAccepted(AppUpdateType.IMMEDIATE) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_ACCEPTED, + expectedProps = blockingProps + ) + } + + @Test + fun `trackUpdateAccepted tracks invalid update accepted`() { + tracker.trackUpdateAccepted(-1) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_ACCEPTED, + expectedProps = emptyProps + ) + } + + @Test + fun `trackUpdateDismissed tracks flexible update dismissed`() { + tracker.trackUpdateDismissed(AppUpdateType.FLEXIBLE) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_DISMISSED, + expectedProps = flexibleProps + ) + } + + @Test + fun `trackUpdateDismissed tracks immediate update dismissed`() { + tracker.trackUpdateDismissed(AppUpdateType.IMMEDIATE) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_DISMISSED, + expectedProps = blockingProps + ) + } + + @Test + fun `trackUpdateDismissed tracks invalid update dismissed`() { + tracker.trackUpdateDismissed(-1) + verifyCorrectEventTracking( + expectedEvent = AnalyticsTracker.Stat.IN_APP_UPDATE_DISMISSED, + expectedProps = emptyProps + ) + } + + private fun mapCaptor() = argumentCaptor>() + private fun verifyCorrectEventTracking( + expectedEvent: AnalyticsTracker.Stat, + expectedProps: Map, + expectedTimes: Int = 1 + ) { + mapCaptor().apply { + verify(analyticsTracker, times(expectedTimes)).track( + eq(expectedEvent), + capture() + ) + Assertions.assertThat(firstValue).isEqualTo(expectedProps) + } + } +} diff --git a/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateManagerImplTest.kt b/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateManagerImplTest.kt new file mode 100644 index 000000000000..c54ba84bfd1e --- /dev/null +++ b/WordPress/src/test/java/org/wordpress/android/inappupdate/InAppUpdateManagerImplTest.kt @@ -0,0 +1,235 @@ +package org.wordpress.android.inappupdate + +import android.app.Activity +import android.content.Context +import android.content.SharedPreferences +import com.google.android.gms.tasks.OnFailureListener +import com.google.android.gms.tasks.OnSuccessListener +import com.google.android.gms.tasks.Task +import com.google.android.play.core.appupdate.AppUpdateInfo +import com.google.android.play.core.appupdate.AppUpdateManager +import com.google.android.play.core.appupdate.AppUpdateOptions +import com.google.android.play.core.install.model.AppUpdateType +import com.google.android.play.core.install.model.InstallStatus +import com.google.android.play.core.install.model.UpdateAvailability +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyLong +import org.mockito.ArgumentMatchers.anyString +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.times +import org.mockito.Mockito.`when` +import org.mockito.junit.MockitoJUnitRunner +import org.mockito.kotlin.any +import org.mockito.kotlin.verify +import org.wordpress.android.inappupdate.IInAppUpdateManager.Companion.APP_UPDATE_FLEXIBLE_REQUEST_CODE +import org.wordpress.android.inappupdate.IInAppUpdateManager.Companion.APP_UPDATE_IMMEDIATE_REQUEST_CODE +import org.wordpress.android.inappupdate.InAppUpdateManagerImpl.Companion.IMMEDIATE_UPDATE_INTERVAL_IN_MILLIS +import org.wordpress.android.inappupdate.InAppUpdateManagerImpl.Companion.KEY_LAST_APP_UPDATE_CHECK_TIME +import org.wordpress.android.util.BuildConfigWrapper +import org.wordpress.android.util.config.RemoteConfigWrapper + +@RunWith(MockitoJUnitRunner::class) +class InAppUpdateManagerImplTest { + @Mock + lateinit var applicationContext: Context + + @Mock + lateinit var appUpdateManager: AppUpdateManager + + @Mock + lateinit var remoteConfigWrapper: RemoteConfigWrapper + + @Mock + lateinit var buildConfigWrapper: BuildConfigWrapper + + @Mock + lateinit var inAppUpdateAnalyticsTracker: InAppUpdateAnalyticsTracker + + @Mock + lateinit var updateListener: InAppUpdateListener + + @Mock + lateinit var activity: Activity + + @Mock + lateinit var appUpdateInfo: AppUpdateInfo + + @Mock + lateinit var sharedPreferences: SharedPreferences + + @Mock + lateinit var sharedPreferencesEditor: SharedPreferences.Editor + + lateinit var currentTimeProvider: () -> Long + + lateinit var inAppUpdateManager: InAppUpdateManagerImpl + + @Before + fun setUp() { + currentTimeProvider = {1715866314746L} // Thu May 16 2024 13:31:54 UTC + + // Mock SharedPreferences behavior + `when`(applicationContext.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPreferences) + `when`(sharedPreferences.getInt(anyString(), anyInt())).thenReturn(-1) + `when`(sharedPreferences.edit()).thenReturn(sharedPreferencesEditor) + `when`(sharedPreferencesEditor.putInt(anyString(), anyInt())).thenReturn(sharedPreferencesEditor) + `when`(sharedPreferencesEditor.putLong(anyString(), anyLong())).thenReturn(sharedPreferencesEditor) + + inAppUpdateManager = InAppUpdateManagerImpl( + applicationContext, + appUpdateManager, + remoteConfigWrapper, + buildConfigWrapper, + inAppUpdateAnalyticsTracker, + currentTimeProvider + ) + } + + @Test + fun `checkForAppUpdate when update is not available does not trigger update`() { + // Arrange + val task = mockAppUpdateInfoTask(appUpdateInfo) + `when`(appUpdateManager.appUpdateInfo).thenReturn(task) + `when`(appUpdateInfo.updateAvailability()).thenReturn(UpdateAvailability.UPDATE_NOT_AVAILABLE) + + // Act + inAppUpdateManager.checkForAppUpdate(activity, updateListener) + + // Assert + verify(appUpdateManager.appUpdateInfo).addOnSuccessListener(any()) + verify(appUpdateManager, times(0)).startUpdateFlowForResult( + any(), + any(), + any(), + anyInt() + ) + } + + @Test + fun `checkForAppUpdate when update is downloaded calls update listener`() { + // Arrange + val task = mockAppUpdateInfoTask(appUpdateInfo) + `when`(appUpdateManager.appUpdateInfo).thenReturn(task) + `when`(appUpdateInfo.updateAvailability()).thenReturn(UpdateAvailability.UPDATE_AVAILABLE) + `when`(appUpdateInfo.installStatus()).thenReturn(InstallStatus.DOWNLOADED) + + // Act + inAppUpdateManager.checkForAppUpdate(activity, updateListener) + + // Assert + verify(updateListener).onAppUpdateDownloaded() + } + + @Test + fun `checkForAppUpdate requests immediate update when necessary`() { + // Arrange + val task = mockAppUpdateInfoTask(appUpdateInfo) + `when`(appUpdateManager.appUpdateInfo).thenReturn(task) + `when`(appUpdateInfo.updateAvailability()).thenReturn(UpdateAvailability.UPDATE_AVAILABLE) + `when`(appUpdateInfo.installStatus()).thenReturn(InstallStatus.UNKNOWN) + `when`(buildConfigWrapper.getAppVersionCode()).thenReturn(100) // current version + `when`(remoteConfigWrapper.getInAppUpdateBlockingVersion()).thenReturn(200) // blocking version + val lastCheckTimestamp = currentTimeProvider.invoke() - IMMEDIATE_UPDATE_INTERVAL_IN_MILLIS + `when`(sharedPreferences.getLong( eq(KEY_LAST_APP_UPDATE_CHECK_TIME), anyLong())).thenReturn(lastCheckTimestamp) + + // Act + inAppUpdateManager.checkForAppUpdate(activity, updateListener) + + // Assert + verify(appUpdateManager).startUpdateFlowForResult( + any(), + any(), + any(), + eq(APP_UPDATE_IMMEDIATE_REQUEST_CODE) + ) + } + + @Test + fun `checkForAppUpdate requests flexible update when necessary`() { + // Arrange + val task = mockAppUpdateInfoTask(appUpdateInfo) + `when`(appUpdateManager.appUpdateInfo).thenReturn(task) + `when`(appUpdateInfo.updateAvailability()).thenReturn(UpdateAvailability.UPDATE_AVAILABLE) + `when`(appUpdateInfo.installStatus()).thenReturn(InstallStatus.UNKNOWN) + `when`(buildConfigWrapper.getAppVersionCode()).thenReturn(100) // current version + `when`(remoteConfigWrapper.getInAppUpdateBlockingVersion()).thenReturn(50) // blocking version + `when`(remoteConfigWrapper.getInAppUpdateFlexibleIntervalInDays()).thenReturn(1) + val lastCheckTimestamp = currentTimeProvider.invoke() - 1000*60*60*24 + `when`(sharedPreferences.getLong( eq(KEY_LAST_APP_UPDATE_CHECK_TIME), anyLong())).thenReturn(lastCheckTimestamp) + + // Act + inAppUpdateManager.checkForAppUpdate(activity, updateListener) + + // Assert + verify(appUpdateManager).startUpdateFlowForResult( + any(), + any(), + any(), + eq(APP_UPDATE_FLEXIBLE_REQUEST_CODE) + ) + } + + @Test + fun `checkForAppUpdate handles developer triggered update in progress`() { + // Arrange + val task = mockAppUpdateInfoTask(appUpdateInfo) + `when`(appUpdateManager.appUpdateInfo).thenReturn(task) + `when`(appUpdateInfo.updateAvailability()).thenReturn(UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) + `when`(appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)).thenReturn(true) + `when`(buildConfigWrapper.getAppVersionCode()).thenReturn(100) + `when`(remoteConfigWrapper.getInAppUpdateBlockingVersion()).thenReturn(200) + + // Act + inAppUpdateManager.checkForAppUpdate(activity, updateListener) + + // Assert + verify(appUpdateManager).startUpdateFlowForResult( + eq(appUpdateInfo), + eq(activity), + any(), + eq(APP_UPDATE_IMMEDIATE_REQUEST_CODE) + ) + } + + @Test + fun `checkForAppUpdate handles failure correctly`() { + // Arrange + val task = mockAppUpdateInfoTaskWithFailure() + `when`(appUpdateManager.appUpdateInfo).thenReturn(task) + + // Act + inAppUpdateManager.checkForAppUpdate(activity, updateListener) + + // Assert + verify(appUpdateManager.appUpdateInfo).addOnFailureListener(any()) + } + + // Helper method to mock Task with success + @Suppress("UNCHECKED_CAST") + private fun mockAppUpdateInfoTask(appUpdateInfo: AppUpdateInfo): Task { + val task = mock(Task::class.java) as Task + `when`(task.addOnSuccessListener(any())).thenAnswer { invocation -> + (invocation.arguments[0] as OnSuccessListener).onSuccess(appUpdateInfo) + task + } + `when`(task.addOnFailureListener(any())).thenReturn(task) + return task + } + + // Helper method to mock Task with failure + @Suppress("UNCHECKED_CAST") + private fun mockAppUpdateInfoTaskWithFailure(): Task { + val task = mock(Task::class.java) as Task + `when`(task.addOnFailureListener(any())).thenAnswer { invocation -> + (invocation.arguments[0] as OnFailureListener).onFailure(Exception("Update check failed")) + task + } + `when`(task.addOnSuccessListener(any())).thenReturn(task) + return task + } +} diff --git a/WordPress/src/test/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModelTest.kt index 7e0d136c874b..15e7168d5e42 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/debug/preferences/DebugSharedPreferenceFlagsViewModelTest.kt @@ -20,10 +20,14 @@ class DebugSharedPreferenceFlagsViewModelTest { @Test fun `WHEN init THEN should load the flags from the prefs`() { whenever(prefsWrapper.getAllPrefs()).thenReturn(mapOf("key" to true)) + DebugPrefs.entries.forEach { + whenever(prefsWrapper.getDebugBooleanPref(it.key, false)).thenReturn(false) + } initViewModel() assertTrue(viewModel.uiStateFlow.value["key"]!!) + assertTrue(viewModel.uiStateFlow.value.size >= 2) } @Test diff --git a/WordPress/src/test/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModelTest.kt index b1ed67de828e..7fba132c2b51 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/sitecreation/previews/SitePreviewViewModelTest.kt @@ -62,7 +62,7 @@ class SitePreviewViewModelTest : BaseUnitTest() { private lateinit var uiStateObserver: Observer @Mock - private lateinit var onOkClickedObserver: Observer + private lateinit var onOkClickedObserver: Observer @Mock private lateinit var preloadPreviewObserver: Observer diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/StatsViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/StatsViewModelTest.kt index 177cf4beb485..7844a4e8e36d 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/StatsViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/StatsViewModelTest.kt @@ -47,7 +47,7 @@ import org.wordpress.android.ui.utils.UiString.UiStringRes import org.wordpress.android.util.JetpackBrandingUtils import org.wordpress.android.util.NetworkUtilsWrapper import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import org.wordpress.android.viewmodel.Event import org.wordpress.android.viewmodel.ResourceProvider @@ -99,7 +99,7 @@ class StatsViewModelTest : BaseUnitTest() { lateinit var jetpackFeatureRemovalOverlayUtil: JetpackFeatureRemovalOverlayUtil @Mock - lateinit var trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + lateinit var trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig private lateinit var viewModel: StatsViewModel private val _liveSelectedSection = MutableLiveData() private val liveSelectedSection: LiveData = _liveSelectedSection diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetBlockListViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetBlockListViewModelTest.kt index e665277cf1e9..63b2f5b57165 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetBlockListViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/widget/today/TodayWidgetBlockListViewModelTest.kt @@ -21,7 +21,7 @@ import org.wordpress.android.ui.prefs.AppPrefsWrapper import org.wordpress.android.ui.stats.refresh.lists.widget.WidgetBlockListProvider.BlockItemUiModel import org.wordpress.android.ui.stats.refresh.lists.widget.configuration.StatsColorSelectionViewModel.Color import org.wordpress.android.ui.stats.refresh.utils.StatsUtils -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import org.wordpress.android.viewmodel.ResourceProvider @RunWith(MockitoJUnitRunner::class) @@ -51,7 +51,7 @@ class TodayWidgetBlockListViewModelTest { private lateinit var todayWidgetUpdater: TodayWidgetUpdater @Mock - private lateinit var trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + private lateinit var trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig private lateinit var viewModel: TodayWidgetBlockListViewModel private val siteId: Int = 15 private val appWidgetId: Int = 1 diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/utils/SelectedSectionManagerTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/utils/SelectedSectionManagerTest.kt index 164b263a442b..50d8151551d1 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/utils/SelectedSectionManagerTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/utils/SelectedSectionManagerTest.kt @@ -13,12 +13,12 @@ import org.mockito.kotlin.whenever import org.wordpress.android.BaseUnitTest import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.MONTHS -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig @ExperimentalCoroutinesApi class SelectedSectionManagerTest : BaseUnitTest() { @Mock - private lateinit var trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + private lateinit var trafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig @Mock lateinit var sharedPreferences: SharedPreferences diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/utils/StatsDateFormatterTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/utils/StatsDateFormatterTest.kt index bec7874c5f06..7e10c522c108 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/utils/StatsDateFormatterTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/utils/StatsDateFormatterTest.kt @@ -17,7 +17,7 @@ import org.wordpress.android.fluxc.network.utils.StatsGranularity.MONTHS import org.wordpress.android.fluxc.network.utils.StatsGranularity.WEEKS import org.wordpress.android.fluxc.network.utils.StatsGranularity.YEARS import org.wordpress.android.util.LocaleManagerWrapper -import org.wordpress.android.util.config.StatsTrafficSubscribersTabFeatureConfig +import org.wordpress.android.util.config.StatsTrafficSubscribersTabsFeatureConfig import org.wordpress.android.viewmodel.ResourceProvider import java.util.Calendar import java.util.Locale @@ -29,7 +29,7 @@ class StatsDateFormatterTest : BaseUnitTest() { lateinit var localeManagerWrapper: LocaleManagerWrapper @Mock - lateinit var mStatsTrafficSubscribersTabFeatureConfig: StatsTrafficSubscribersTabFeatureConfig + lateinit var mStatsTrafficSubscribersTabsFeatureConfig: StatsTrafficSubscribersTabsFeatureConfig @Mock lateinit var resourceProvider: ResourceProvider @@ -38,11 +38,11 @@ class StatsDateFormatterTest : BaseUnitTest() { @Before fun setUp() { whenever(localeManagerWrapper.getLocale()).thenReturn(Locale.US) - whenever(mStatsTrafficSubscribersTabFeatureConfig.isEnabled()).thenReturn(false) + whenever(mStatsTrafficSubscribersTabsFeatureConfig.isEnabled()).thenReturn(false) statsDateFormatter = StatsDateFormatter( localeManagerWrapper, resourceProvider, - mStatsTrafficSubscribersTabFeatureConfig + mStatsTrafficSubscribersTabsFeatureConfig ) } @@ -83,7 +83,7 @@ class StatsDateFormatterTest : BaseUnitTest() { @Test fun `prints a week date in the same year in string format with stats traffic tab enabled`() { - whenever(mStatsTrafficSubscribersTabFeatureConfig.isEnabled()).thenReturn(true) + whenever(mStatsTrafficSubscribersTabsFeatureConfig.isEnabled()).thenReturn(true) val unparsedDate = "2018W12W19" val result = "Dec 17 - Dec 23, 2018" whenever( @@ -101,7 +101,7 @@ class StatsDateFormatterTest : BaseUnitTest() { @Test fun `prints a week date in two different years in string format with traffic tab enabled`() { - whenever(mStatsTrafficSubscribersTabFeatureConfig.isEnabled()).thenReturn(true) + whenever(mStatsTrafficSubscribersTabsFeatureConfig.isEnabled()).thenReturn(true) val unparsedDate = "2018W12W31" val result = "Dec 31, 2018 - Jan 6, 2019" whenever( diff --git a/WordPress/src/wordpress/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfigConstants.kt b/WordPress/src/wordpress/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfigConstants.kt new file mode 100644 index 000000000000..168bda3e189a --- /dev/null +++ b/WordPress/src/wordpress/java/org/wordpress/android/util/config/InAppUpdateBlockingVersionConfigConstants.kt @@ -0,0 +1,5 @@ +package org.wordpress.android.util.config + +const val IN_APP_UPDATE_BLOCKING_VERSION_REMOTE_FIELD = "wp_in_app_update_blocking_version_android" + + diff --git a/build.gradle b/build.gradle index e11b395fb220..c07a8e74de6a 100644 --- a/build.gradle +++ b/build.gradle @@ -23,14 +23,14 @@ ext { automatticAboutVersion = '1.4.0' automatticRestVersion = '1.0.8' automatticTracksVersion = '5.0.0' - gutenbergMobileVersion = 'v1.118.0' + gutenbergMobileVersion = 'v1.119.0-alpha2' wordPressAztecVersion = 'v2.1.3' - wordPressFluxCVersion = 'trunk-935a54775a4086640c7ac1c9fcd2d494d8acdf65' + wordPressFluxCVersion = '2.79.0' wordPressLoginVersion = '1.15.0' wordPressPersistentEditTextVersion = '1.0.2' wordPressUtilsVersion = '3.14.0' indexosMediaForMobileVersion = '43a9026f0973a2f0a74fa813132f6a16f7499c3a' - gravatarVersion = '0.2.0' + gravatarVersion = '0.3.0' // debug flipperVersion = '0.245.0' @@ -90,6 +90,7 @@ ext { squareupRetrofitVersion = '2.9.0' uCropVersion = '2.2.9' zendeskVersion = '5.1.2' + googlePlayInAppUpdateVersion = '2.1.0' // react native facebookReactVersion = '0.73.3' diff --git a/fastlane/jetpack_metadata/android/ar/changelogs/1426.txt b/fastlane/jetpack_metadata/android/ar/changelogs/1426.txt deleted file mode 100644 index c6467ac8d574..000000000000 --- a/fastlane/jetpack_metadata/android/ar/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -أعدنا تصميم شاشة منتقي الموقع. يمكنك الآن تثبيت المواقع التي تفضلها، والاطلاع على المواقع التي تم الوصول إليها مؤخرًا، والمزيد. أزلنا أيضًا إمكانية إظهار المواقع وإخفائها. -(هل قلنا كلمة "مواقع" بما فيه الكفاية؟ نظن ذلك). diff --git a/fastlane/jetpack_metadata/android/ar/changelogs/1430.txt b/fastlane/jetpack_metadata/android/ar/changelogs/1430.txt new file mode 100644 index 000000000000..f86e6fe00bf5 --- /dev/null +++ b/fastlane/jetpack_metadata/android/ar/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8: +في شاشة الإحصاءات، تظهر النسب المئوية الآن في موضعها الصحيح لمستخدمي اللغات من اليمين إلى اليسار. تم الأمر. + diff --git a/fastlane/jetpack_metadata/android/de-DE/changelogs/1426.txt b/fastlane/jetpack_metadata/android/de-DE/changelogs/1426.txt deleted file mode 100644 index ca33e96a4e3f..000000000000 --- a/fastlane/jetpack_metadata/android/de-DE/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -Wir haben den Bildschirm für die Website-Auswahl neu gestaltet. Du kannst jetzt deine Lieblings-Websites anheften, kürzlich aufgerufene Websites anzeigen und vieles mehr. Zudem haben wir die Möglichkeit zum Anzeigen und Ausblenden von Websites entfernt. -(Haben wir den Begriff „Websites“ oft genug erwähnt? Wir würden sagen, ja.) diff --git a/fastlane/jetpack_metadata/android/de-DE/changelogs/1430.txt b/fastlane/jetpack_metadata/android/de-DE/changelogs/1430.txt new file mode 100644 index 000000000000..41dd0cf10313 --- /dev/null +++ b/fastlane/jetpack_metadata/android/de-DE/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8: +Auf dem Stats-Bildschirm werden die Prozentwerte für Benutzer von Rechts-nach-Links-Sprachen nun in ihrer korrekten Position angezeigt. Genau richtig. + diff --git a/fastlane/jetpack_metadata/android/en-US/changelogs/1426.txt b/fastlane/jetpack_metadata/android/en-US/changelogs/1426.txt deleted file mode 100644 index 42b557a2dfb5..000000000000 --- a/fastlane/jetpack_metadata/android/en-US/changelogs/1426.txt +++ /dev/null @@ -1,2 +0,0 @@ -We redesigned the site picker screen. You can now pin your favorite sites, see recently accessed sites, and more. We also removed the ability to show and hide sites. -(Did we say the word “sites” enough? We think so.) diff --git a/fastlane/jetpack_metadata/android/en-US/changelogs/1430.txt b/fastlane/jetpack_metadata/android/en-US/changelogs/1430.txt new file mode 100644 index 000000000000..9f2bf4ba3ae2 --- /dev/null +++ b/fastlane/jetpack_metadata/android/en-US/changelogs/1430.txt @@ -0,0 +1,2 @@ +On the Stats screen, percentages now appear in their correct position for right-to-left language users. Right on. + diff --git a/fastlane/jetpack_metadata/android/es-ES/changelogs/1426.txt b/fastlane/jetpack_metadata/android/es-ES/changelogs/1426.txt deleted file mode 100644 index b134bb742d50..000000000000 --- a/fastlane/jetpack_metadata/android/es-ES/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -Hemos rediseñado la pantalla del selector de sitios. Ahora puedes fijar tus sitios favoritos, ver los últimos sitios a los que has accedido y muchas más funciones. También hemos eliminado la funcionalidad para mostrar y ocultar sitios. -(¿Hemos mencionado bastante la palabra "sitios"? Diría que sí). diff --git a/fastlane/jetpack_metadata/android/es-ES/changelogs/1430.txt b/fastlane/jetpack_metadata/android/es-ES/changelogs/1430.txt new file mode 100644 index 000000000000..2d27964fdbee --- /dev/null +++ b/fastlane/jetpack_metadata/android/es-ES/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8: +En la pantalla Estadísticas, los porcentajes aparecen ahora en su posición correcta para los usuarios de idiomas con escritura de derecha a izquierda. Muy bien. + diff --git a/fastlane/jetpack_metadata/android/fr-FR/changelogs/1426.txt b/fastlane/jetpack_metadata/android/fr-FR/changelogs/1426.txt deleted file mode 100644 index 897a91f6f6d3..000000000000 --- a/fastlane/jetpack_metadata/android/fr-FR/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7 : -Nous avons repensé l’écran de sélection de site. Vous pouvez désormais épingler vos sites favoris, voir les sites récemment consultés et plus encore. Nous avons également supprimé la possibilité d’afficher et de masquer des sites. -(Si jamais vous ne l’aviez pas remarqué, il est question de sites.) diff --git a/fastlane/jetpack_metadata/android/fr-FR/changelogs/1430.txt b/fastlane/jetpack_metadata/android/fr-FR/changelogs/1430.txt new file mode 100644 index 000000000000..ebef70697f81 --- /dev/null +++ b/fastlane/jetpack_metadata/android/fr-FR/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8 : +Sur l’écran Statistiques, les pourcentages apparaissent désormais au bon endroit pour les locuteurs de langues qui se lisent de droite à gauche. À gauche toute ! + diff --git a/fastlane/jetpack_metadata/android/id/changelogs/1426.txt b/fastlane/jetpack_metadata/android/id/changelogs/1426.txt deleted file mode 100644 index c98856e036af..000000000000 --- a/fastlane/jetpack_metadata/android/id/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -Kami mendesain ulang tampilan pemilih situs. Sekarang, Anda bisa menyematkan situs favorit, melihat situs yang baru-baru ini diakses, dan masih banyak lagi. Kami juga menghapus fitur untuk menampilkan dan menyembunyikan situs. -(Apa pembaruan kami terkait "situs" sudah lengkap? Kami rasa sudah.) diff --git a/fastlane/jetpack_metadata/android/id/changelogs/1430.txt b/fastlane/jetpack_metadata/android/id/changelogs/1430.txt new file mode 100644 index 000000000000..9ea2d8bcf04a --- /dev/null +++ b/fastlane/jetpack_metadata/android/id/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8: +Di layar Statistik, persentase kini muncul di posisi yang benar untuk pengguna bahasa yang ditulis dari kanan ke kiri. Benar. + diff --git a/fastlane/jetpack_metadata/android/it-IT/changelogs/1426.txt b/fastlane/jetpack_metadata/android/it-IT/changelogs/1426.txt deleted file mode 100644 index 14bf70f751f2..000000000000 --- a/fastlane/jetpack_metadata/android/it-IT/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -Abbiamo rinnovato la schermata della scelta siti. Ora puoi fissare i tuoi siti preferiti, vedere i siti consultati di recente e altro ancora. Abbiamo anche rimosso la possibilità di mostrare e nascondere i siti. -(Abbiamo usato abbastanza la parola "siti"? Pensiamo di sì.) diff --git a/fastlane/jetpack_metadata/android/it-IT/changelogs/1430.txt b/fastlane/jetpack_metadata/android/it-IT/changelogs/1430.txt new file mode 100644 index 000000000000..9e1faf386618 --- /dev/null +++ b/fastlane/jetpack_metadata/android/it-IT/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8: +Nella pagina Statistiche ora le percentuali sono nella posizione corretta per gli utenti di lingue da destra a sinistra. Proprio così! + diff --git a/fastlane/jetpack_metadata/android/iw-IL/changelogs/1426.txt b/fastlane/jetpack_metadata/android/iw-IL/changelogs/1426.txt deleted file mode 100644 index f46943020b9a..000000000000 --- a/fastlane/jetpack_metadata/android/iw-IL/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -עיצבנו מחדש את המסך של בוחר האתרים. כעת אפשר לנעוץ אתרים מועדפים, לראות אתרים שניגשת אליהם לאחרונה ועוד. בנוסף, הסרנו את היכולת להציג ולהסתיר אתרים. -(חזרנו יותר מדי על המילה "אתרים"? כן, אנחנו חושבים שכן.) diff --git a/fastlane/jetpack_metadata/android/iw-IL/changelogs/1430.txt b/fastlane/jetpack_metadata/android/iw-IL/changelogs/1430.txt new file mode 100644 index 000000000000..bfd071600679 --- /dev/null +++ b/fastlane/jetpack_metadata/android/iw-IL/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8: +במסך 'נתונים סטטיסטיים', האחוזים מופיעים כעת לפי המיקום התקין אצל משתמשים שמציגים את הממשק בשפות שנקראות מימין לשמאל. ישר ולעניין. + diff --git a/fastlane/jetpack_metadata/android/ja-JP/changelogs/1426.txt b/fastlane/jetpack_metadata/android/ja-JP/changelogs/1426.txt deleted file mode 100644 index 209dbcdaabaa..000000000000 --- a/fastlane/jetpack_metadata/android/ja-JP/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -サイトピッカー画面を再設計しました。 お気に入りのサイトを固定したり、最近アクセスしたサイトを表示したりできるようになりました。 また、サイトを表示または非表示する機能が削除されました -(サイトと言いすぎましたか ? そうかもしれません)。 diff --git a/fastlane/jetpack_metadata/android/ja-JP/changelogs/1430.txt b/fastlane/jetpack_metadata/android/ja-JP/changelogs/1430.txt new file mode 100644 index 000000000000..91976aadfbc5 --- /dev/null +++ b/fastlane/jetpack_metadata/android/ja-JP/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8: +「統計」画面で、右から左に記述する言語のユーザーに対してパーセンテージが正しい位置に表示されるようになりました。 ご活用ください。 + diff --git a/fastlane/jetpack_metadata/android/ko-KR/changelogs/1426.txt b/fastlane/jetpack_metadata/android/ko-KR/changelogs/1426.txt deleted file mode 100644 index 04e660827ea4..000000000000 --- a/fastlane/jetpack_metadata/android/ko-KR/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -사이트 선택 도구 화면의 디자인이 변경되었습니다. 이제 즐겨 찾는 사이트를 고정하고, 최근 접근한 사이트를 확인하는 등의 작업이 가능합니다. 또한 사이트를 표시하고 숨기는 기능이 제거되었습니다. -(사이트라는 단어가 충분히 언급되었나요? 그런 것 같습니다.) diff --git a/fastlane/jetpack_metadata/android/ko-KR/changelogs/1430.txt b/fastlane/jetpack_metadata/android/ko-KR/changelogs/1430.txt new file mode 100644 index 000000000000..48187203de94 --- /dev/null +++ b/fastlane/jetpack_metadata/android/ko-KR/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8: +이제 통계 화면에서 오른쪽에서 왼쪽으로 읽는(RTL) 언어 사용자에게 백분율(%) 기호가 올바른 위치에 표시됩니다. + diff --git a/fastlane/jetpack_metadata/android/nl-NL/changelogs/1426.txt b/fastlane/jetpack_metadata/android/nl-NL/changelogs/1426.txt deleted file mode 100644 index 4b9d0056fc00..000000000000 --- a/fastlane/jetpack_metadata/android/nl-NL/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -We hebben het sitekiezer-scherm een nieuw ontwerp gegeven. Je kan nu je favoriete sites vastmaken, recent bekeken sites zien en meer. We hebben ook de mogelijkheid om sites te tonen en verbergen verwijderd. -(Hebben we vaak genoeg 'sites' gezegd? Wij denken van wel.) diff --git a/fastlane/jetpack_metadata/android/nl-NL/changelogs/1430.txt b/fastlane/jetpack_metadata/android/nl-NL/changelogs/1430.txt new file mode 100644 index 000000000000..60385848b6de --- /dev/null +++ b/fastlane/jetpack_metadata/android/nl-NL/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8 +Op het scherm Statistieken worden nu de percentages op de juiste plek weergegeven voor gebruikers die talen gebruiken die van rechts naar links gelezen worden. Recht zo die gaat. + diff --git a/fastlane/jetpack_metadata/android/pt-BR/changelogs/1426.txt b/fastlane/jetpack_metadata/android/pt-BR/changelogs/1426.txt deleted file mode 100644 index e8175a8c62a8..000000000000 --- a/fastlane/jetpack_metadata/android/pt-BR/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -A tela do seletor de sites está de cara nova. Agora você pode fixar seus sites favoritos, ver os recém-acessados e muito mais. Também removemos a capacidade de mostrar e ocultar sites. -(Acho que já deu da palavra "sites", não é mesmo?) diff --git a/fastlane/jetpack_metadata/android/pt-BR/changelogs/1430.txt b/fastlane/jetpack_metadata/android/pt-BR/changelogs/1430.txt new file mode 100644 index 000000000000..580d5dd41265 --- /dev/null +++ b/fastlane/jetpack_metadata/android/pt-BR/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8: +Na tela Estatísticas, as porcentagens agora aparecem na posição correta para usuários de idiomas escritos da direita para a esquerda. Pode acreditar. + diff --git a/fastlane/jetpack_metadata/android/ru-RU/changelogs/1426.txt b/fastlane/jetpack_metadata/android/ru-RU/changelogs/1426.txt deleted file mode 100644 index fead9bcc5e2a..000000000000 --- a/fastlane/jetpack_metadata/android/ru-RU/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -Мы обновили дизайн экрана выбора сайтов. Теперь вы можете закреплять любимые сайты, просматривать сайты, на которые недавно заходили, и многое другое. Кроме того, мы удалили функцию отображения и скрытия сайтов. -(Слово «сайты» не слишком часто повторяется? Наверное, достаточно.) diff --git a/fastlane/jetpack_metadata/android/ru-RU/changelogs/1430.txt b/fastlane/jetpack_metadata/android/ru-RU/changelogs/1430.txt new file mode 100644 index 000000000000..e2a48efb997f --- /dev/null +++ b/fastlane/jetpack_metadata/android/ru-RU/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8 +Теперь знак процента отображается в нужном месте при направлении письма справа налево. Именно там, где надо. + diff --git a/fastlane/jetpack_metadata/android/sv-SE/changelogs/1426.txt b/fastlane/jetpack_metadata/android/sv-SE/changelogs/1426.txt deleted file mode 100644 index 3b1c7fb90f22..000000000000 --- a/fastlane/jetpack_metadata/android/sv-SE/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -Vi har gjort om webbplatsväljarskärmen. Du kan nu nåla fast dina favoritwebbplatser, se nyligen besökta webbplatser och mycket mer. Vi har också tagit bort möjligheten att visa och dölja webbplatser. -(Har vi sagt ordet "webbplatser" tillräckligt? Troligtvis.) diff --git a/fastlane/jetpack_metadata/android/sv-SE/changelogs/1430.txt b/fastlane/jetpack_metadata/android/sv-SE/changelogs/1430.txt new file mode 100644 index 000000000000..f28fc5543ab6 --- /dev/null +++ b/fastlane/jetpack_metadata/android/sv-SE/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8: +På statistikskärmen visas nu procentsatserna i rätt position för användare som läser från höger till vänster. Precis. + diff --git a/fastlane/jetpack_metadata/android/tr-TR/changelogs/1426.txt b/fastlane/jetpack_metadata/android/tr-TR/changelogs/1426.txt deleted file mode 100644 index aada6c507ff0..000000000000 --- a/fastlane/jetpack_metadata/android/tr-TR/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -Site seçici ekranını yeniden tasarladık. Artık favori sitelerinizi sabitleyebilir, yakın zamanda eriştiğiniz siteleri görebilir ve daha fazlasını yapabilirsiniz. Ayrıca siteleri gösterebilme ve gizleyebilme olanağını kaldırdık. -("Siteler" kelimesinden yeterince bahsettik mi? Bizce bahsettik). diff --git a/fastlane/jetpack_metadata/android/tr-TR/changelogs/1430.txt b/fastlane/jetpack_metadata/android/tr-TR/changelogs/1430.txt new file mode 100644 index 000000000000..5e52073bc355 --- /dev/null +++ b/fastlane/jetpack_metadata/android/tr-TR/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8: +Sağdan sola yazılan dilleri kullanan kullanıcılar için İstatistikler ekranındaki yüzdeler artık doğru konumda görünüyor. Harika. + diff --git a/fastlane/jetpack_metadata/android/zh-CN/changelogs/1426.txt b/fastlane/jetpack_metadata/android/zh-CN/changelogs/1426.txt deleted file mode 100644 index 06ee9ae84b48..000000000000 --- a/fastlane/jetpack_metadata/android/zh-CN/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -我们已重新设计站点选择器屏幕。 您现在可以固定您最喜欢的站点,查看最新访问的站点,以及进行更多操作。 此外,我们还移除了显示和隐藏站点的功能。 -(“站点”一词我们说的够多了吗? 我们认为够多了。) diff --git a/fastlane/jetpack_metadata/android/zh-CN/changelogs/1430.txt b/fastlane/jetpack_metadata/android/zh-CN/changelogs/1430.txt new file mode 100644 index 000000000000..f5c3f64d69cf --- /dev/null +++ b/fastlane/jetpack_metadata/android/zh-CN/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8: +现在,在“统计信息”屏幕上,百分比会显示在正确位置,与从右到左语言用户的习惯相符。 完全符合。 + diff --git a/fastlane/jetpack_metadata/android/zh-TW/changelogs/1426.txt b/fastlane/jetpack_metadata/android/zh-TW/changelogs/1426.txt deleted file mode 100644 index 7d21cacd1702..000000000000 --- a/fastlane/jetpack_metadata/android/zh-TW/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -我們重新設計了網站選擇器畫面, 現在你可以釘選最常用的網站、查看近期存取過的網站,以及進行其他操作。 此外,我們移除了顯示和隱藏網站的功能。 -(「網站」這個詞是不是出現了好幾次? 這樣應該夠清楚了。) diff --git a/fastlane/jetpack_metadata/android/zh-TW/changelogs/1430.txt b/fastlane/jetpack_metadata/android/zh-TW/changelogs/1430.txt new file mode 100644 index 000000000000..15b20e8f08f5 --- /dev/null +++ b/fastlane/jetpack_metadata/android/zh-TW/changelogs/1430.txt @@ -0,0 +1,3 @@ +24.8: +「統計」畫面現在已為由右向左文字的語言使用者,顯示正確的百分比位置。 太好了。 + diff --git a/fastlane/metadata/android/ar/changelogs/1426.txt b/fastlane/metadata/android/ar/changelogs/1426.txt deleted file mode 100644 index c6467ac8d574..000000000000 --- a/fastlane/metadata/android/ar/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -أعدنا تصميم شاشة منتقي الموقع. يمكنك الآن تثبيت المواقع التي تفضلها، والاطلاع على المواقع التي تم الوصول إليها مؤخرًا، والمزيد. أزلنا أيضًا إمكانية إظهار المواقع وإخفائها. -(هل قلنا كلمة "مواقع" بما فيه الكفاية؟ نظن ذلك). diff --git a/fastlane/metadata/android/ar/changelogs/1430.txt b/fastlane/metadata/android/ar/changelogs/1430.txt new file mode 100644 index 000000000000..1f5036a96f82 --- /dev/null +++ b/fastlane/metadata/android/ar/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8: +تؤدي التغييرات في شهر إبريل إلى التحديثات في شهر مايو، لكنها للأسف لا تُقدِّم ملاحظات حول الإصدار. انتظر شهر يونيو لمعرفة التحديث المقبل. diff --git a/fastlane/metadata/android/de-DE/changelogs/1426.txt b/fastlane/metadata/android/de-DE/changelogs/1426.txt deleted file mode 100644 index ef81e21a247a..000000000000 --- a/fastlane/metadata/android/de-DE/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -Wir haben den Bildschirm für die Website-Auswahl neu gestaltet. Du kannst jetzt deine Lieblings-Websites anpinnen, die zuletzt aufgerufenen Websites sehen und mehr. Außerdem haben wir die Möglichkeit, Websites ein- und auszublenden, entfernt. -(Haben wir das Wort „Websites“ schon oft genug gesagt? Wir denken schon.) diff --git a/fastlane/metadata/android/de-DE/changelogs/1430.txt b/fastlane/metadata/android/de-DE/changelogs/1430.txt new file mode 100644 index 000000000000..deac764d3d31 --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8: +Der April macht die Blumen und der Mai hat den Dank dafür – aber er hat keine Versionshinweise. Das nächste Update gibt's im Juni. diff --git a/fastlane/metadata/android/en-US/changelogs/1426.txt b/fastlane/metadata/android/en-US/changelogs/1426.txt deleted file mode 100644 index 42b557a2dfb5..000000000000 --- a/fastlane/metadata/android/en-US/changelogs/1426.txt +++ /dev/null @@ -1,2 +0,0 @@ -We redesigned the site picker screen. You can now pin your favorite sites, see recently accessed sites, and more. We also removed the ability to show and hide sites. -(Did we say the word “sites” enough? We think so.) diff --git a/fastlane/metadata/android/en-US/changelogs/1430.txt b/fastlane/metadata/android/en-US/changelogs/1430.txt new file mode 100644 index 000000000000..68ef433035e3 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/1430.txt @@ -0,0 +1 @@ +April showers bring May flowers, but they unfortunately don’t bring release notes. Stay Juned for the next update. diff --git a/fastlane/metadata/android/es-ES/changelogs/1426.txt b/fastlane/metadata/android/es-ES/changelogs/1426.txt deleted file mode 100644 index b134bb742d50..000000000000 --- a/fastlane/metadata/android/es-ES/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -Hemos rediseñado la pantalla del selector de sitios. Ahora puedes fijar tus sitios favoritos, ver los últimos sitios a los que has accedido y muchas más funciones. También hemos eliminado la funcionalidad para mostrar y ocultar sitios. -(¿Hemos mencionado bastante la palabra "sitios"? Diría que sí). diff --git a/fastlane/metadata/android/es-ES/changelogs/1430.txt b/fastlane/metadata/android/es-ES/changelogs/1430.txt new file mode 100644 index 000000000000..f3265be3ae52 --- /dev/null +++ b/fastlane/metadata/android/es-ES/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8: +Las lluvias de abril nos traen flores en mayo, pero desafortunadamente no nos traen notas de la versión. Sigue con atención hasta la próxima actualización. diff --git a/fastlane/metadata/android/fr-CA/changelogs/1426.txt b/fastlane/metadata/android/fr-CA/changelogs/1426.txt deleted file mode 100644 index 897a91f6f6d3..000000000000 --- a/fastlane/metadata/android/fr-CA/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7 : -Nous avons repensé l’écran de sélection de site. Vous pouvez désormais épingler vos sites favoris, voir les sites récemment consultés et plus encore. Nous avons également supprimé la possibilité d’afficher et de masquer des sites. -(Si jamais vous ne l’aviez pas remarqué, il est question de sites.) diff --git a/fastlane/metadata/android/fr-CA/changelogs/1430.txt b/fastlane/metadata/android/fr-CA/changelogs/1430.txt new file mode 100644 index 000000000000..69f14348f8f7 --- /dev/null +++ b/fastlane/metadata/android/fr-CA/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8 : +En mai, fais ce qu’il te plaît… alors pas de notes de version ce mois-ci. On vous tient au juin pour la prochaine mise à jour. diff --git a/fastlane/metadata/android/fr-FR/changelogs/1426.txt b/fastlane/metadata/android/fr-FR/changelogs/1426.txt deleted file mode 100644 index 897a91f6f6d3..000000000000 --- a/fastlane/metadata/android/fr-FR/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7 : -Nous avons repensé l’écran de sélection de site. Vous pouvez désormais épingler vos sites favoris, voir les sites récemment consultés et plus encore. Nous avons également supprimé la possibilité d’afficher et de masquer des sites. -(Si jamais vous ne l’aviez pas remarqué, il est question de sites.) diff --git a/fastlane/metadata/android/fr-FR/changelogs/1430.txt b/fastlane/metadata/android/fr-FR/changelogs/1430.txt new file mode 100644 index 000000000000..69f14348f8f7 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8 : +En mai, fais ce qu’il te plaît… alors pas de notes de version ce mois-ci. On vous tient au juin pour la prochaine mise à jour. diff --git a/fastlane/metadata/android/id/changelogs/1426.txt b/fastlane/metadata/android/id/changelogs/1426.txt deleted file mode 100644 index c98856e036af..000000000000 --- a/fastlane/metadata/android/id/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -Kami mendesain ulang tampilan pemilih situs. Sekarang, Anda bisa menyematkan situs favorit, melihat situs yang baru-baru ini diakses, dan masih banyak lagi. Kami juga menghapus fitur untuk menampilkan dan menyembunyikan situs. -(Apa pembaruan kami terkait "situs" sudah lengkap? Kami rasa sudah.) diff --git a/fastlane/metadata/android/id/changelogs/1430.txt b/fastlane/metadata/android/id/changelogs/1430.txt new file mode 100644 index 000000000000..c2d476dd8b05 --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8: +Bulan April berganti Mei. Sayang, tiada catatan rilis yang mengiringi. Nantikan pembaruan berikutnya di bulan Juni. diff --git a/fastlane/metadata/android/it-IT/changelogs/1426.txt b/fastlane/metadata/android/it-IT/changelogs/1426.txt deleted file mode 100644 index 14bf70f751f2..000000000000 --- a/fastlane/metadata/android/it-IT/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -Abbiamo rinnovato la schermata della scelta siti. Ora puoi fissare i tuoi siti preferiti, vedere i siti consultati di recente e altro ancora. Abbiamo anche rimosso la possibilità di mostrare e nascondere i siti. -(Abbiamo usato abbastanza la parola "siti"? Pensiamo di sì.) diff --git a/fastlane/metadata/android/it-IT/changelogs/1430.txt b/fastlane/metadata/android/it-IT/changelogs/1430.txt new file mode 100644 index 000000000000..3e1563774bdd --- /dev/null +++ b/fastlane/metadata/android/it-IT/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8: +Le piogge di aprile portano i fiori di maggio, ma purtroppo non le note di rilascio. Rimanete sintonizzati per il prossimo aggiornamento a giugno. diff --git a/fastlane/metadata/android/iw-IL/changelogs/1426.txt b/fastlane/metadata/android/iw-IL/changelogs/1426.txt deleted file mode 100644 index f46943020b9a..000000000000 --- a/fastlane/metadata/android/iw-IL/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -עיצבנו מחדש את המסך של בוחר האתרים. כעת אפשר לנעוץ אתרים מועדפים, לראות אתרים שניגשת אליהם לאחרונה ועוד. בנוסף, הסרנו את היכולת להציג ולהסתיר אתרים. -(חזרנו יותר מדי על המילה "אתרים"? כן, אנחנו חושבים שכן.) diff --git a/fastlane/metadata/android/iw-IL/changelogs/1430.txt b/fastlane/metadata/android/iw-IL/changelogs/1430.txt new file mode 100644 index 000000000000..bd24ac36ed95 --- /dev/null +++ b/fastlane/metadata/android/iw-IL/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8: +סוף החורף מבשר את בוא האביב, אבל לא את בוא ההערות לשחרור גרסה חדשה. שווה לחכות לעדכון ביוני – הולך להיות חם. diff --git a/fastlane/metadata/android/ja-JP/changelogs/1426.txt b/fastlane/metadata/android/ja-JP/changelogs/1426.txt deleted file mode 100644 index 209dbcdaabaa..000000000000 --- a/fastlane/metadata/android/ja-JP/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -サイトピッカー画面を再設計しました。 お気に入りのサイトを固定したり、最近アクセスしたサイトを表示したりできるようになりました。 また、サイトを表示または非表示する機能が削除されました -(サイトと言いすぎましたか ? そうかもしれません)。 diff --git a/fastlane/metadata/android/ja-JP/changelogs/1430.txt b/fastlane/metadata/android/ja-JP/changelogs/1430.txt new file mode 100644 index 000000000000..050a568b7123 --- /dev/null +++ b/fastlane/metadata/android/ja-JP/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8: +4月のにわか雨は5月の花を咲かせますが、残念ながらリリースノートを咲かせることにはなりません。 今後のアップデートをお待ちください。 diff --git a/fastlane/metadata/android/ko-KR/changelogs/1426.txt b/fastlane/metadata/android/ko-KR/changelogs/1426.txt deleted file mode 100644 index eb60546818ac..000000000000 --- a/fastlane/metadata/android/ko-KR/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -사이트 선택 화면을 새롭게 디자인했습니다. 이제 즐겨찾는 사이트를 고정해 두고, 최근 접근한 사이트를 보는 등 더 많은 작업을 할 수 있습니다. 또한 사이트를 표시하거나 감추는 기능을 제거했습니다. -("사이트"라는 용어를 충분히 사용한 것 같죠?) diff --git a/fastlane/metadata/android/ko-KR/changelogs/1430.txt b/fastlane/metadata/android/ko-KR/changelogs/1430.txt new file mode 100644 index 000000000000..337bf2246843 --- /dev/null +++ b/fastlane/metadata/android/ko-KR/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8: +5월에는 새로운 릴리스가 없습니다. 6월의 업데이트를 기다려 주세요. diff --git a/fastlane/metadata/android/nl-NL/changelogs/1426.txt b/fastlane/metadata/android/nl-NL/changelogs/1426.txt deleted file mode 100644 index d8b12a5ba8bc..000000000000 --- a/fastlane/metadata/android/nl-NL/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -We hebben het scherm voor het kiezen van de site opnieuw ontworpen. Je kunt nu je favoriete sites vastzetten, recent bezochte sites bekijken en meer. We hebben ook de mogelijkheid om sites te tonen en te verbergen verwijderd. -(Hebben we het woord "sites" genoeg gezegd? Wij denken van wel.) diff --git a/fastlane/metadata/android/nl-NL/changelogs/1430.txt b/fastlane/metadata/android/nl-NL/changelogs/1430.txt new file mode 100644 index 000000000000..da98c5dda5bf --- /dev/null +++ b/fastlane/metadata/android/nl-NL/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8: +Buien in April brengen bloemen in mei, maar helaas brengen ze geen releaselog. Blijf op de hoogte voor de volgende update. diff --git a/fastlane/metadata/android/pl-PL/changelogs/1426.txt b/fastlane/metadata/android/pl-PL/changelogs/1426.txt deleted file mode 100644 index 5adb54c85118..000000000000 --- a/fastlane/metadata/android/pl-PL/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -Przeprojektowaliśmy ekran wyboru witryn. Możesz teraz przypinać ulubione witryny, wyświetlać ostatnio odwiedzane witryny i nie tylko. Usunęliśmy również możliwość pokazywania i ukrywania witryn. -(Czy wystarczająco dużo razy użyliśmy słowa "witryny"? Wydaje nam się, że tak). diff --git a/fastlane/metadata/android/ru-RU/changelogs/1426.txt b/fastlane/metadata/android/ru-RU/changelogs/1426.txt deleted file mode 100644 index fead9bcc5e2a..000000000000 --- a/fastlane/metadata/android/ru-RU/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -Мы обновили дизайн экрана выбора сайтов. Теперь вы можете закреплять любимые сайты, просматривать сайты, на которые недавно заходили, и многое другое. Кроме того, мы удалили функцию отображения и скрытия сайтов. -(Слово «сайты» не слишком часто повторяется? Наверное, достаточно.) diff --git a/fastlane/metadata/android/ru-RU/changelogs/1430.txt b/fastlane/metadata/android/ru-RU/changelogs/1430.txt new file mode 100644 index 000000000000..7919af1c919c --- /dev/null +++ b/fastlane/metadata/android/ru-RU/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8 +Дождь в апреле — цветы в мае, только вот, увы, пока никаких обновлений. Ждём июня! diff --git a/fastlane/metadata/android/sv-SE/changelogs/1426.txt b/fastlane/metadata/android/sv-SE/changelogs/1426.txt deleted file mode 100644 index 3b1c7fb90f22..000000000000 --- a/fastlane/metadata/android/sv-SE/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -Vi har gjort om webbplatsväljarskärmen. Du kan nu nåla fast dina favoritwebbplatser, se nyligen besökta webbplatser och mycket mer. Vi har också tagit bort möjligheten att visa och dölja webbplatser. -(Har vi sagt ordet "webbplatser" tillräckligt? Troligtvis.) diff --git a/fastlane/metadata/android/sv-SE/changelogs/1430.txt b/fastlane/metadata/android/sv-SE/changelogs/1430.txt new file mode 100644 index 000000000000..7cb1cc796132 --- /dev/null +++ b/fastlane/metadata/android/sv-SE/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8: +Aprilregn ger majblommor, men dessvärre inte lanseringsmeddelanden. Håll utkik efter nästa uppdatering i juni. diff --git a/fastlane/metadata/android/tr-TR/changelogs/1426.txt b/fastlane/metadata/android/tr-TR/changelogs/1426.txt deleted file mode 100644 index aada6c507ff0..000000000000 --- a/fastlane/metadata/android/tr-TR/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -Site seçici ekranını yeniden tasarladık. Artık favori sitelerinizi sabitleyebilir, yakın zamanda eriştiğiniz siteleri görebilir ve daha fazlasını yapabilirsiniz. Ayrıca siteleri gösterebilme ve gizleyebilme olanağını kaldırdık. -("Siteler" kelimesinden yeterince bahsettik mi? Bizce bahsettik). diff --git a/fastlane/metadata/android/tr-TR/changelogs/1430.txt b/fastlane/metadata/android/tr-TR/changelogs/1430.txt new file mode 100644 index 000000000000..2d44af46b081 --- /dev/null +++ b/fastlane/metadata/android/tr-TR/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8: +Nisan yağmurları mayısta çiçeklerin açmasını sağlar, ancak sürüm notlarının getirilmesini sağlayamaz. Bir sonraki güncellemeyi heyecanla bekleyin. diff --git a/fastlane/metadata/android/zh-CN/changelogs/1426.txt b/fastlane/metadata/android/zh-CN/changelogs/1426.txt deleted file mode 100644 index 06ee9ae84b48..000000000000 --- a/fastlane/metadata/android/zh-CN/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -我们已重新设计站点选择器屏幕。 您现在可以固定您最喜欢的站点,查看最新访问的站点,以及进行更多操作。 此外,我们还移除了显示和隐藏站点的功能。 -(“站点”一词我们说的够多了吗? 我们认为够多了。) diff --git a/fastlane/metadata/android/zh-CN/changelogs/1430.txt b/fastlane/metadata/android/zh-CN/changelogs/1430.txt new file mode 100644 index 000000000000..2c6f58c6de9c --- /dev/null +++ b/fastlane/metadata/android/zh-CN/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8: +四月的阵雨会带来五月的鲜花,但很遗憾,它们不会带来版本说明。 敬请期待下一次更新。 diff --git a/fastlane/metadata/android/zh-TW/changelogs/1426.txt b/fastlane/metadata/android/zh-TW/changelogs/1426.txt deleted file mode 100644 index 7d21cacd1702..000000000000 --- a/fastlane/metadata/android/zh-TW/changelogs/1426.txt +++ /dev/null @@ -1,3 +0,0 @@ -24.7: -我們重新設計了網站選擇器畫面, 現在你可以釘選最常用的網站、查看近期存取過的網站,以及進行其他操作。 此外,我們移除了顯示和隱藏網站的功能。 -(「網站」這個詞是不是出現了好幾次? 這樣應該夠清楚了。) diff --git a/fastlane/metadata/android/zh-TW/changelogs/1430.txt b/fastlane/metadata/android/zh-TW/changelogs/1430.txt new file mode 100644 index 000000000000..8993cc59005e --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/1430.txt @@ -0,0 +1,2 @@ +24.8: +四月細雨帶來五月花開,卻沒有帶來版本資訊。 六月敬請關注下次更新。 diff --git a/fastlane/resources/values/strings.xml b/fastlane/resources/values/strings.xml index 5deeefd7a248..60f8764c813a 100644 --- a/fastlane/resources/values/strings.xml +++ b/fastlane/resources/values/strings.xml @@ -993,8 +993,8 @@ Manage Insights WordPress.com Email - Total %1$s Followers: %2$s - Follower + Total %1$s Subscribers: %2$s + Subscriber Since Authors Posts and pages @@ -1007,7 +1007,7 @@ Views %1$s | %2$s Service - Followers + Subscribers %1$s - %2$s Stats item settings We cannot open the statistics at the moment. Please try again later @@ -1025,6 +1025,11 @@ Site timezone (UTC + %s) Site timezone (UTC - %s) File download stats were not recorded before June 28th 2019. + Name + Subscriber since + Latest emails + Opens + Clicks -%s @@ -1038,13 +1043,14 @@ Loading selected card data %1$s %2$s for period: %3$s, change from previous period - %4$s - %1$s, %2$s%% of total followers + %1$s, %2$s%% of total subscribers Graph updated. Expand Collapse Item expanded Item collapsed %1$s: %2$s, %3$s: %4$s + %1$s: %2$s, %3$s: %4$s, %5$s: %6$s %1$s, %2$d %3$s   & %1$d %2$s @@ -1317,14 +1323,15 @@ @string/comments Search Terms Jetpack Social Connections - Followers - Follower Totals + Subscriber Totals Total Likes Total Comments - Total Followers + Total Subscribers + Subscribers + Subscribers + Emails - Subscribers seconds ago a minute ago %1$d minutes @@ -1400,7 +1407,6 @@ Tumblr Google+ LinkedIn - Social Path %1$s of views Not enough activity. Check back later when your site\'s had more visitors! @@ -1414,7 +1420,7 @@ ⭐️ Your latest post %1$s has received %2$s like. ⭐️ Your latest post %1$s has received %2$s likes. 💡Tap \'VIEW MORE\' to see your top commenters. - 💡Commenting on other blogs is a great way to build attention and followers for your new site. + 💡Commenting on other blogs is a great way to build attention and subscribers for your new site. Please log in to the WordPress app to add a widget. @@ -1501,7 +1507,7 @@ No notifications…yet. You\'re all up to date! No comments yet - No followers yet + No subscribers yet No likes yet Get active! Comment on posts from blogs you follow. Reignite the conversation: write a new post @@ -1516,13 +1522,13 @@ Could not open notification Ignore The request has expired. Log in to WordPress.com to try again. - Follows + Subscribers New notifications Tap to show them All Unread Comments - Follows + Subscribers Likes Unread @@ -1599,14 +1605,14 @@ Comments on my site Likes on my comments Likes on my posts - Site follows + Site subscriptions Site achievements Username mentions @string/comments_on_my_site @string/likes_on_my_comments @string/likes_on_my_posts - @string/site_follows + @string/site_subscriptions @string/site_achievements @string/username_mentions @@ -1788,6 +1794,8 @@ Date and Time Notification Add to calendar + No app found to handle the request to add to calendar + Unable to add to calendar Social Sharing Increase your traffic by auto-sharing your posts with your friends on social media. Connect accounts @@ -2064,12 +2072,12 @@ Couldn\'t save site info Couldn\'t retrieve site users Couldn\'t retrieve authors - Couldn\'t retrieve site followers - Couldn\'t retrieve site email followers + Couldn\'t retrieve site subscribers + Couldn\'t retrieve site email subscribers Couldn\'t retrieve site viewers Couldn\'t update user role Couldn\'t remove user - Couldn\'t remove follower + Couldn\'t remove subscriber Couldn\'t remove viewer Could not like comment. Please try again later. Could not approve comment. Please try again later. @@ -2079,6 +2087,7 @@ Unable to load this page right now. Check your network connection and try again. Couldn\'t update site title. Check your network connection and try again. + No camera app available. Could not find the post on the server @@ -2638,7 +2647,7 @@ Since %1$s Remove %1$s If you remove %1$s, that user will no longer be able to access this site, but any content that was created by %1$s will remain on the site.\n\nWould you still like to remove this user? - If removed, this follower will stop receiving notifications about this site, unless they re-follow.\n\nWould you still like to remove this follower? + If removed, this subscriber will stop receiving notifications about this site, unless they re-subscribe.\n\nWould you still like to remove this subscriber? If you remove this viewer, he or she will not be able to visit this site.\n\nWould you still like to remove this viewer? Successfully removed %1$s Invite People @@ -2647,7 +2656,7 @@ @string/invite %s: User not found %s: Already a member - %s: Already following + %s: Already subscribed %s: User blocked invites %s: Invalid email Custom message @@ -2664,18 +2673,18 @@ Optional: enter a custom message to be sent with your invitation. Team - Followers - Email Followers + Subscribers + Email Subscribers Viewers No users yet - No followers yet - No email followers yet + No subscribers yet + No email subscribers yet No viewers yet Fetching users… - Follower - Email Follower + Subscriber + Email Subscriber - Follower + Subscriber Viewer Invite Link diff --git a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java index 936737aeb994..cbacf9890529 100644 --- a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java +++ b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java @@ -1122,7 +1122,12 @@ public enum Stat { RESOLVE_AUTOSAVE_CONFLICT_CONFIRM_TAPPED, RESOLVE_AUTOSAVE_CONFLICT_CANCEL_TAPPED, RESOLVE_AUTOSAVE_CONFLICT_CLOSE_TAPPED, - RESOLVE_AUTOSAVE_CONFLICT_DISMISSED; + RESOLVE_AUTOSAVE_CONFLICT_DISMISSED, + IN_APP_UPDATE_SHOWN, + IN_APP_UPDATE_DISMISSED, + IN_APP_UPDATE_ACCEPTED, + IN_APP_UPDATE_COMPLETED_WITH_APP_RESTART; + /* * Please set the event name in the enum only if the new Stat's name in lower case does not match it. * In that case you also need to add the event in the `AnalyticsTrackerNosaraTest.specialNames` map. diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index d4c8c63b0770..c8dc71940370 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -694,7 +694,8 @@ private ArrayList initOtherMediaImageOptions() { Bundle arguments = getArguments(); FragmentActivity activity = getActivity(); - if (activity == null || arguments == null) { + final Context context = getContext(); + if (activity == null || context == null || arguments == null) { AppLog.e(T.EDITOR, "Failed to initialize other media options because the activity or getArguments() is null"); return otherMediaOptions; @@ -710,13 +711,13 @@ private ArrayList initOtherMediaImageOptions() { String packageName = activity.getApplication().getPackageName(); if (supportStockPhotos) { int stockMediaResourceId = - getResources().getIdentifier("photo_picker_stock_media", "string", packageName); + context.getResources().getIdentifier("photo_picker_stock_media", "string", packageName); otherMediaOptions.add(new MediaOption(MEDIA_SOURCE_STOCK_MEDIA, getString(stockMediaResourceId))); } if (supportsTenor) { int gifMediaResourceId = - getResources().getIdentifier("photo_picker_gif", "string", packageName); + context.getResources().getIdentifier("photo_picker_gif", "string", packageName); otherMediaOptions.add(new MediaOption(GIF_MEDIA, getString(gifMediaResourceId))); } @@ -900,6 +901,10 @@ public void onClick(DialogInterface dialog, int id) { @UiThread public void showFeaturedImageConfirmationDialog(final int mediaId) { + if (isStateSaved()) { + return; + } + GutenbergDialogFragment dialog = new GutenbergDialogFragment(); dialog.initialize( TAG_REPLACE_FEATURED_DIALOG, diff --git a/version.properties b/version.properties index ded165375c84..0e9ac9f04ba4 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ -versionName=24.8-rc-3 -versionCode=1429 \ No newline at end of file +versionName=24.9-rc-1 +versionCode=1431