Skip to content

Conversation

@cmonfortep
Copy link
Contributor

@cmonfortep cmonfortep commented Jul 19, 2021

Task/Issue URL: https://app.asana.com/0/488551667048375/1200651343654716/f
Tech Design URL:
CC:

Description:

This PR introduces a new widget for Android. The new widget will show user's favorites and the search bar.
The widget empty case includes a CTA to take the user through a small onboarding about how to add favorites.

We've also updated the UI for the existing widgets.

Steps to test this PR:

  • Test 1: onboarding
  1. Fresh install
  2. Navigate through the onboarding until you see the widget CTA (visit a site, open a new tab, visit another site, open another tab)
  3. Click on "Add Wiget" and then "Add automatically"
  4. press home and go to your homescreen
  5. Ensure widget was added
  6. Widget should a CTA saying "No Favorites added yet" - "Add Favorite"
  7. Click on "Add Favorite"
  8. Ensure pixel "m_sfew_l" sent
  9. Ensure Dax CTA appears "Visit your favorite sites in a flash! ..."
  10. Ensure pixel "m_fo_s" sent
  11. Visit a Site
  12. Ensure overflow menu is highlighted
  13. Open the menu
  14. Ensure "Add Favorite" is highlighted
  15. Click on that option
  16. Ensure pixel "m_nav_af_p" sent with "fmi=true"
  17. Visit another site
  18. Add the new site as favorite
  19. Ensure pixel "m_nav_af_p" sent with "fmi=false"
  20. Press home
  21. Ensure new favorites sites are shown in the widget
  22. Now click on the widget "search bar"
  23. Ensure you go to SystemSearch screen
  24. Ensure Pixel "m_sfbw_l" sent
  • Test 2: interact with favorites
  1. click on any favorite from the widget
  2. Ensure pixel "m_sfiw_l" sent
  3. Focus the URL bar
  4. Quick access grid should appear, visit a favorite from there
  5. Ensure pixel "m_fav_o" sent
  6. Now open a new tab
  7. Ensure pixel "m_fav_ht" sent
  8. Go to bookmarks screen
  9. Click on a favorite
  10. ensure pixel "m_fav_b" sent
  11. Remove one of your favorites
  12. Go to your homescreen
  13. Ensure the widget doesn't show the favorite you removed
  • Test 3: Add widget manually
  1. Add the favorites widget manually on your home screen (no need to remove the previous one)
  2. When the widget is placed on your homescreen, the configuration screen should appear
  3. Ensure pixel "m_sfw_cs" is sent
  4. Select "system default" theme and "add widget"
  5. Ensure pixel "m_sfw_sd" sent
  6. Change the theme of your device
  7. Widget should also change to honor system theme
  • Test 4: Resize the widget
  1. You should be able to resize the widget (width or height)
  2. Reduce the widget width to only show 2 columns
  3. Ensure search bar hint is not visible
  4. If widget has not favorites, empty CTA will not appear when widget has 2 columns

Internal references:

Software Engineering Expectations
Technical Design Template

cmonfortep added 30 commits June 3, 2021 23:30
- widget manifest
- configuration activity
- widget class
- search bar with clickable intent
- grid of items for favorites (using WidgetService)
private val favoritesDao: FavoritesDao,
private val faviconManager: Lazy<FaviconManager>,
) : FavoritesRepository {

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: remove this change

Comment on lines +102 to +107
fun inject(searchAndFavsWidget: SearchAndFavoritesWidget)

fun inject(favoritesWidgetItemFactory: FavoritesWidgetService.FavoritesWidgetItemFactory)

fun inject(emptyFavoritesWidgetItemFactory: EmptyFavoritesWidgetService.EmptyFavoritesWidgetItemFactory)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need these? For instance for the SearchAndFavoritesWidget why can't we do the injection in onReceive?

BTW I think we also don't need the fun inject(searchWidget: SearchWidget)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aitorvs and I talked about this, and try directly using AndroidInjector, however it will not work without providing an AndroidInjector.Factory in the same way we do for each Activity. We decided to keep it as it is since we will remove dagger-android soon.

@CDRussell CDRussell self-assigned this Jul 22, 2021
@cmonfortep cmonfortep marked this pull request as ready for review July 22, 2021 09:22

@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun notifyWidgets() {
GlobalScope.launch {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove GlobalScope

Copy link
Member

@CDRussell CDRussell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking awesome! 🎉

} else null
}

override suspend fun loadFromDiskWithParams(tabId: String?, url: String, radius: Int, width: Int, height: Int): Bitmap? {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
override suspend fun loadFromDiskWithParams(tabId: String?, url: String, radius: Int, width: Int, height: Int): Bitmap? {
override suspend fun loadFromDiskWithParams(tabId: String?, url: String, cornerRadius: Int, width: Int, height: Int): Bitmap? {

}

private fun launchedFromSearchWithFavsWidget(intent: Intent): Boolean {
return intent.getBooleanExtra(WIDGET_SEARCH_WITH_FAVS_EXTRA, false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a pixel

return EmptyFavoritesWidgetItemFactory(this.applicationContext, intent)
}

class EmptyFavoritesWidgetItemFactory(val context: Context, intent: Intent) : RemoteViewsFactory {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a comment around why this is needed

super.onDeleted(context, appWidgetIds)
}

private fun updateWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int, newOptions: Bundle?) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we pull out smaller functions from this?

var columns = calculateColumns(context, width)
var rows = calculateRows(context, height)

columns = if (columns < 2) 2 else columns
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe use coerce here too for consistency with rows

return Pair(columns, rows)
}

private fun calculateColumns(context: Context, width: Int): Int {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could likely extract calculateColumns and calculateRows to a utility class which could be unit tested

<string name="widgetConfigurationAddWidgetOption">Add widget</string>
<string name="searchWidgetEmtpyFavoritesHint">No favorites added yet</string>
<string name="searchWidgetEmtpyFavoritesCta">Add Favorite</string>
<string name="daxFavoritesOnboardingCtaText"><![CDATA[Visit your favorite sites in a flash!<br/><br/>Go to a site you love. Then tap the \"⋯\" icon and select Add Favorite.]]></string>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try the unicode character for the overflow (will also requiring manually setting the accessibility text)

<dimen name="searchWidgetSearchBarSideMargin">16dp</dimen>
<dimen name="searchWidgetFavoriteItemContainerWidth">64dp</dimen>
<dimen name="searchWidgetFavoriteItemContainerHeight">78dp</dimen>
<dimen name="searchWidget4colWidth">280dp</dimen>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4col probably not still valid

Copy link
Member

@CDRussell CDRussell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes looking good! added some tiny optional suggestions but don't mind about any of them

info.text = v?.context?.getString(R.string.daxFavoritesOnboardingCtaContentDescription)
}
}
// Using braille unicode inside textview, override description for accessibility
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth clarifying why we are doing that... e.g., using it to show something that looks like the overflow icon

@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun notifyWidgets() {
GlobalScope.launch {
appCoroutineScope.launch() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haven't checked in an IDE, but could probably remove the () i think?

Comment on lines 59 to 60
rows = 1.coerceAtLeast(rows)
rows = 4.coerceAtMost(rows)
Copy link
Member

@CDRussell CDRussell Aug 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 and 4 might read slightly better if they had constants explaining what they were (reads like magic numbers currently)

@cmonfortep cmonfortep merged commit b85f29e into develop Aug 2, 2021
@cmonfortep cmonfortep deleted the feature/cristian/favorites_widget branch August 2, 2021 13:21
var portraitWidth = appWidgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)

if (newOptions != null) {
portraitWidth = appWidgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it bug? Check newOptions is null and set same value for portraitWidth

portraitWidth = appWidgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
landsWidth = appWidgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
landsHeight = appWidgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
portraitHeight = appWidgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it bug? Check newOptions is null and set same value for variables?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants