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/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 6472b694a351..dd34f91e605d 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -5,6 +5,7 @@ * [*] 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] * [*] 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] 24.9 ----- 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/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/build.gradle b/build.gradle index 9b774d3f6deb..1047ec8f8322 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ ext { automatticAboutVersion = '1.4.0' automatticRestVersion = '1.0.8' automatticTracksVersion = '5.0.0' - gutenbergMobileVersion = 'v1.118.0' + gutenbergMobileVersion = 'v1.119.0-alpha1' wordPressAztecVersion = 'v2.1.3' wordPressFluxCVersion = '2.79.0' wordPressLoginVersion = '1.15.0' 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 279ed0d5db49..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))); }