* For #4474: Adds what's new button to home screen menu * For #4474: Adds tests for what's new buttonmaster
parent
46b09395f8
commit
09dcdb079d
|
@ -20,5 +20,6 @@ enum class BrowserDirection(@IdRes val fragmentId: Int) {
|
||||||
FromSettings(R.id.settingsFragment),
|
FromSettings(R.id.settingsFragment),
|
||||||
FromBookmarks(R.id.bookmarkFragment),
|
FromBookmarks(R.id.bookmarkFragment),
|
||||||
FromHistory(R.id.historyFragment),
|
FromHistory(R.id.historyFragment),
|
||||||
FromExceptions(R.id.exceptionsFragment)
|
FromExceptions(R.id.exceptionsFragment),
|
||||||
|
FromAbout(R.id.aboutFragment)
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ import org.mozilla.fenix.home.intent.StartSearchIntentProcessor
|
||||||
import org.mozilla.fenix.library.bookmarks.BookmarkFragmentDirections
|
import org.mozilla.fenix.library.bookmarks.BookmarkFragmentDirections
|
||||||
import org.mozilla.fenix.library.history.HistoryFragmentDirections
|
import org.mozilla.fenix.library.history.HistoryFragmentDirections
|
||||||
import org.mozilla.fenix.search.SearchFragmentDirections
|
import org.mozilla.fenix.search.SearchFragmentDirections
|
||||||
|
import org.mozilla.fenix.settings.AboutFragmentDirections
|
||||||
import org.mozilla.fenix.settings.SettingsFragmentDirections
|
import org.mozilla.fenix.settings.SettingsFragmentDirections
|
||||||
import org.mozilla.fenix.share.ShareFragment
|
import org.mozilla.fenix.share.ShareFragment
|
||||||
import org.mozilla.fenix.theme.DefaultThemeManager
|
import org.mozilla.fenix.theme.DefaultThemeManager
|
||||||
|
@ -233,6 +234,8 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback
|
||||||
ExceptionsFragmentDirections.actionExceptionsFragmentToBrowserFragment(
|
ExceptionsFragmentDirections.actionExceptionsFragmentToBrowserFragment(
|
||||||
customTabSessionId
|
customTabSessionId
|
||||||
)
|
)
|
||||||
|
BrowserDirection.FromAbout ->
|
||||||
|
AboutFragmentDirections.actionAboutFragmentToBrowserFragment(customTabSessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun load(
|
private fun load(
|
||||||
|
|
|
@ -84,6 +84,7 @@ import org.mozilla.fenix.share.ShareTab
|
||||||
import org.mozilla.fenix.utils.FragmentPreDrawManager
|
import org.mozilla.fenix.utils.FragmentPreDrawManager
|
||||||
import org.mozilla.fenix.utils.Settings
|
import org.mozilla.fenix.utils.Settings
|
||||||
import org.mozilla.fenix.utils.allowUndo
|
import org.mozilla.fenix.utils.allowUndo
|
||||||
|
import org.mozilla.fenix.whatsnew.WhatsNew
|
||||||
|
|
||||||
@SuppressWarnings("TooManyFunctions", "LargeClass")
|
@SuppressWarnings("TooManyFunctions", "LargeClass")
|
||||||
class HomeFragment : Fragment(), AccountObserver {
|
class HomeFragment : Fragment(), AccountObserver {
|
||||||
|
@ -572,6 +573,19 @@ class HomeFragment : Fragment(), AccountObserver {
|
||||||
from = BrowserDirection.FromHome
|
from = BrowserDirection.FromHome
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
HomeMenu.Item.WhatsNew -> {
|
||||||
|
invokePendingDeleteJobs()
|
||||||
|
hideOnboardingIfNeeded()
|
||||||
|
WhatsNew.userViewedWhatsNew(context!!)
|
||||||
|
(activity as HomeActivity).openToBrowserAndLoad(
|
||||||
|
searchTermOrURL = SupportUtils.getSumoURLForTopic(
|
||||||
|
context!!,
|
||||||
|
SupportUtils.SumoTopic.WHATS_NEW
|
||||||
|
),
|
||||||
|
newTab = true,
|
||||||
|
from = BrowserDirection.FromHome
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,17 +5,21 @@
|
||||||
package org.mozilla.fenix.home
|
package org.mozilla.fenix.home
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import mozilla.components.browser.menu.BrowserMenuBuilder
|
import mozilla.components.browser.menu.BrowserMenuBuilder
|
||||||
import mozilla.components.browser.menu.item.BrowserMenuDivider
|
import mozilla.components.browser.menu.item.BrowserMenuDivider
|
||||||
|
import mozilla.components.browser.menu.item.BrowserMenuHighlightableItem
|
||||||
import mozilla.components.browser.menu.item.BrowserMenuImageText
|
import mozilla.components.browser.menu.item.BrowserMenuImageText
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.theme.ThemeManager
|
import org.mozilla.fenix.theme.ThemeManager
|
||||||
|
import org.mozilla.fenix.whatsnew.WhatsNew
|
||||||
|
|
||||||
class HomeMenu(
|
class HomeMenu(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val onItemTapped: (Item) -> Unit = {}
|
private val onItemTapped: (Item) -> Unit = {}
|
||||||
) {
|
) {
|
||||||
sealed class Item {
|
sealed class Item {
|
||||||
|
object WhatsNew : Item()
|
||||||
object Help : Item()
|
object Help : Item()
|
||||||
object Settings : Item()
|
object Settings : Item()
|
||||||
object Library : Item()
|
object Library : Item()
|
||||||
|
@ -30,7 +34,7 @@ class HomeMenu(
|
||||||
R.drawable.ic_settings,
|
R.drawable.ic_settings,
|
||||||
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||||
) {
|
) {
|
||||||
onItemTapped.invoke(HomeMenu.Item.Settings)
|
onItemTapped.invoke(Item.Settings)
|
||||||
},
|
},
|
||||||
|
|
||||||
BrowserMenuImageText(
|
BrowserMenuImageText(
|
||||||
|
@ -38,7 +42,7 @@ class HomeMenu(
|
||||||
R.drawable.ic_library,
|
R.drawable.ic_library,
|
||||||
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||||
) {
|
) {
|
||||||
onItemTapped.invoke(HomeMenu.Item.Library)
|
onItemTapped.invoke(Item.Library)
|
||||||
},
|
},
|
||||||
|
|
||||||
BrowserMenuDivider(),
|
BrowserMenuDivider(),
|
||||||
|
@ -47,7 +51,21 @@ class HomeMenu(
|
||||||
R.drawable.ic_help,
|
R.drawable.ic_help,
|
||||||
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
ThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||||
) {
|
) {
|
||||||
onItemTapped.invoke(HomeMenu.Item.Help)
|
onItemTapped.invoke(Item.Help)
|
||||||
})
|
},
|
||||||
|
|
||||||
|
BrowserMenuHighlightableItem(
|
||||||
|
context.getString(R.string.browser_menu_whats_new),
|
||||||
|
R.drawable.ic_whats_new,
|
||||||
|
highlight = BrowserMenuHighlightableItem.Highlight(
|
||||||
|
startImageResource = R.drawable.ic_whats_new_notification,
|
||||||
|
backgroundResource = ThemeManager.resolveAttribute(R.attr.selectableItemBackground, context),
|
||||||
|
colorResource = ContextCompat.getColor(context, R.color.whats_new_notification_color)
|
||||||
|
),
|
||||||
|
isHighlighted = { WhatsNew.shouldHighlightWhatsNew(context) }
|
||||||
|
) {
|
||||||
|
onItemTapped.invoke(Item.WhatsNew)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,11 @@ import androidx.core.content.pm.PackageInfoCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
|
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
|
||||||
import kotlinx.android.synthetic.main.fragment_about.*
|
import kotlinx.android.synthetic.main.fragment_about.*
|
||||||
|
import org.mozilla.fenix.BrowserDirection
|
||||||
import org.mozilla.fenix.BuildConfig
|
import org.mozilla.fenix.BuildConfig
|
||||||
|
import org.mozilla.fenix.HomeActivity
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.whatsnew.WhatsNew
|
||||||
import org.mozilla.geckoview.BuildConfig as GeckoViewBuildConfig
|
import org.mozilla.geckoview.BuildConfig as GeckoViewBuildConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,6 +73,21 @@ class AboutFragment : Fragment() {
|
||||||
startActivity(Intent(context, OssLicensesMenuActivity::class.java))
|
startActivity(Intent(context, OssLicensesMenuActivity::class.java))
|
||||||
OssLicensesMenuActivity.setActivityTitle(getString(R.string.open_source_licenses_title, appName))
|
OssLicensesMenuActivity.setActivityTitle(getString(R.string.open_source_licenses_title, appName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
with(whats_new_button) {
|
||||||
|
text = getString(R.string.about_whats_new, getString(R.string.app_name))
|
||||||
|
setOnClickListener {
|
||||||
|
WhatsNew.userViewedWhatsNew(context!!)
|
||||||
|
(activity as HomeActivity).openToBrowserAndLoad(
|
||||||
|
searchTermOrURL = SupportUtils.getSumoURLForTopic(
|
||||||
|
context!!,
|
||||||
|
SupportUtils.SumoTopic.WHATS_NEW
|
||||||
|
),
|
||||||
|
newTab = true,
|
||||||
|
from = BrowserDirection.FromAbout
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -29,7 +29,8 @@ object SupportUtils {
|
||||||
HELP("faq-android"),
|
HELP("faq-android"),
|
||||||
PRIVATE_BROWSING_MYTHS("common-myths-about-private-browsing"),
|
PRIVATE_BROWSING_MYTHS("common-myths-about-private-browsing"),
|
||||||
YOUR_RIGHTS("your-rights"),
|
YOUR_RIGHTS("your-rights"),
|
||||||
TRACKING_PROTECTION("tracking-protection-firefox-preview")
|
TRACKING_PROTECTION("tracking-protection-firefox-preview"),
|
||||||
|
WHATS_NEW("whats-new-firefox-preview")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSumoURLForTopic(context: Context, topic: SumoTopic): String {
|
fun getSumoURLForTopic(context: Context, topic: SumoTopic): String {
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
package org.mozilla.fenix.whatsnew
|
||||||
|
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
|
||||||
|
// This file is a modified port from Focus Android
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class tracking whether the application was recently updated in order to show "What's new"
|
||||||
|
* menu items and indicators in the application UI.
|
||||||
|
*
|
||||||
|
* The application is considered updated when the application's version name changes (versionName
|
||||||
|
* in the manifest). The applications version code would be a good candidates too, but it might
|
||||||
|
* change more often (RC builds) without the application actually changing from the user's point
|
||||||
|
* of view.
|
||||||
|
*
|
||||||
|
* Whenever the application was updated we still consider the application to be "recently updated"
|
||||||
|
* for the next few days.
|
||||||
|
*/
|
||||||
|
class WhatsNew private constructor(private val storage: WhatsNewStorage) {
|
||||||
|
|
||||||
|
private fun hasBeenUpdatedRecently(currentVersion: WhatsNewVersion): Boolean {
|
||||||
|
val lastKnownAppVersion = storage.getVersion()
|
||||||
|
|
||||||
|
// Update the version and date if *just* updated
|
||||||
|
lastKnownAppVersion?.let {
|
||||||
|
if (currentVersion.majorVersionNumber > it.majorVersionNumber) {
|
||||||
|
storage.setVersion(currentVersion)
|
||||||
|
storage.setDateOfUpdate(System.currentTimeMillis())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (!storage.getWhatsNewHasBeenCleared() && storage.getDaysSinceUpdate() < DAYS_PER_UPDATE)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* How many days do we consider the app to be updated?
|
||||||
|
*/
|
||||||
|
private const val DAYS_PER_UPDATE = 3
|
||||||
|
|
||||||
|
internal var wasUpdatedRecently: Boolean? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should we highlight the "What's new" menu item because this app been updated recently?
|
||||||
|
*
|
||||||
|
* This method returns true either if this is the first start of the application since it
|
||||||
|
* was updated or this is a later start but still recent enough to consider the app to be
|
||||||
|
* updated recently.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun shouldHighlightWhatsNew(currentVersion: WhatsNewVersion, storage: WhatsNewStorage): Boolean {
|
||||||
|
// Cache the value for the lifetime of this process (or until userViewedWhatsNew() is called)
|
||||||
|
if (wasUpdatedRecently == null) {
|
||||||
|
val whatsNew = WhatsNew(storage)
|
||||||
|
wasUpdatedRecently = whatsNew.hasBeenUpdatedRecently(currentVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
return wasUpdatedRecently!!
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to run from the context.
|
||||||
|
*/
|
||||||
|
fun shouldHighlightWhatsNew(context: Context): Boolean {
|
||||||
|
return shouldHighlightWhatsNew(
|
||||||
|
ContextWhatsNewVersion(context),
|
||||||
|
SharedPreferenceWhatsNewStorage(context)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the "updated" state and continue as if the app was not updated recently.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
private fun userViewedWhatsNew(storage: WhatsNewStorage) {
|
||||||
|
wasUpdatedRecently = false
|
||||||
|
storage.setWhatsNewHasBeenCleared(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to run from the context.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun userViewedWhatsNew(context: Context) {
|
||||||
|
userViewedWhatsNew(
|
||||||
|
SharedPreferenceWhatsNewStorage(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package org.mozilla.fenix.whatsnew
|
||||||
|
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.preference.PreferenceManager
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
// This file is a modified port from Focus Android
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to abstract where the cached version and session counter is stored
|
||||||
|
*/
|
||||||
|
interface WhatsNewStorage {
|
||||||
|
fun getVersion(): WhatsNewVersion?
|
||||||
|
fun setVersion(version: WhatsNewVersion)
|
||||||
|
fun getWhatsNewHasBeenCleared(): Boolean
|
||||||
|
fun setWhatsNewHasBeenCleared(cleared: Boolean)
|
||||||
|
fun getDaysSinceUpdate(): Long
|
||||||
|
fun setDateOfUpdate(day: Long)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
internal const val PREFERENCE_KEY_APP_NAME = "whatsnew-lastKnownAppVersionName"
|
||||||
|
internal const val PREFERENCE_KEY_WHATS_NEW_CLEARED = "whatsnew-cleared"
|
||||||
|
internal const val PREFERENCE_KEY_UPDATE_DAY = "whatsnew-lastKnownAppVersionUpdateDay"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SharedPreferenceWhatsNewStorage(private val sharedPreference: SharedPreferences) :
|
||||||
|
WhatsNewStorage {
|
||||||
|
|
||||||
|
constructor(context: Context) : this(PreferenceManager.getDefaultSharedPreferences(context))
|
||||||
|
|
||||||
|
override fun getVersion(): WhatsNewVersion? {
|
||||||
|
return sharedPreference.getString(WhatsNewStorage.PREFERENCE_KEY_APP_NAME, null)?.let {
|
||||||
|
WhatsNewVersion(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setVersion(version: WhatsNewVersion) {
|
||||||
|
sharedPreference.edit()
|
||||||
|
.putString(WhatsNewStorage.PREFERENCE_KEY_APP_NAME, version.version)
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getWhatsNewHasBeenCleared(): Boolean {
|
||||||
|
return sharedPreference.getBoolean(WhatsNewStorage.PREFERENCE_KEY_WHATS_NEW_CLEARED, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setWhatsNewHasBeenCleared(cleared: Boolean) {
|
||||||
|
sharedPreference.edit()
|
||||||
|
.putBoolean(WhatsNewStorage.PREFERENCE_KEY_WHATS_NEW_CLEARED, cleared)
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDaysSinceUpdate(): Long {
|
||||||
|
val updateDay = sharedPreference.getLong(WhatsNewStorage.PREFERENCE_KEY_UPDATE_DAY, 0)
|
||||||
|
return TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - updateDay)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setDateOfUpdate(day: Long) {
|
||||||
|
sharedPreference.edit()
|
||||||
|
.putLong(WhatsNewStorage.PREFERENCE_KEY_UPDATE_DAY, day)
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package org.mozilla.fenix.whatsnew
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import mozilla.components.support.ktx.android.content.appVersionName
|
||||||
|
|
||||||
|
// This file is a modified port from Focus Android
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience class to deal with the application version number
|
||||||
|
* I opted to keep it contained to the whatsnew package. We may
|
||||||
|
* want to pull it
|
||||||
|
*/
|
||||||
|
open class WhatsNewVersion(internal open val version: String) {
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return version.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other is WhatsNewVersion) {
|
||||||
|
return version == other.version
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val majorVersionNumber: Int
|
||||||
|
get() = version.split(".").first().toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ContextWhatsNewVersion(private val context: Context) : WhatsNewVersion("") {
|
||||||
|
override val version: String
|
||||||
|
get() = context.appVersionName ?: ""
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@drawable/ic_whats_new" />
|
||||||
|
<item
|
||||||
|
android:left="200dp"
|
||||||
|
android:bottom="200dp">
|
||||||
|
<shape
|
||||||
|
android:shape="oval">
|
||||||
|
|
||||||
|
<solid android:color="@color/whats_new_notification_color" />
|
||||||
|
<size
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
|
@ -81,6 +81,20 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/whats_new_button"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/about_whats_new"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="?accent"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/view_licenses_button"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
|
@ -404,7 +404,11 @@
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/aboutFragment"
|
android:id="@+id/aboutFragment"
|
||||||
android:name="org.mozilla.fenix.settings.AboutFragment"
|
android:name="org.mozilla.fenix.settings.AboutFragment"
|
||||||
android:label="AboutFragment" />
|
android:label="AboutFragment" >
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_aboutFragment_to_browserFragment"
|
||||||
|
app:destination="@id/browserFragment" />
|
||||||
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/crashReporterFragment"
|
android:id="@+id/crashReporterFragment"
|
||||||
android:name="org.mozilla.fenix.crashes.CrashReporterFragment"
|
android:name="org.mozilla.fenix.crashes.CrashReporterFragment"
|
||||||
|
|
|
@ -217,4 +217,7 @@
|
||||||
|
|
||||||
<!-- Private Browsing Mode Persistent Notification -->
|
<!-- Private Browsing Mode Persistent Notification -->
|
||||||
<color name="pbm_notification_color">#592ACB</color>
|
<color name="pbm_notification_color">#592ACB</color>
|
||||||
|
|
||||||
|
<!-- Notification Color -->
|
||||||
|
<color name="whats_new_notification_color">#00B3F4</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -42,6 +42,8 @@
|
||||||
<string name="browser_menu_stop">Stop</string>
|
<string name="browser_menu_stop">Stop</string>
|
||||||
<!-- Browser menu button that sends a user to help articles -->
|
<!-- Browser menu button that sends a user to help articles -->
|
||||||
<string name="browser_menu_help">Help</string>
|
<string name="browser_menu_help">Help</string>
|
||||||
|
<!-- Browser menu button that sends a to a the what's new article -->
|
||||||
|
<string name="browser_menu_whats_new">What\'s New</string>
|
||||||
<!-- Browser menu button that opens the settings menu -->
|
<!-- Browser menu button that opens the settings menu -->
|
||||||
<string name="browser_menu_settings">Settings</string>
|
<string name="browser_menu_settings">Settings</string>
|
||||||
<!-- Browser menu button that opens a user's library -->
|
<!-- Browser menu button that opens a user's library -->
|
||||||
|
@ -817,6 +819,8 @@
|
||||||
<string name="about_your_rights">Your rights</string>
|
<string name="about_your_rights">Your rights</string>
|
||||||
<!-- About page link text to open open source licenses screen -->
|
<!-- About page link text to open open source licenses screen -->
|
||||||
<string name="about_open_source_licenses">Open source libraries we use</string>
|
<string name="about_open_source_licenses">Open source libraries we use</string>
|
||||||
|
<!-- About page link text to open what's new link -->
|
||||||
|
<string name="about_whats_new">What\'s new in %s</string>
|
||||||
<!-- Open source licenses page title
|
<!-- Open source licenses page title
|
||||||
The first parameter is the app name -->
|
The first parameter is the app name -->
|
||||||
<string name="open_source_licenses_title">%s | OSS Libraries</string>
|
<string name="open_source_licenses_title">%s | OSS Libraries</string>
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
package org.mozilla.fenix.whatsnew
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||||
|
import mozilla.components.support.test.robolectric.testContext
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mozilla.fenix.TestApplication
|
||||||
|
import org.mozilla.fenix.ext.clearAndCommit
|
||||||
|
import org.mozilla.fenix.utils.Settings
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
|
||||||
|
@ObsoleteCoroutinesApi
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
@Config(application = TestApplication::class)
|
||||||
|
class WhatsNewStorageTest {
|
||||||
|
private lateinit var storage: SharedPreferenceWhatsNewStorage
|
||||||
|
private lateinit var settings: Settings
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
storage = SharedPreferenceWhatsNewStorage(testContext)
|
||||||
|
settings = Settings.getInstance(testContext)
|
||||||
|
.apply(Settings::clear)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testGettingAndSettingAVersion() {
|
||||||
|
val version = WhatsNewVersion("3.0")
|
||||||
|
storage.setVersion(version)
|
||||||
|
|
||||||
|
val storedVersion = storage.getVersion()
|
||||||
|
Assert.assertEquals(version, storedVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testGettingAndSettingTheDateOfUpdate() {
|
||||||
|
val currentTime = System.currentTimeMillis()
|
||||||
|
val twoDaysAgo = (currentTime - DAY_IN_MILLIS * 2)
|
||||||
|
storage.setDateOfUpdate(twoDaysAgo)
|
||||||
|
|
||||||
|
val storedDate = storage.getDaysSinceUpdate()
|
||||||
|
Assert.assertEquals(2, storedDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testGettingAndSettingHasBeenCleared() {
|
||||||
|
val hasBeenCleared = true
|
||||||
|
storage.setWhatsNewHasBeenCleared(hasBeenCleared)
|
||||||
|
|
||||||
|
val storedHasBeenCleared = storage.getWhatsNewHasBeenCleared()
|
||||||
|
Assert.assertEquals(hasBeenCleared, storedHasBeenCleared)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val DAY_IN_MILLIS = 3600 * 1000 * 24
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Settings.clear() {
|
||||||
|
preferences.clearAndCommit()
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package org.mozilla.fenix.whatsnew
|
||||||
|
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNotEquals
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mozilla.fenix.TestApplication
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
|
||||||
|
@ObsoleteCoroutinesApi
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
@Config(application = TestApplication::class)
|
||||||
|
class WhatsNewVersionTest {
|
||||||
|
@Test
|
||||||
|
fun testMajorVersionNumber() {
|
||||||
|
val versionOne = WhatsNewVersion("1.2.0")
|
||||||
|
assertEquals(1, versionOne.majorVersionNumber)
|
||||||
|
|
||||||
|
val versionTwo = WhatsNewVersion("2.4.0")
|
||||||
|
assertNotEquals(1, versionTwo.majorVersionNumber)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue