Custom UIWindow in tvOS makes app unresponsive to keyboard input - tvos

In tvOS, if I use a custom UIWindow instance, the app stops responding to the keyboard and remote in the simulator. Is there any variable or property that I should set on the UIWindow instance?
class AppDelegate: UIResponder, UIApplicationDelegate {
lazy var window : UIWindow? = {
let screen = UIScreen.main
let w = UIWindow(frame: screen.bounds)
return w
}()
// ...
}
The reason is that I need to subclass UIWindow to have custom tint colors and to respond to Dark/Light mode changes via traitCollectionDidChange.
This was in tvOS 10.2.1

You need to call makeKeyAndVisible() in your instance of UIWindow.
https://developer.apple.com/reference/uikit/uiwindow/1621601-makekeyandvisible
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func applicationDidFinishLaunching(_ application: UIApplication) {
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = yourRootViewController
window?.makeKeyAndVisible()
}
}

Apparently one would need to instantiate the storyboard and present the window as well if a custom UIWindow is required. Simply providing a UIWindow instance would not be sufficient.
First, remove the key UIMainStoryboardFile from the Info.plist file of your main app.
Then add in code to in the application did launch handler to:
Instantiate the window and assign to the app delegate's property.
Instantiate the storyboard.
Instantiate the initial view controller
Assign the view controller to the window.
Show the window.
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window : UIWindow?
// ...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = MainWindow(frame: UIScreen.main.bounds)
// We need to instantiate our own storyboard instead of specifying one in `Info.plist` since we need our own custom `UIWindow` instance.
// Otherwise if we just create the custom UIWindow instance and let the system creates a storyboard,
// then the application won't respond to the keyboard/remote (user input).
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
window?.rootViewController = storyboard.instantiateInitialViewController()
defer {
window?.makeKeyAndVisible()
}
// ... All other setup code
}
// ...
}

Related

How to show/hide ng4-spinner-loader on model only angular 4/6/7?

I want to show a loader on pop-up/model once a HTTP request triggers and hide it when all http requests are completed.
I am new to angular and I am not able to find a way to implement it.
My app.html:
<ng4-loading-spinner [threshold]="2000" [template]="template" [loadingText]="'Please wait...'" [zIndex]="9999"> </ng4-loading-spinner>
app.ts
this.spinnerService.show();
I am using import { Ng4LoadingSpinnerService } from 'ng4-loading-spinner';
It depends on the scope. If the request is inside of a component or will be called in a service instance of a component, then you can set an *ngIf with a flag variable on true on the loading spinner before it starts and after the response arrives you set the variable to false:
HTML
<ng4-loading-spinner *ngIf="isLoading"></ng4-loading-spinner>
TS:
getData() {
this.isLoading = true;
this.httpClient.get(...).subscribe(
() => {
this.isLoading = false;
}
);
}

Dialog in Wi-Fi Network Request API

I'm using the new API 29 for connecting the device to a wifi network in an Android 10 device:
private val connectivityManager: ConnectivityManager by inject()
override fun connectToNetwork(ssid: String, password: String) {
val networkRequest = buildNetworkRequest(ssid, password)
val networkCallback =
object : NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
Timber.d("Connected to network $ssid")
}
override fun onUnavailable() {
super.onUnavailable()
Timber.e("Unable to connect to network $ssid")
}
}
connectivityManager.requestNetwork(networkRequest, networkCallback, CONNECTION_TIME_OUT)
}
private fun buildNetworkRequest(ssid: String, password: String) =
NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setNetworkSpecifier(buildWifiNetworkSpecifier(ssid, password))
.build()
private fun buildWifiNetworkSpecifier(ssid: String, password: String) =
WifiNetworkSpecifier.Builder()
.setSsid(ssid)
.setWpa2Passphrase(password)
.build()
A dialog appears with a "Device to use with " with the specified wifi network listed. The dialog has two buttons for "cancel" and "connect". When I click "connect", the device connects to the wifi network (I can see that in the system settings) and the connect button is disabled.
But the dialog does not go away and none of the methods in the requestNetwork callback is invoked. Eventually I reach the specified timeout and another dialog comes with "Something came up. The application has cancelled the request to choose a device".
What is happening here? I want to connect to a network and have the 'onAvailable' or 'onUnavailable' methods in the callback invoked.
After trying almost everything, I finally managed to make this work as expected. Don't know why, but when I went to the device's system settings and forgot the wifi network that I was trying to connect programatically, this started to work.
I'm glad I got rid of the problem, but I don't know what was causing it, and it's a risk that the same thing happens to a future user.
I had the same problem. By trial and error, I found out that this is due to the fact that the request occurs earlier than the network is finally connected. If you send a request after network availability, then everything works. Here is an example of code that solved my problem:
override fun onAvailable(network: Network) {
GlobalScope.launch(Dispatchers.IO) {
while (!isNetworkAvailable(ctx)) {
delay(200)
}
launch(Dispatchers.Main) {
// begin request....
}
}
cm.unregisterNetworkCallback(this)
}
and
private fun isNetworkAvailable(ctx: Context): Boolean {
val connectivityManager = ctx.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val nw = connectivityManager.activeNetwork ?: return false
val actNw = connectivityManager.getNetworkCapabilities(nw) ?: return false
return actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
} else {
val nwInfo = connectivityManager.activeNetworkInfo ?: return false
return nwInfo.isConnected
}
}
Also, before connecting, you have to clear ssid for the correct connection:
private fun removeSsid(ctx : Context, ssid : String, pass : String){
val suggestion = WifiNetworkSuggestion.Builder()
.setSsid(ssid)
.setWpa2Passphrase(pass)
.build()
val wifiManager = ctx.getSystemService(WIFI_SERVICE) as WifiManager
val sList = ArrayList<WifiNetworkSuggestion>()
sList.add(suggestion)
wifiManager.removeNetworkSuggestions(sList)
}

How do I prevent errors about trying to present a controller on a controller which is already presenting?

Once in a while, I get errors like:
Warning: Attempt to present <Controller3> on <Controller1> which is already presenting <Controller2>
I understand that the next controller needs to be presented on the controller that's at the top of the stack (Controller2), not a controller somewhere below (Controller1).
Instead of fixing such errors one-off, how do we design our app to prevent this problem once and for all?
One clean solution to this problem is a navigation controller.
If you can't or don't want to use one, you can easily simulate it on a normal view controller:
extension UIViewController {
var topViewController: UIViewController {
return presentedViewController == nil ? self : presentedViewController!.topViewController
}
// If the topmost view controller is an instance of one of the given classes, it's popped.
// Then, the given view controller, if any, if pushed.
//
// This function can be called on any of the view controllers in the stack.
func pop(ifOneOf: [AnyClass], thenPush: UIViewController? = nil) {
if topViewController.presentingViewController != nil && topViewController.isKindOfOneOf(ifOneOf) {
topViewController.dismiss(animated: false, completion: {
self.pop(ifOneOf: [], thenPush: thenPush)
})
return
}
if thenPush != nil {
push(thenPush!)
}
}
// Pushes the given view controller onto the stack.
//
// This method can be called on any of the view controllers in the stack.
func push(_ child: UIViewController) {
topViewController.present(child, animated: true)
}
}
extension NSObjectProtocol {
func isKindOfOneOf(_ classes: [AnyClass]) -> Bool {
for clazz in classes {
if isKind(of: clazz) {
return true
}
}
return false
}
}
As you can see, this provides push() and pop(), similar to a navigation controller. Further, you can call these methods on any controller in the stack, and it will automatically redirect them to the topmost controller, preventing the error in the question.
This extension also fixes the problem that if you want to dismiss a controller and present another, you need to present only in the completion block, even if you're dismissing without an animation. Otherwise, you'll get the same error as above. This extension fixes all these problems.

Xamarin: open Google Maps App from a Android WebView App

I have created a WebView App with VS2015 / Xamarin.
The WebView loads an external webiste (the pages are not included in the APP).
One of the website pages contains an html link of this kind:
<a href="geo:42.374260,-71.120824">
I would like to make sure Android opens Google Maps App when a user clicks on the link.
I have made use of ShouldOverrideUrlLoading in this way:
public class WebClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading(WebView view, string url)
{
//Click on a georeferenced link
if (url.IndexOf("geo:")>-1)
{
var geoUri = Android.Net.Uri.Parse("geo:42.374260,-71.120824");
var mapIntent = new Intent(Intent.ActionView, geoUri);
StartActivity(mapIntent);
return true;
}
//Click on a generic link
view.LoadUrl(url);
return true;
}
}
Now, problem is, the StartActivity line returns an error: An object reference is required for the non-static field, method, or property 'ContextWrapper.StartActivity(Intent)'
Could you suggest which is the right syntax I should use to avoid this error ?
I solved the problem.
The right way of calling StartActivity is the following:
if (url != null && url.IndexOf("geo:")>-1)
{
var geoUri = Android.Net.Uri.Parse(url);
var mapIntent = new Intent(Intent.ActionView, geoUri);
view.Context.StartActivity(mapIntent);
return true;
}
An interesting thread on the topic is to be found here: How to handle geo: links in webview

how to get attachCamera() when load page,so when people load or refresh page they will ask for for their camera access first?

i have used this code to get camera access how i can load it on page load
private function startVideo():void
{
if (true) // TODO: Recognize no video settings
{
var camera:Camera = Camera.getCamera(cameraIndex.toString());
if (camera)
{
vidMe.attachCamera(camera);
if (outgoingStream)
{
outgoingStream.attachCamera(camera);
}
}
}
else
{
vidMe.attachCamera(null);
if (outgoingStream)
{
outgoingStream.attachCamera(null);
}
}
}
Flash shows the camera request dialog the first time you call attachCamera(). To have a user be asked upfront, before your flash application reaches any functionality, I would suggest adding it upfront in our constructor.
This dummy function puts together a fake NetConnection, and connects it to no server. Going through this upfront will present a user with the camera use dialog while your flash application is loading, thus happening on page refresh or initial load.
private function ensurePermissions() : void {
var unusedNetConnection : NetConnection = new NetConnection()
unusedNetConnection.connect( null );
var ensureCamPermissions : NetStream = new NetStream( unusedNetConnection );
ensureCamPermissions.attachCamera( myCamera );
try {
ensureCamPermissions.close();
unusedNetConnection.close();
} catch( error:Error ) {
// Ignore any errors here
} finally {
ensureCamPermissions = null;
unusedNetConnection = null;
}
}