I am trying to implement BetterVideoPlayer (implementation 'com.github.halilozercan:BetterVideoPlayer:1.2.alpha1') for NativeScript Android. So, in an Angular project I have created separate component like this:
import { View } from "tns-core-modules/ui/core/view";
declare var com, android;
export class BetterPlayer extends View {
public mediaPlayer;
public createNativeView(): any {
const nativeView = new android.widget.FrameLayout(this._context);
nativeView.addView(this._openVideo());
// or return this._openVideo(); // still not working..
return nativeView;
}
public _openVideo() {
let url = android.net.Uri.parse("http://jzvd.nathen.cn/c6e3dc12a1154626b3476d9bf3bd7266/6b56c5f0dc31428083757a45764763b0-5287d2089db37e62345123a1be272f8b.mp4");
this.mediaPlayer = new com.halilibo.bettervideoplayer.BetterVideoPlayer(this._context);
console.dir(this.mediaPlayer);
this.mediaPlayer.setSource(url);
this.mediaPlayer.setAutoPlay(true);
this.mediaPlayer.setHideControlsOnPlay(true);
return this.mediaPlayer;
}
}
Then from component I have registered it using registerElement
registerElement("BetterPlayer", () => BetterPlayer);
HTML:
<BetterPlayer height="200"></BetterPlayer>
But the problem is, it isn't showing the player. Only showing a black area. In where I did mistake? Please give me advice. Thanks in advance.
BetterVideoPlayer creates the actual media player within at onFinishInflate(), is a method called only when a View element is initiated via Android XML, programatic creation like you do in {N} won't trigger this method. So you could simply do this.mediaPlayer.onFinishInflate(); to force creating the media player and it should work, I was able to watch the video on device once I did.
Related
I have a class that is used to play sound using audioplayers.I have the below code where the function clickSound just plays a simple button click sound audio without any loop and the function loopSound plays music in a loop.However I am unable to play both at the same time in case the music is playing and I want to click a button for example.How can I implement that functionality with the below code?
import 'package:audioplayers/audioplayers.dart';
import 'package:biblequiz/services/preferences.dart';
class AudioService {
AudioPlayer player = AudioPlayer();
static final AudioService instance = AudioService._();
AudioService._() {
this.player.setVolume(1.0);
}
void clickSound() async {
await player.play(AssetSource('sounds/click.mp3'));
}
void loopSound() async {
player.setReleaseMode(ReleaseMode.loop);
await player.play(AssetSource('sounds/music.wav'));
}
void stopLoop() async {
await player.stop();
}
}
"Multiple singletons" by itself sounds mutually exclusive. While nothing is blocking you from creating another static instance under a different name, in your case, you may want to rethink your app's architecture. For example, you may want to use multiple instances of your AudioService instead of a singleton and inject the necessary instance of that AudioService.
I managed to test a simple app and the ads (banner and interstitial) work as expected (the test ones, because the real ones don't, idk why).
The problem is I want to integrate the ads into my main application. I did it for the banner and it works but don't know how to handle the interstitial. For the test app, I called a function when a button was pressed, but here in my app I don't want a button to be pressed in order to show an interstitial, I want this to happen every time the player dies. And I don't know how to do this.
using System;
using UnityEngine;
using GoogleMobileAds.Api;
public class ads : MonoBehaviour
{
private BannerView bannerView;
private InterstitialAd interstitial;
private void RequestBanner()...
private void RequestInterstitial()
{
string adUnitId = "ca-app-pub-3940256099942544/1033173712";
this.interstitial = new InterstitialAd(adUnitId);
AdRequest request = new AdRequest.Builder().Build();
this.interstitial.LoadAd(request);
}
public void GameOverSoShowInterstitial()
{
if (this.interstitial.IsLoaded())
{
this.interstitial.Show();
}
}
void Start()
{
MobileAds.Initialize(initStatus => { });
this.RequestBanner();
this.RequestInterstitial();
}
}
^this is my ads script
but it is located in the second scene named Menu, but since the interstitial should appear when the player dies, I should move that GameObject in the other scene that is the actual game, because it's impossible to die in the 'menu' section. And also, for checking when the ad has to appear I should use the GameManager, right? which is in the game scene, too.
public void GameOver()
{
gameOver = true;
gameScore = score.GetComponent<Score>().getScore();
score.SetActive(false);
Invoke("ActivateGameOverCanvas", 1);
pauseBtn.SetActive(false);
}
^this is the part with the gameover from gamemanager, so I think here I should introduce somehow an instruction that would generate the interstitial ad, but I don't have a clue. I tried to add the 'ads' script to gamemanager too, so at the end of the execution of GameOver function from gamemanager to call the GameOverSoShowInterstitial from ads, but it didn't work.
Any ideas? :(
1. Using objects from another Scene
When it comes to using scripts/objects from once Scene to another we generally put that object into a MonoBehaviour built-in method DontDestroyOnLoad(object);. By Using this method we can make sure that object will not be destroyed when you change the scenes. Assuming that Menu your script is in Menu Scene you can Put that object in DontDestroyOnLoad() method and it will not get destroyed throughout the game.
2. Using the Method from Add script
For this we have multiple options. Like you can find the Object Using GameObject.FindObjectOfType() or GameObject.FindGameObjectWithTag(), etc. But using Gameobject.FindXXX() method can be heavy on performance side. The battre way is to use singleton pattern. In this you basically create an object when your Game Starts(When you need the object). And there will be no other object of the same type (it mean that there will be no other object with the same script attached to the object).
3. See both of the points in Action
Your Ad script
you don't have to change any thing just have to add the Singleton pattern.
using System;
using UnityEngine;
using GoogleMobileAds.Api;
public class ads : MonoBehaviour
{
//This is a static instance of this class so that you can access it from anywhere from you game, Without creating any creating a new Object.(Point #2)
public static ads instance {
get; //These are the getter and setter methods of native C# find the reference below
private set;
}
private Awake()
{
//Check if the instance is already assigned(if there is already an object with this same script attached)
if(instance == null)
{
instance = this; //If instance is empty the assign this object to the instance
DoNotDestroyOnLoad(this.gameObject); //This line will stop Unity from destroying the object when you load new scene.(Point #1)
}
else
{
Destroy(this.gameObject); //since there is already an object of with script destroy this object so that there will be no conflicts between multiple instances of the same class.
}
}
void Start()
{
MobileAds.Initialize(initStatus => { });
this.RequestBanner();
this.RequestInterstitial();
}
public void ShowInterstitialAd()
{
... Show your Ad
}
}
Game Manager Script
public void PlayerDie() //assuming that you have a method for when your Player dies
{
...
ads.instance.ShowInterstitialAd(); //Call the ShowInterstitialAd() method because the object will not be destroyed when you load another scene.
}
I'm basically done here. I hope this helps you. here are some of the reference that might be helpful to understand this further.
Singleton pattern
Getter and setter methods of native C#
How to add an event listener to handle window message event in a WebView. I have tried this,
webView.evaluateJavascript("window.addEventListener('message', function (e) { Android.logData('HELLO')});", null);
But it is not working. Is there any way to achieve this?
After exploring I didn't find any way to get data from the website running in WebView to the app without adding any code to in the website. And finally, I decided to make the necessary changes on my website as well. And this is how I did it:
In App
Created a class
private class JsObject {
#JavascriptInterface
public void shareData(String data) {
Log.v(LOG_TAG, data);
}
}
Add an instance of the new class as Javascript Interface to the WebView with a name
ssWebView.addJavascriptInterface(new JsObject(), "Android");
This instance will be added to the window object of the WebView as Android(name, the second argument of the above function)
In Website
In the website to share data
window.Android && window.Android.shareData("This is the data from website");
I am rewriting my vanilla Xamarin app to use Prism Library.
The current app uses Azure ADB2C for authorisation using this framework.
Android needs to have its parent window set, which is achieved by adding this code into the MainActivity.cs of the Android project:
var authenticationService = DependencyService.Get<IAuthenticationService>();
authenticationService.SetParent(this);
This doesn't work for the Prism app, authenticationService is null. For the record, the DependencyService used here is Xamarin.Forms.DependencyService.
I also tried the example from the Prism docs and put this code into the AndroidInitializer:
public void RegisterTypes(IContainerRegistry container)
{
// Register any platform specific implementations
container.RegisterSingleton<IAuthenticationService, B2CAuthenticationService>("B2CAuthenticationService");
var authService = Container.Resolve<IAuthenticationService>();
authService.SetParent(this);
}
In this code, Container (which is a DryIoC Container) had no definition for Resolve.
For completeness, this is my App.cs RegisterTypes:
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IAuthenticationService, B2CAuthenticationService>();
...
...
}
There are a few wrong assumptions you're making here. To start you'll notice that IContainerRegistry specifically has the name Registry in it to imply we don't want you resolving types here. This is why you don't see the Resolve method on it but rather the IContainerProvider instance.
By design, Prism no longer works directly with the Xamarin.Forms DependencyService as this is a complete anti-pattern. That said if you follow the guidance for registering Platform Specific types you can see how to use the IPlatformInitializer to register platform specific types. It is important to realize here that the IPlatformInitializer is called before RegisterTypes is called in PrismApplication.
What I would suggest is to introduce a IParentWindowProvider like:
public interface IParentWindowProvider
{
object Parent { get; }
}
You can then implement this on Android like:
public class MainActivity : IPlatformInitializer, IParentWindowProvider
{
object IParentWindowProvider.Parent => this;
void IPlatformInitializer.RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterInstance<IParentWindowProvider>(this);
}
}
Then in your Application you might do something like:
protected override void OnInitialized()
{
if(Container.IsRegistered<IParentWindowProvider>())
{
var provider = Container.Resolve<IParentWindowProvider>();
var authService = Container.Resolve<IAuthenticationService>();
authService.SetParent(provider.Parent);
}
}
For more info be sure to check out the relevant docs and sample
https://prismlibrary.com/docs/xamarin-forms/dependency-injection/platform-specific-services.html
https://github.com/PrismLibrary/Prism-Samples-Forms/tree/master/03-PlatformSpecificServices
I am developing an APP with wechat login feature. After getting approval from wechat it will call a custom activity of my NativeScript APP. I am getting response correctly but how can I move to another page instated of the home page after doing some verification. I am using NativeScript + Angular.
Sample code
getJSON("https://api.weixin.qq.com/sns/oauth2/access_token?appid=ID&secret=SECRET&code=" + res.code + "&grant_type=authorization_code").then((res) => {
console.dir(res);
// ===> here I want navigation
}, err => {
console.dir(err);
})
I tried like this:
frame.topmost().navigate("src/app/login/login.component");
but getting error:
JS: Unhandled Promise rejection: Cannot set property '_moduleName' of undefined ; Zone: ; Task: Promise.then ; Value: TypeError: Cannot set property '_moduleName' of undefined TypeError: Cannot set property '_moduleName' of undefined
Please give me some suggestions. Thanks in advance :)
Fire an event from the Activity callback, something like
import { android } from "tns-core-modules/application";
...
public onResp(res: com.tencent.mm.opensdk.modelbase.BaseResp) {
console.log("onResp");
console.dir(res);
androidApp.notify(<AndroidActivityEventData>{ eventName: 'wxapiresponse', object: android, activity: this });
}
In app component, listen to the event
export class AppComponent implements OnInit {
constructor(private ngZone: NgZone, private routerExtensions: RouterExtensions) {}
ngOnInit() {
application.android.on('wxapiresponse', this.wxApiResponse, this);
}
wxApiResponse() {
// making sure the event callback runs inside Angular zone
this.ngZone.run(() => {
this.routerExtensions.navigate(...);
});
}
}
A similar problem cost me an entire Weekend, without knowing how the companion controller .js or .ts files look like I can only recommend you go to the basics / source:
https://docs.nativescript.org/core-concepts/navigation#frame
it helped me find the problem in my code.
If you wrote a custom Page that does not start from a template chances are you wrote the controller too and it's easy to overlook some line that is present in the samples - even thou it looks like you don't need it.
If you paste your controller JS we could understand your context.
For instance, here is piece of code you should definitely include to help you debug:
in your page-you-navigate-to.xml
<Page loaded="onPageLoaded" class="page">
...
</Page>
in your page-you-navigate-to.ts
import { EventData } from "tns-core-modules/data/observable";
import { Page } from "tns-core-modules/ui/page";
export function onPageLoaded(args: EventData): void {
const page = <Page>args.object;
var context = page.navigationContext;
page.bindingContext = context;
console.log("Target page Loaded.");
}
Start your debugger, and step through see where you have null values and read up on those method calls and their parameters.