diff --git a/WordPress/src/debug/java/org/wordpress/android/WordPressDebug.java b/WordPress/src/debug/java/org/wordpress/android/WordPressDebug.java deleted file mode 100644 index 3d8d460b0530..000000000000 --- a/WordPress/src/debug/java/org/wordpress/android/WordPressDebug.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.wordpress.android; - -import android.os.StrictMode; - -import org.wordpress.android.util.AppLog; -import org.wordpress.android.util.AppLog.T; - -import dagger.hilt.android.HiltAndroidApp; - -@HiltAndroidApp -public class WordPressDebug extends WordPressApp { - @Override - public void onCreate() { - super.onCreate(); - - // enableStrictMode() - } - - /** - * enables "strict mode" for testing - should NEVER be used in release builds - */ - private void enableStrictMode() { - // return if the build is not a debug build - if (!BuildConfig.DEBUG) { - AppLog.e(T.UTILS, "You should not call enableStrictMode() on a non debug build"); - return; - } - - StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() - .detectDiskReads() - .detectDiskWrites() - .detectNetwork() - .penaltyLog() - .penaltyFlashScreen() - .build()); - - StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() - .detectActivityLeaks() - .detectLeakedSqlLiteObjects() - .detectLeakedClosableObjects() - .detectLeakedRegistrationObjects() // <-- requires Jelly Bean - .penaltyLog() - .build()); - - AppLog.w(T.UTILS, "Strict mode enabled"); - } -} diff --git a/WordPress/src/debug/java/org/wordpress/android/WordPressDebug.kt b/WordPress/src/debug/java/org/wordpress/android/WordPressDebug.kt new file mode 100644 index 000000000000..c42bfcb6b9a4 --- /dev/null +++ b/WordPress/src/debug/java/org/wordpress/android/WordPressDebug.kt @@ -0,0 +1,57 @@ +package org.wordpress.android + +import android.os.Build +import android.os.StrictMode +import android.os.StrictMode.ThreadPolicy +import android.os.StrictMode.VmPolicy +import dagger.hilt.android.HiltAndroidApp +import org.wordpress.android.util.AppLog + +@HiltAndroidApp +class WordPressDebug : WordPressApp() { + override fun onCreate() { + super.onCreate() + // enableStrictMode() + } + + /** + * enables "strict mode" for testing - should NEVER be used in release builds + */ + private fun enableStrictMode() { + // return if the build is not a debug build + if (!BuildConfig.DEBUG) { + AppLog.e(AppLog.T.UTILS, "You should not call enableStrictMode() on a non debug build") + return + } + + StrictMode.setThreadPolicy( + ThreadPolicy.Builder() + .detectDiskReads() + .detectDiskWrites() + .detectNetwork() + .penaltyLog() + .penaltyFlashScreen() + .build() + ) + + StrictMode.setVmPolicy( + VmPolicy.Builder() + .detectActivityLeaks() + .detectLeakedSqlLiteObjects() + .detectLeakedClosableObjects() + .detectLeakedRegistrationObjects() + .penaltyLog() + .apply { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + detectNonSdkApiUsage() + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + detectUnsafeIntentLaunch() + } + } + .build() + ) + + AppLog.w(AppLog.T.UTILS, "Strict mode enabled") + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt b/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt index a5af3e64975a..6e2361fc19a8 100644 --- a/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt +++ b/WordPress/src/main/java/org/wordpress/android/AppInitializer.kt @@ -952,6 +952,7 @@ class AppInitializer @Inject constructor( // Do nothing } + @Deprecated("Deprecated in Java") override fun onLowMemory() { // Do nothing } diff --git a/WordPress/src/main/java/org/wordpress/android/support/SupportWebViewActivity.kt b/WordPress/src/main/java/org/wordpress/android/support/SupportWebViewActivity.kt index 54775236ae88..a4a565e19e2b 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/SupportWebViewActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/SupportWebViewActivity.kt @@ -27,6 +27,7 @@ import org.wordpress.android.ui.WPWebViewActivity import org.wordpress.android.ui.accounts.HelpActivity import org.wordpress.android.util.ToastUtils import org.wordpress.android.util.extensions.getSerializableCompat +import org.wordpress.android.util.extensions.setWindowNavigationBarColor import org.wordpress.android.widgets.WPSnackbar import java.util.UUID import javax.inject.Inject @@ -55,7 +56,7 @@ class SupportWebViewActivity : WPWebViewActivity(), SupportWebViewClient.Support supportActionBar?.title = getString(R.string.help) supportActionBar?.subtitle = "" - window.navigationBarColor = getColor(R.color.docsbot_chat_container) + window.setWindowNavigationBarColor(getColor(R.color.docsbot_chat_container)) // Prevent AppBar scrolling away val toolbar = findViewById(R.id.toolbar) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupEpilogueFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupEpilogueFragment.kt index db4275a1e781..440314539931 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupEpilogueFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupEpilogueFragment.kt @@ -671,7 +671,9 @@ class SignupEpilogueFragment : LoginBaseFormFragment(), if (newAvatarUploaded && resource is BitmapDrawable) { var bitmap = resource.bitmap // create a copy since the original bitmap may by automatically recycled - bitmap = bitmap.copy(bitmap.config, true) + bitmap.config?.let { config -> + bitmap = bitmap.copy(config, true) + } getBitmapCache().put((avatarUrl), bitmap) } } 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 0b8ad6687560..71333b178927 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 @@ -556,7 +556,9 @@ class MeFragment : Fragment(R.layout.me_fragment), OnScrollToTopListener { if (newAvatarSelected && resource is BitmapDrawable) { var bitmap = resource.bitmap // create a copy since the original bitmap may by automatically recycled - bitmap = bitmap.copy(bitmap.config, true) + bitmap.config?.let { config -> + bitmap = bitmap.copy(config, true) + } WordPress.getBitmapCache().put( avatarUrl, bitmap diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainNavigationView.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainNavigationView.kt index 9281b67bf82f..5add1ebb9a5f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainNavigationView.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainNavigationView.kt @@ -188,7 +188,9 @@ class WPMainNavigationView @JvmOverloads constructor( if (resource is BitmapDrawable) { var bitmap = resource.bitmap // create a copy since the original bitmap may by automatically recycled - bitmap = bitmap.copy(bitmap.config, true) + bitmap.config?.let { config -> + bitmap = bitmap.copy(config, true) + } WordPress.getBitmapCache().put( avatarUrl, bitmap diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteFragment.kt index ef8c9647d3ef..30cd1a647adc 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/MySiteFragment.kt @@ -828,7 +828,11 @@ class MySiteFragment : Fragment(R.layout.my_site_fragment), val context = activity ?: return val options = UCrop.Options() options.setShowCropGrid(false) - options.setStatusBarColor(context.getColorFromAttribute(android.R.attr.statusBarColor)) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { + options.setStatusBarColor(com.google.android.material.R.attr.colorSurface) + } else { + options.setStatusBarColor(com.google.android.material.R.attr.colorOnSurface) + } options.setToolbarColor(context.getColorFromAttribute(R.attr.wpColorAppBar)) options.setToolbarWidgetColor(context.getColorFromAttribute(com.google.android.material.R.attr.colorOnSurface)) options.setAllowedGestures(UCropActivity.SCALE, UCropActivity.NONE, UCropActivity.NONE) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/quicklinksitem/QuickLinksItemViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/quicklinksitem/QuickLinksItemViewModelSlice.kt index ffdd2c1641c0..a27355922ceb 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/quicklinksitem/QuickLinksItemViewModelSlice.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/quicklinksitem/QuickLinksItemViewModelSlice.kt @@ -231,7 +231,9 @@ class QuickLinksItemViewModelSlice @Inject constructor( this@QuickLinksItemViewModelSlice::onMoreClick ) ) - quickLinkItems.removeLast() + if (quickLinkItems.isNotEmpty()) { + quickLinkItems.removeAt(quickLinkItems.lastIndex) + } quickLinkItems.add(lastItem) quickLinks.copy(quickLinkItems = quickLinkItems, showMoreFocusPoint = true) } else { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/items/listitem/MySiteListItemViewHolder.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/items/listitem/MySiteListItemViewHolder.kt index a27315b01d19..1deaf5c7b791 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/items/listitem/MySiteListItemViewHolder.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/items/listitem/MySiteListItemViewHolder.kt @@ -73,7 +73,9 @@ class MySiteListItemViewHolder( if (resource is BitmapDrawable) { var bitmap = resource.bitmap // create a copy since the original bitmap may by automatically recycled - bitmap = bitmap.copy(bitmap.config, true) + bitmap.config?.let { config -> + bitmap = bitmap.copy(config, true) + } WordPress.getBitmapCache().put( avatarUrl, bitmap diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/utils/WidgetUtils.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/utils/WidgetUtils.kt index ae336e122ccc..66d8017fe044 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/utils/WidgetUtils.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/widget/utils/WidgetUtils.kt @@ -144,7 +144,7 @@ class WidgetUtils ) } - @Suppress("LongParameterList") + @Suppress("LongParameterList", "DEPRECATION") fun showList( appWidgetManager: AppWidgetManager, views: RemoteViews, diff --git a/WordPress/src/main/java/org/wordpress/android/util/WPActivityUtils.java b/WordPress/src/main/java/org/wordpress/android/util/WPActivityUtils.java index d5acabcdc659..4aae2ad19ca0 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/WPActivityUtils.java +++ b/WordPress/src/main/java/org/wordpress/android/util/WPActivityUtils.java @@ -11,7 +11,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; -import android.view.Window; import android.widget.ListView; import androidx.annotation.NonNull; @@ -25,7 +24,6 @@ import org.wordpress.android.R; import org.wordpress.android.util.AppLog.T; import org.wordpress.android.util.extensions.DialogExtensionsKt; -import org.wordpress.android.util.extensions.WindowExtensionsKt; import java.util.ArrayList; import java.util.List; @@ -182,30 +180,4 @@ public static void enableReaderDeeplinks(Context context) { pm.setComponentEnabledSetting(new ComponentName(context, READER_DEEPLINK_ACTIVITY_ALIAS), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); } - - /** - * @deprecated Use {@link WindowExtensionsKt} instead. - */ - @Deprecated - public static void setLightStatusBar(Window window, boolean showInLightMode) { - WindowExtensionsKt.setLightStatusBar(window, showInLightMode); - } - - /** - * @deprecated Use {@link WindowExtensionsKt} instead. - */ - @Deprecated - public static void setLightNavigationBar(Window window, boolean showInLightMode) { - WindowExtensionsKt.setLightNavigationBar(window, showInLightMode, true); - } - - /** - * @deprecated Use {@link WindowExtensionsKt} instead. - */ - @Deprecated - public static void showFullScreen(View decorView) { - int flags = decorView.getSystemUiVisibility(); - flags = flags | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; - decorView.setSystemUiVisibility(flags); - } } diff --git a/WordPress/src/main/java/org/wordpress/android/util/extensions/WindowExtensions.kt b/WordPress/src/main/java/org/wordpress/android/util/extensions/WindowExtensions.kt index fc2b01af23a0..825b9493d6cd 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/extensions/WindowExtensions.kt +++ b/WordPress/src/main/java/org/wordpress/android/util/extensions/WindowExtensions.kt @@ -1,78 +1,59 @@ package org.wordpress.android.util.extensions -import android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN -import android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE -import android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR -import android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR +import android.os.Build import android.view.Window -import androidx.core.content.ContextCompat +import android.view.WindowInsets import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsControllerCompat import org.wordpress.android.util.ColorUtils -import android.R as AndroidR -import com.google.android.material.R as MaterialR - -@Suppress("DEPRECATION") -fun Window.setLightStatusBar(showInLightMode: Boolean) { - if (isLightTheme()) { - decorView.systemUiVisibility = decorView.systemUiVisibility.let { - if (showInLightMode) { - it or SYSTEM_UI_FLAG_LIGHT_STATUS_BAR - } else { - it and SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() - } - } - } -} - -@Suppress("DEPRECATION") -fun Window.setLightNavigationBar(showInLightMode: Boolean, applyDefaultColors: Boolean = false) { - if (isLightTheme()) { - decorView.systemUiVisibility = decorView.systemUiVisibility.let { - if (showInLightMode) { - it or SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR - } else { - it and SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv() - } - } - if (applyDefaultColors) { - navigationBarColor = if (showInLightMode) { - context.getColorFromAttribute(MaterialR.attr.colorSurface) - } else { - ContextCompat.getColor(context, AndroidR.color.black) - } - } - } -} fun Window.setEdgeToEdgeContentDisplay(isEnabled: Boolean) { val decorFitsSystemWindows = !isEnabled WindowCompat.setDecorFitsSystemWindows(this, decorFitsSystemWindows) } -@Suppress("DEPRECATION") -fun Window.showFullScreen() { - decorView.systemUiVisibility = decorView.systemUiVisibility.let { - it or SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or SYSTEM_UI_FLAG_LAYOUT_STABLE - } -} - fun Window.setWindowStatusBarColor(color: Int) { - val windowInsetsController = WindowInsetsControllerCompat(this, decorView) - - statusBarColor = color - windowInsetsController.isAppearanceLightStatusBars = ColorUtils.isColorLight(statusBarColor) - - // we need to set the light navigation appearance here because, for some reason, changing the status bar also - // changes the navigation bar appearance but this method is supposed to only change the status bar - windowInsetsController.isAppearanceLightNavigationBars = ColorUtils.isColorLight(navigationBarColor) + setWindowBarColor(color, InsetsType.STATUS_BAR) } fun Window.setWindowNavigationBarColor(color: Int) { - val windowInsetsController = WindowInsetsControllerCompat(this, decorView) + setWindowBarColor(color, InsetsType.NAVIGATION_BAR) +} - navigationBarColor = color - windowInsetsController.isAppearanceLightNavigationBars = ColorUtils.isColorLight(navigationBarColor) +/** + * Sets the status bar or navigation bar color + * TODO Setting both the status bar color and navigation bar color causes the insets to be set twice + */ +@Suppress("DEPRECATION") +private fun Window.setWindowBarColor(color: Int, insetsType: InsetsType) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { + // Android 15+ + decorView.setOnApplyWindowInsetsListener { view, insets -> + view.setBackgroundColor(color) + val barInsets = insets.getInsets( + when (insetsType) { + InsetsType.STATUS_BAR -> WindowInsets.Type.statusBars() + InsetsType.NAVIGATION_BAR -> WindowInsets.Type.navigationBars() + } + ) + // Adjust padding to avoid overlap + view.setPadding(0, barInsets.top, 0, 0) + insets + } + } else { + when(insetsType) { + InsetsType.STATUS_BAR -> statusBarColor = color + InsetsType.NAVIGATION_BAR -> navigationBarColor = color + } + val windowInsetsController = WindowInsetsControllerCompat(this, decorView) + if (insetsType == InsetsType.STATUS_BAR) { + windowInsetsController.isAppearanceLightStatusBars = ColorUtils.isColorLight(statusBarColor) + } + windowInsetsController.isAppearanceLightNavigationBars = ColorUtils.isColorLight(navigationBarColor) + } } -private fun Window.isLightTheme() = !context.resources.configuration.isDarkTheme() +private enum class InsetsType { + STATUS_BAR, + NAVIGATION_BAR +} diff --git a/WordPress/src/main/java/org/wordpress/android/util/signature/SignatureUtils.kt b/WordPress/src/main/java/org/wordpress/android/util/signature/SignatureUtils.kt index 2095bf4aedc8..a65ea89ab0c8 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/signature/SignatureUtils.kt +++ b/WordPress/src/main/java/org/wordpress/android/util/signature/SignatureUtils.kt @@ -33,14 +33,14 @@ class SignatureUtils @Inject constructor( contextProvider.getContext().packageManager.getPackageInfoCompat( trustedPackageId, PackageManager.GET_SIGNING_CERTIFICATES - ) - ).signingInfo + )?.signingInfo + ) if (signingInfo.hasMultipleSigners()) { throw SignatureNotFoundException() } - val allSignaturesMatch = signingInfo.signingCertificateHistory.all { + val allSignaturesMatch = signingInfo.signingCertificateHistory?.all { toHexStringWithColons(messageDigest.digest(it.toByteArray())) == trustedSignatureHash - } + } ?: false if (allSignaturesMatch) { true } else throw SignatureNotFoundException() @@ -54,8 +54,10 @@ class SignatureUtils @Inject constructor( trustedSignatureHash: String ): Boolean { try { - val signatures = contextProvider.getContext().packageManager - .getPackageInfo(trustedPackageId, PackageManager.GET_SIGNATURES).signatures + val signatures = requireNotNull( + contextProvider.getContext().packageManager + .getPackageInfo(trustedPackageId, PackageManager.GET_SIGNATURES).signatures + ) val allSignaturesMatch = signatures.all { toHexStringWithColons(messageDigest.digest(it.toByteArray())) == trustedSignatureHash } diff --git a/build.gradle b/build.gradle index 3074f2a867dc..c9089dbf0c7b 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,7 @@ plugins { ext { minSdkVersion = 26 - compileSdkVersion = 34 + compileSdkVersion = 35 targetSdkVersion = 34 secretProperties = loadSecrets( diff --git a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/comment/CommentSqlUtilsTest.kt b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/comment/CommentSqlUtilsTest.kt index 3bbe93c21ab9..f7de0d5a2bca 100644 --- a/libs/fluxc/src/test/java/org/wordpress/android/fluxc/comment/CommentSqlUtilsTest.kt +++ b/libs/fluxc/src/test/java/org/wordpress/android/fluxc/comment/CommentSqlUtilsTest.kt @@ -179,9 +179,9 @@ class CommentSqlUtilsTest { val commentsInDb = generateCommentModels(60, ALL) val remoteComments = generateCommentModels(30, ALL) - remoteComments.removeLast() - remoteComments.removeLast() - remoteComments.removeLast() + remoteComments.removeAt(remoteComments.lastIndex) + remoteComments.removeAt(remoteComments.lastIndex) + remoteComments.removeAt(remoteComments.lastIndex) commentsInDb.forEach { CommentSqlUtils.insertOrUpdateComment(it) @@ -239,9 +239,9 @@ class CommentSqlUtilsTest { val remoteComments = generateCommentModels(50, ALL) // exclude first 3 comments - remoteComments.removeFirst() - remoteComments.removeFirst() - remoteComments.removeFirst() + remoteComments.removeAt(0) + remoteComments.removeAt(0) + remoteComments.removeAt(0) commentsInDb.forEach { CommentSqlUtils.insertOrUpdateComment(it)