My LottieAnimation is not showing inside of a jetpack compose #Preview fun. It works fine when running on an actual device.
#Preview
#Composable
fun LottiePreview() {
MaterialTheme {
Surface {
Column {
Text(text = "Above lottie")
val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.no_receipts))
LottieAnimation(
composition = composition,
)
Text(text = "Below lottie")
}
}
}
}
I've tinkered with different settings and constructors but haven't been able to get the preview to work - I also have been unable to find any documentation or open issues on this. Am I just missing something obvious to get lotties working in previews?
Animations don't work on the static previews. You need to tap on that button with a finger with waves on the tips (right after the label "LottiePrevi..."), so you'll be able to see animations running, and also interact with the composables.
Related
I have a composable like here.
#Composable
fun MyBasicTextField() {
val keyboardController = LocalSoftwareKeyboardController.current
val focusRequester = remember{ FocusRequester() }
BasicTextField(
modifier = Modifier
.focusRequester(focusRequester),
keyboardActions = keyboardActions ?: KeyboardActions(onAny = { keyboardController?.hide() }),
)
LaunchedEffect(Unit) {
Log.d(TAG, "focusRequester.requestFocus()")
focusRequester.requestFocus()
}
}
When the screen where this composable is used opens for the first time, always the keyboard is shown (above log message is visible).
Then I leave the app at that state and open another app (opening a link on that screen, which opens the default browser for instance)
Tapping on the BACK button (triangle) leaved the other app (e.g. webbrowser) and comes back to the initial screen: Either with opened Android keyboard on some devices or without any keyboard showing.
I have the feeling that the screen did not notice the missing keyboard (which disappreared while leaving the app for the browser) and thus does not recompose anything?
Can I flag to compose the screen from fresh everytime I re-/compose it?
I'm not sure why you need this, because focusing and showing keyboard every time re-composition happens may be undesirable, what if the user is doing something else on the screen which updates the field, you don't want want to show the keyboard but re-composition happens and you pop it again.
Having said this it's of course doable:
For example the argument passed into the LaunchedEffect determines if during re-composition the block will be run.
So you could try something like this:
#Composable
fun MyBasicTextField() {
val keyboardController = LocalSoftwareKeyboardController.current
val focusRequester = remember{ FocusRequester() }
var launchKey by remember { mutableStateOf(0) }
BasicTextField(
modifier = Modifier
.focusRequester(focusRequester),
keyboardActions = keyboardActions ?: KeyboardActions(onAny = { keyboardController?.hide() }),
)
//passing launchKey instead of Unit
LaunchedEffect(launchKey++) {
Log.d(TAG, "focusRequester.requestFocus()")
focusRequester.requestFocus()
}
}
I have a screen that has a player with a video that plays once (so it doesn't loop). After the video finished playing, the player keeps showing the last frame, which is what I want. However, if the video finished playing and I turn the screen off and on, then the player shows a black screen. What I want, is that the player keeps showing the last frame. Is this possible? This issue also persists if the non-looping video hasn't finished playing yet.
It's important to note that this issue doesn't exist for looping videos.
There is a lot of documentation about the lifecycle of the player in regular Android, but none for Compose. I could only find articles online about a Compose version, where the player is released in onDispose.
I have a Jetpack Compose implementation of ExoPlayer using AndroidView (since there is no native support yet). Here it is:
#Composable
fun VideoLayout(#RawRes rawResource: Int) {
val context = LocalContext.current
val uri = RawResourceDataSource.buildRawResourceUri(rawResource)
val mediaItem = MediaItem.fromUri(uri)
val exoPlayer = remember(context, mediaItem) {
ExoPlayer.Builder(context).build().apply {
addMediaItem(mediaItem)
prepare()
play()
repeatMode = REPEAT_MODE_OFF
}
}
DisposableEffect(exoPlayer) { onDispose { exoPlayer.release() } }
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = {
StyledPlayerView(it).apply {
player = exoPlayer
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
useController = false
}
}
)
}
I am adding a "Today" extension widget to my iOS app that I made with Xamarin. I am following this walkthrough:
https://developer.xamarin.com/guides/ios/platform_features/introduction_to_extensions/
The widget appears in my Notification section in the simulator, but I can't get any contents to appear in it. It won't even create the UIViewController class that I made and set as the initial controller to start with (I know because it never hits my breakpoint in the constructor). I set it as the principal class with this key as explained in the walkthrough:
Any idea why? I also get this message when I first launch the app after adding the extension:
appname may slow down your phone the developer of this app needs to update it to improve its compatibility
I made a sample project, with Xamarin, and the widget does appear in this project when deployed on the simulator, just not with the contents that I'm trying to add in the CodeViewController class:
https://drive.google.com/file/d/0B8xKHTqtwfKtY0xZN0xaejhlZmM/view?usp=sharing
To save you 2 days I spent on it here is the solution.
Don't run it on simulator. It doesn't work (at least on mine).
Don't try to hit breakpoint in VS. When you testing your extension your app is in background mode. VS will not give you to stop in debugger. To prove run any of your apps, press home and try to set breakpoint in VS. VS will hang till you bring your app to foreground.
Do not use View.Frame in DidLoad. The size of the frame there is the whole screen size, so when you put your label to center you will not see it. Use WillAppear like this
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
if (TodayMessage == null)
{
// Add label to view
TodayMessage = new UILabel(new CGRect(0, 0, View.Frame.Width, View.Frame.Height))
{
TextAlignment = UITextAlignment.Center,
BackgroundColor = UIColor.LightGray,
TextColor = UIColor.Black
};
// Calculate the values
var dayOfYear = DateTime.Now.DayOfYear;
var leapYearExtra = DateTime.IsLeapYear(DateTime.Now.Year) ? 1 : 0;
var daysRemaining = 365 + leapYearExtra - dayOfYear;
// Display the message
if (daysRemaining == 1)
{
TodayMessage.Text = String.Format("Today is day {0}. There is one day remaining in the year.", dayOfYear);
}
else
{
TodayMessage.Text = String.Format("Today is day {0}. There are {1} days remaining in the year.", dayOfYear, daysRemaining);
}
View.AddSubview(TodayMessage);
}
}
Please help me with Notification in my Firefox add-on.
var notifications = require("sdk/notifications");
function showNotifcation(title, text) {
notifications.notify({
iconURL: data.url("img/icon.png"),
title: title,
text: text
});
setTimeout(notifications.close(), 1000);
}
Not work.
Without more information from you it is not possible to be sure as to what your problem/issue is.
However, a brief look at the sdk/notifications documentation, and source code, indicates that you are attempting to use a non-existent method: notifications.close(). There is no such method in sdk/notifications.
One possible reason for your attempt to use this method is that you are conflating the Web Notification API, more detail, with the Add-on SDK sdk/notifications.
The Add-on SDK, sdk/notifications, has no way for you to programmatically close the notification from your code. Thus, there is no way for you to set a timeout for the notification using this interface. However, in some operating systems/windowing systems there is already a default timeout for these notifications.
You will need to either display a panel on your own, or use the chrome interfaces described in User Notifications and Alerts.
In addition, it would be unusual for you to be able to just call setTimeout(). That will, under most contexts, not be defined. You would normally need to use sdk/timers with:
var { setTimeout } = require("sdk/timers");
In some contexts, you might be able to use window.setTimeout(), when window is appropriately defined (which you will probably have to set yourself).
Modifying the code from my answer to Prevent XUL notificationBox from closing when button is hit (if you want buttons, that answer will show you how to do it), and other answers of mine: Something along the lines of what I believe you desire would be (code for the timeout is at the bottom):
function showNotificationBox(text) {
//Create some common variables if they do not exist.
if (window === null || typeof window !== "object") {
// Add/remove a "/" to comment/un-comment the code appropriate for your add-on:
//* Add-on SDK:
var window = require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
/* Overlay and bootstrap (from almost any context/scope):
var window=Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
//*/
}
if (typeof gBrowser === "undefined") {
var gBrowser = window.gBrowser;
}
let notifyBox = gBrowser.getNotificationBox();
//appendNotification( label , value , image (URL) , priority , buttons, eventCallback )
let theNotification = notifyBox.appendNotification(text, "Test notification unique ID",
"chrome://browser/content/aboutRobots-icon.png",
notifyBox.PRIORITY_INFO_HIGH, [], null);
//* Add-on SDK:
var { setTimeout } = require("sdk/timers");
setTimeout(theNotification.close(), 10000);
//*/
/* Overlay and bootstrap:
let timerCallback = {
notify:function notify() {theNotification.close(); }
}
let closeNotificationTimer = Components.classes["#mozilla.org/timer;1"]
.createInstance(Components.interfaces.nsITimer);
closeNotificationTimer.initWithCallback(timerCallback,10000,
Components.interfaces.nsITimer.TYPE_ONE_SHOT);
//*/
}
Note: I changed the timeout to 10 seconds from the 1 second which is in the code in your question. One second is a unreasonable amount of time to expect to show anything which you actually desire the user to see and understand.
The above implements the user notification in a notificationBox. As such it shows up within the Firefox window:
It is also possible to use the nsIAlertsService which is what sdk/notifications uses. This will normally display an alert box in the bottom right of the screen, potentially outside of the Firefox window (see image on nsIAlertsService for example). The notification may show up elsewhere depending on how you have your windowing system set up (this is OS dependent). However, the documentation did not have a method to clear the notification, or set a timeout. However, the interface definition does show that a closeAlert() method does exist. The source code for the sdk/notifications does not expose this to the Add-on SDK. Thus, you would need to use the chrome interfaces. I have updated the documentation to show closeAlert().
Such as (some code taken and modified from nsIAlertsService):
//* Add-on SDK:
var {Cc, Ci} = require("chrome");
//*/
/* Overlay and bootstrap:
const Cc = Components.classes;
const Ci = Components.interfaces;
//*/
function showNotifcation(title, text) {
var alertsService = Cc["#mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
try {
//The second use of title is the alert name.
alertsService.showAlertNotification(icon, title, text, false, "", null, title);
} catch (e) {
// This can fail on Mac OS X
}
//* Add-on SDK:
var { setTimeout } = require("sdk/timers");
setTimeout(alertsService.closeAlert(title), 10000);
//*/
/* Overlay and bootstrap:
let alertTimerCallback = {
notify:function notify() {alertsService.closeAlert(title); }
}
let closeAlertTimer = Cc["#mozilla.org/timer;1"].createInstance(Components.interfaces
.nsITimer);
closeAlertTimer.initWithCallback(alertTimerCallback,10000,Ci.nsITimer.TYPE_ONE_SHOT);
//*/
}
I have only tested the above code with a bootstrapped/restartless Firefox add-on. Thus, the Add-on SDK code may be slightly off.
Edited to explain why its not a duplicate:
I am trying to use MPMusicPlayerController.systemMusicPlayer() to control music playback from my app, but I want to disable the Command Center previous track button. I also want to override the default Command Center play and next track functions. The code should be simple:
This code is in ViewController.swift - viewDidLoad
let commandCenter = MPRemoteCommandCenter.sharedCommandCenter()
commandCenter.previousTrackCommand.enabled = false
commandCenter.previousTrackCommand.addTargetWithHandler({ (commandEvent: MPRemoteCommandEvent!) -> MPRemoteCommandHandlerStatus in
self.empty()
return MPRemoteCommandHandlerStatus.Success
})
//MPRemoteCommandCenter.sharedCommandCenter().previousTrackCommand.addTarget(self, action: "empty")
commandCenter.nextTrackCommand.enabled = true
commandCenter.nextTrackCommand.addTargetWithHandler { (commandEvent: MPRemoteCommandEvent!) -> MPRemoteCommandHandlerStatus in
self.gameOver()
return MPRemoteCommandHandlerStatus.Success
}
commandCenter.playCommand.enabled = true
commandCenter.playCommand.addTargetWithHandler { (commandEvent: MPRemoteCommandEvent!) -> MPRemoteCommandHandlerStatus in
self.playing()
return MPRemoteCommandHandlerStatus.Success
}
Also, in AppDelegate.swift - application
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
And in the iOS simulator (both iPad and iPhone), it works correctly, as can be seen in the first screenshot (of the simulator).
However, when deploying the app to my iPad, none of the MPRemoteCommandCenter commands work at all, as can be seen in the second screenshot (of an actual device).
This is different from the "dupliate" question (How Do I Get Audio Controls on Lock Screen/Control Center from AVAudioPlayer in Swift) in the following ways:
I am not using AVAudioSession, I am using MPMusicPlayerController.systemMusicPlayer()
I have already called beginReceivingRemoteControlEvents, so that cant be the issue (unless I have somehow called it incorrectly, in which case, I would love an answer explaining how else it should be called).
Thank you.
,