-
Notifications
You must be signed in to change notification settings - Fork 1.3k
For #24040 - App should not crash when the search widget is clicked while PIP mode is active #24951
Conversation
No Taskcluster jobs started for this pull requestThe `allowPullRequests` configuration for this repository (in `.taskcluster.yml` on the
default branch) does not allow starting tasks for this pull request. |
@Mugurell , to avoid the crash when the search widget is tapped and to open the search page in portrait mode, I've modified the StartSearchIntentProcessor and added a method in BrowserStore. The app doesn't crash and the search is opened in portrait mode. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a fine approach to handle the race but I'm a bit worried about the result.
It seems like we will open to the fullscreen video and after a small delay we'll navigate to home to actually enter search mode. Not sure how big the delay would be but it could probably be seen by users as another issue.
Would it be possible to go the other way around, navigate to home to enter search immediately, and handle fullscreen media cleanup in the background?
block: () -> Unit | ||
) { | ||
|
||
if (state.findActiveMediaTab() == null || !state.findActiveMediaTab()?.mediaSessionState?.fullscreen!!) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the fullscreen check avoid the not null assertion (!!) operator for something like
state.findActiveMediaTab()?.mediaSessionState?.fullscreen? == false
(here and below. This could even be another method (extension or not) - isPlayingMediaInFullscreen()
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for reviewing! I'll update the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Mugurell , if we go to homefragment immediately the screen orientation will still be in landscape mode.
When the BrowserFragment receives pipmode as false, FullScreenFeature.onBackPressed() is invoked which triggers the full screen exit event which finally reaches MediaSessionFullscreenFeature. When MediaSessionFullscreenFeature finds out that the fullscreen is false only then the activity orientation is set to portrait. Will explore more..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the intent processor can also set sensor orientation?
It might be that we need all the functionality to exit fullscreen here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. We can do it that way
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Mugurell , I've updated the changes. But the screen first goes to the landscape mode and then to the portrait mode, since the activity is in landscape while it's in PIP mode.
Untitled.4.mp4
Maybe, we could just set the activity's orientation to ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED in MediaSessionFullscreenFeature, when the activity is in PIP mode. Is this a good way to solve the problem? If so I'll look deeper into the MediaSessionFullscreenFeature class since it's used by several classes.
private fun processFullscreen(sessionStates: List<SessionState>) {
/* there should only be one fullscreen session */
val activeState = sessionStates.firstOrNull()
if (activeState == null || activeState.mediaSessionState?.fullscreen != true) {
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER
return
}
when (activeState.mediaSessionState?.elementMetadata?.portrait) {
true ->
activity.requestedOrientation =
ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
false ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && activity.isInPictureInPictureMode) {
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}else{
activity.requestedOrientation =
ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
}
else -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER
}
}
When we search from search widget
fromsearch.mp4
Also when we enter from PIP (from home)
frompip2.mp4
import mozilla.components.feature.media.ext.findActiveMediaTab | ||
import mozilla.components.lib.state.Store | ||
|
||
fun BrowserStore.waitForPIPToClose( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure on whether we need this as an extension function - for this method to be added to the BrowserStore namespace and use the lower level Subscription
api but looks like it may be useful in other scenarios also and it seems we already do something similarly with BrowserStore.waitForSelectedOrDefaultSearchEngine.
Only please add the mozilla license and documentation and move this rather to fenix/ext
where other extensions are.
If you could also add some tests (in the likes of the ones for waitForSelectedOrDefaultSearchEngine
would be great!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.. Actually after going through waitForSelectedOrDefaultSearchEngine in BrowserStore, I thought that we can implement similar solution to avoid the race. Will move extension to fenix/ext and also add tests.
} | ||
} | ||
subscription.resume() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: missing newline.
} | ||
return processed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this return false
if fullscreen media is currently playing and the block of waitForPIPToClose
wasn't yet executed?
Seeing that in the current implementation true
is returned if event != null
and we're already in this case it seems to be okay to return here true
without the need of the new processed
property.
We return that this processor will act upon the Intent, just that we now introduce a small delay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this return false if fullscreen media is currently playing and the block of waitForPIPToClose wasn't yet executed?
-- No. It should not return false
Seeing that in the current implementation true is returned if event != null and we're already in this case it seems to be okay to return here true without the need of the new processed property.
We return that this processor will act upon the Intent, just that we now introduce a small delay.
-- I understand now that we can return true
val event = intent.extras?.getString(HomeActivity.OPEN_TO_SEARCH)
return if (event != null) {
store.waitForPIPToClose {
openSearch(event,navController,out)
}
return true
} else {
false
}
This pull request has conflicts when rebasing. Could you fix it @indurs? 🙏 |
@Mugurell , setting the orientation as ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED in MediaSessionFullscreenFeature when the activity is in PIP mode, might solve the orientation issue. Full screen media cleanup will be handled in the onStop() method of BaseBrowserFragment. In this scenario we don't have to change the orientation in StartSearchIntentProcessor. |
@indurs Videos look great! |
@Mugurell , Thank you !
I think there are several ways a user can navigate away from PIP mode. So far I've looked into search widget, share from focus to fenix, share from fenix to focus, home shortcut... and more |
@indurs I've created mozilla-mobile/android-components#12118 for this. |
@Mugurell, this PR is just for the change made to avoid the crash when the search widget is clicked. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@indurs There are some CI tasks failing related to code style, please check.
Also please squash the commits.
Would be great to add some tests to ensure the BrowserStore.waitForPIPToClose
functionality will not regress in the future.
Tested this for myself now and I see an important issue remaining: no crash but although the device was always kept in portrait upon clicking the search widget while in PIP the app and the search UX will start and remain blocked in landscape.
SearchWidgetOpensFenixInLandscape.mp4
If a more complex solution with support from AC is needed maybe for now just catching the IllegalStateException
would be a quick fix to prevent the crash.
@Mugurell, this PR is only for avoiding the crash when the search widget is tapped while in PIP mode. So the orientation will be locked to landscape as before. |
Having the device locked to landscape means users would have a hard time going back to portrait. |
Got it. Will just catch the IllegalStateException and commit. |
@Mugurell , after updating the MediaSessionFullscreenFeature for fixing the orientation issue, I'm not using the BrowserStore.waitForPIPToClose functionality. Shall I delete that extension from BrowserStore? |
Agree, thank you! So I understand you plan to base this fix on mozilla-mobile/android-components#12169 ? |
Thank You! Yes. We've to merge the MediaSessionFullscreenFeature changes in AC first and then this fix in Fenix. I'll squash the changes. |
@Mugurell, when I squash the commits, I get the error "Unable to reorder. Reordering replays all commits up to the last one required for the reorder. A merge commit cannot exist among those commits." Few days back I updated the branch couple of times by selecting "update with merge commit". Could you please let me know how to fix this issue? |
All solutions
Sorry but I don't know of any simple solution. While making small changes to existing commits I do prefer comiting with |
Thank You. Will try that approach.
Sure. I'll follow for future commits. |
@Mugurell , as you suggested I tried the above method and there is now only one commit. I think it should be fine now. Please let me know if I need to make any changes. Thank You |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested locally and it works great!
Just a few questions about some decisions in code.
@@ -503,6 +504,13 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { | |||
) | |||
) | |||
|
|||
if (components.core.store.state.findActiveMediaTab() != null) { | |||
lifecycleScope.launch(IO) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure this is really needed. Have you observed an adverse effect of running this without the coroutine?
It's just that we already filter tabs in many (like it happens with findActiveMediaTab()
on the Main thread without a known impact and there is also an inherent cost for the context switching here so I'm not sure of the benefits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't observe any improvement using the coroutine. You're right regarding the performance penalty with context-switching. I'll remove the coroutine.
MetricsUtils.Source.SHORTCUT | ||
} | ||
else -> null | ||
return openSearch(event, navController, out) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comparing before and after it seems to me that openSearch
contains the exact same code as before just extracted now in a new method?
If so this change probably won't be needed with preserving git history being more important.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The structure of the code changed when I modified StartSearchIntentProcessor. I removed the changes but I left the modified structure. I'll revert back to the original code structure. Thank you for pointing that. I'll keep that in mind when I do future changes.
… is clicked while PIP mode is active When the search event is from the search widget while PIP is active, the search fragment opens after the screen is unlocked. This avoids the issue of the search page opening in landscape mode and also the app doesn't crash. Co-Authored-By: Mugurell <[email protected]>
@Mugurell , I've committed the changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
Thank you for all the work on this!
Thank You @Mugurell ! |
Patch merged in #25410. |
When the search event is from the search widget while PIP is active, the search fragment opens after the screen is unlocked. This avoids the issue of the search page opening in landscape mode and also the app doesn't crash.
screenpip.mp4
Pull Request checklist
To download an APK when reviewing a PR: