Executive Summary

This blog post is for Power Apps developers as well as end-users that are looking for a wildcard-like solution for Lookup controls in Microsoft Power App and Dynamics. It begins by describing the problem that a client had with finding records where only part of the name was known. Next, it covers the workarounds that were offered to the client as well as the client’s response. Next, the solution is explained in detail, including the necessary code and instructions for setting up within the Power Apps solution. Finally, a video of the final product is provided demonstrating how the implementation behaves.

The Problem

In 2019, I worked with a client that wanted to migrate a CRM system that had been developed in FileMaker (an Apple Subsidiary). The client was an Apple / Mac user through and through; however, he did like Microsoft Office products. He had researched Power Apps and wanted to migrate his system to it and integrate it with Microsoft Outlook. After spending a few months migrating the system to Power Apps the client was pleased but was a little frustrated with some of the user interface elements. He did not like the way that the Lookup controls functioned and claimed that it was not finding all the records.

After meeting with the client, I figured out the problem. In the example below, we are using fictitious company names. Let us pretend that we are adding an Account for a Contact and are having trouble remembering the exact Account name. We remember that the word “Generic” is in the name, but cannot remember the rest.

If you start by typing “gen” into the Lookup control, notice that the lookup only returns items that begin with “gen”.

Lookup Control

This scenario is what the client was upset about, but I had some workarounds for him.

The Workarounds

I explained to the client that if he entered a wildcard before typing the search term that it would return the results that he was expecting. I also informed him that he could configure and use Relevance Search to locate the name and then enter a full name into the Lookup control. The client found these workarounds to be frustrating and wished that it behaved like his FileMaker solution. He did not want to have to explain wildcards or relevance search to office staff and wanted the lookup to behave how he viewed to be correct.
If you start typing “*gen” into the Lookup control, it will return Accounts that contain “gen”, including “Advanced Generic Corporation”, the company we are looking for.

Relevance Search in Lookup Control

I put together a prototype using a Single Line of Text field with an Auto Complete control like below. When you enter “gen” into the Auto Complete control, it behaves like the client wanted, returning matches that started with or contained the term. The client also likes the fact that it highlighted the text that matched too. My initial prototype had the Lookup and the Autocomplete next to each other. The user could use the OOTB Lookup or use the Auto Complete control. Once a user was selected a record in the Auto Complete control, the Lookup would be resolved based on the record selected. Unfortunately, the client found this to be “clunky” and insisted that it work like user interfaces do on Apple products.

Auto Complete in Lookup Control

The Solution

I started working on a solution making these 2 controls work together to achieve the client’s expectations. The implementation would follow these rules:

  1. On load:
    1. If the Lookup was a value, show it and hide the Auto Complete field (note: the lookup is what is important). Otherwise, show the Auto Complete and hide the Lookup.
    2. Populate a list of all entity values to be used by the Auto Complete.
  2. When the Auto Complete changes, try to resolve it against a pre-populated list of Entity values. In this example, I use the Account name field, but it could be any entity.
    1. If there is a single match, set the Lookup field to that value, show the Lookup and hide the Auto Complete.
    2. If there is more than one match, alert the user and force them to use Lookup.
    3. If no match is found, do nothing.
  3. When the Lookup changes, if an item has been selected, hide the Auto Complete field. If not, then hide the Lookup and show the Auto Complete (i.e. if the user clears the lookup).

The JavaScript

There are a variety of ways the scenario could be achieved. I prefer to establish a single event on the form and wire up events within my JavaScript. I also broke this code up for cleanliness and reusability. For this blog post, I have all the JavaScript in a single file; however, some methods could be moved to a shared.js file and reused across multiple forms. Here is a breakdown of the code:

  • configureForm – a method called when the form is loaded. It sets up the on-change events for the fields and initializes the fields.
  • onAccountAutoCompleteChange – event handler for the on-change event of the Auto Complete textbox. It simply calls onAutoCompleteChanged with the necessary parameters.
  • onAccountLookupChange –calls the onLookupChanged with the necessary parameters and is used to initialize the fields.

These methods could be moved to a shared file if desired:

  • populateAccountSet –populates the accountSet array. It calls populateSet with the necessary parameters.
  • populateSet – populates an array with the desired entity field values. These values are used to resolve against when the user selects a value in the Auto Complete control.
  • onAutoCompleteChanged – performs the logic established above for the Auto Complete field.
  • onLookupChanged – performs the logic established above for the Lookup field.
var accountSet = new Array();
var formContext = null;
function configureForm(executionContext) {
    formContext = executionContext.getFormContext(); 
    populateAccountSet();
    formContext.data.entity.attributes.getByName('new_accounttext').addOnChange(onAccountAutoCompleteChange);
    formContext.data.entity.attributes.getByName('new_account').addOnChange(onAccountLookupChange);
    onAccountLookupChange();
}

function onAccountAutoCompleteChange() {
    onAutoCompleteChanged("new_accounttext", "new_account", "account", accountSet);
}

function populateAccountSet() {
    populateSet("account", "?$select=accountid,name", accountSet, "accountid", "name");
}

function populateSet(entity, query, set, idField, nameField) {
    if (set !== null && set.length > 0) return;
    Xrm.WebApi.retrieveMultipleRecords(entity, query).then(
        function success(result) {
            if (result.entities.length > 0) {
                for (var i = 0; i < result.entities.length; i++) {
                    set.push(
                        new Array(
                            result.entities[i][idField],
                            result.entities[i][nameField]
                        )
                    );
                }
            }
        },
        function (error) {
            Xrm.Utility.alertDialog(error.message, null);
        });
}

function onAutoCompleteChanged(autoCompleteField, lookupField, entityType, dataSet) {
    var value = formContext.data.entity.attributes.getByName(autoCompleteField).getValue();
    if (value !== null) {
        var valueLowerCase = value.toLowerCase();
        var matches = [];
        for (var i = 0; i < dataSet.length; i++) { if (dataSet[i][1] !== null && dataSet[i][1] !== undefined) { if (valueLowerCase === dataSet[i][1].toLowerCase()) { matches.push(dataSet[i]); } } } if (matches.length > 0) {
            if (matches.length > 1) {
                var friendlyEntityType = entityType.replace("new_", "");
                friendlyEntityType = friendlyEntityType.replace("_", " ");
                var alertStrings = {
                    text: "More than one record exists; please use default " + friendlyEntityType + " control."
                };
                Xrm.Utility.alertDialog(alertStrings);
                formContext.data.entity.attributes.getByName(autoCompleteField).setValue(null);
                formContext.data.entity.attributes.getByName(lookupField).setValue(null);
                formContext.getControl(autoCompleteField).setVisible(false);
                formContext.getControl(lookupField).setVisible(true);
            } else {
                var lookupVal = new Array();
                lookupVal[0] = new Object();
                lookupVal[0].id = matches[0][0];
                lookupVal[0].name = matches[0][1];
                lookupVal[0].entityType = entityType;
                formContext.data.entity.attributes.getByName(lookupField).setValue(lookupVal);
                onLookupChanged(autoCompleteField, lookupField);
            }
        }
    } else {
        formContext.getControl(lookupField).setVisible(true);
    }
}

function onLookupChanged(autoCompleteField, lookupField) {
    var lookupVal = formContext.data.entity.attributes.getByName(lookupField).getValue();
    if (lookupVal !== null && lookupVal !== undefined) {
        formContext.getControl(autoCompleteField).setVisible(false);
        formContext.getControl(lookupField).setVisible(true);
    } else {
        formContext.getControl(autoCompleteField).setVisible(true);
        formContext.getControl(lookupField).setVisible(false);
        formContext.data.entity.attributes.getByName(autoCompleteField).setValue(null);
    }
}

function onAccountLookupChange() {
    onLookupChanged("new_accounttext", "new_account");
}
 

Updating the PowerApps Solution

For this script to be used, you must first create a Web Resource for it. In this example, I created one called accountwildcard.

Create a Web Resource

Next, you need to add an event handler function to the OnLoad event; be sure to check the box Pass execution context as the first parameter. Add the library to the form and tell it to call the configureForm method for the OnLoad event.

Adding and Event Handler Function

Your form properties should look like this then you are done.

You will also want to make sure the labels for both fields are the same to make the transition look clean.

Make sure both labels are the same

The Final Product

As you can see below, the transition is smooth and seamless. When you type, the Auto Complete control serves up wild-carded results. After hitting tab or moving outside of the control, the Lookup is populated with the match and the Auto Complete is hidden. If you clear the Lookup, it hides itself and shows the Auto Complete again.

This is a specialized solution and I would recommend steering your client towards using a wild card in Lookup control; however, if you have a client that is insistent on this functionality, I hope this saves some time. The client I did this for is pleased with the results and has had no issues with it since.

In this blog post, I would like to introduce and explain Azure’s new service offering for Application Gateway Ingress Controller (AGIC) for Azure Kubernetes Services (AKS). AKS is gaining momentum within the enterprise and Microsoft has been refining the offering to drive adoption. This feature piques my interest because of its capability to improve latency and allow for better application performance within AKS Clusters. I will first discuss the basics of the application gateway Ingress controller. Then, I will dive into the underlying architecture and network components that can provide performance benefits. In addition, I will also explain the improvements and differences this offers over the already existing in-cluster ingress controller for AKS. Finally, I will discuss the new application gateway features that Microsoft is developing to refine the service even further.

In definition, the AGIC is a Kubernetes application that is like Azure’s L7 Application Gateway load balancer by leveraging features such as:

  • URL routing
  • Cookie-based affinity
  • SSL termination or end-to-end SSL
  • Support for public, private, hybrid web sites
  • Integrated Web Application Firewall (WAF)

The controller runs in its own pod on your AKS. It monitors and communicates routing changes to your AKS through the Azure Resource Manager to the Application Gateway which allows for uninterrupted service to the AKS no matter the changes. Below is a high-level design of how the AGIC is deployed along with the application gateway and AKS, including the Kubernetes API server.

Azure Resource Manager

Next, let’s take a closer look at the AGIC and breakdown how it differs from that of the already existing In-Cluster Ingress Controller. The image below shows some distinct differences between the two. The in-cluster load balancer performs all the data path operations leveraging the AKS clusters compute resources, because of this it is competing for the same resources as the application in the cluster.

In-Cluster Ingress Controller

In terms of networking features, there are 2 different models you can leverage when configuring you AKS. They are Kubenet and Azure CNI:

Kubenet

This is the default configuration for AKS cluster creation, each node receives an IP address from the Azure virtual network subnet. Pods then get an IP address from a logically different address space to that of the Azure virtual network subnet. They also use Network Address Translation (NAT) configuration to allow the Pods to the communication of other resources in the virtual network. If you want to dive deeper into Kubenet this document is very helpful. Overall these are the high-level features of Kubenet:

  • Conserves IP address space.
  • Uses Kubernetes internal or external load balancer to reach pods from outside of the cluster.
  • You must manually manage and maintain user-defined routes (UDRs).
  • Maximum of 400 nodes per cluster.
  • Azure Container Networking Interface (ACNI)

The other networking model is utilizing Azure Container Networking Interface (ACNI). This is how the AGIC works, every pod gets an IP address from their own private subnet and can be accessed directly, because of this these IP addresses need to be unique across your network space and should be planned in advance. Each node has a configuration parameter for the maximum number of pods that it supports. The equivalent number of IP addresses per node is then reserved upfront for that node. This approach requires more planning, as can otherwise lead to IP address exhaustion or the need to rebuild clusters in a larger subnet as your application demands grow.

Azure Virtual Network

From the Azure documentation, they also outline the advantages of using ACNI.

Azure CNI

Pods get full virtual network connectivity and can be directly reached via their private IP address from connected networks.
Requires more IP address space.

Performance

Based on the network model of utilizing the ACNI, the Application Gateway can have direct access to the AKS pods. Based on the Microsoft documentation because of this, the AGIC can achieve up to a 50 percent lower network latency versus that of in-cluster ingress controllers. Since Application Gateway is also a managed service it backed by Azure Virtual machine scale sets. This means instead of utilizing the AKS compute resources for data processing like that of the in-cluster ingress controller does the Application Gateway can leverage the Azure backbone instead for things like autoscaling at peak times and will not impact the compute load of you ASK cluster which could impact the performance of your application. Based on Microsoft, they compared the performance between the two services. They set up a web app running 3 nodes with 22 pods per node for each service and created a traffic request to hit the web apps. In their findings, under heavy load, the In-cluster ingress controller had approximately 48 percent higher network latency per request compared to the AGIC.

Conclusion

Overall, the AGIC offers a lot of benefits for businesses and organizations leveraging AKS from improving network performance to handling the on the go changes to your cluster configuration when they are made. Microsoft is also building from this offering by adding more features to this service, such as using certificates stored on Application Gateway, mutual TLS authentication, gRPC, and HTTP/2. I hope you found this brief post on Application Gateway Ingress-Controller helpful and useful for future clients. If you want to see Microsoft’s whole article on AGIC click here. You can also check out the Microsoft Ignite video explaining more on the advancements of Application Gateway.

Getting started with Power Apps Portals is simple; knowing how to customize the portal to your needs and requirements will require more guidance. This guide will take you through important links, tips, tricks, gotchas, and other information you will need to level up on your Power Apps Portals.

Getting Started

Here is the simple part I was talking about in the introduction. You will need a trial environment or your own environment with access to portals to follow these steps.

  1. Navigate to https://make.powerapps.com
  2. Click Create
  3. Select Portal from Blank

Reference: https://docs.microsoft.com/en-us/powerapps/maker/portals/create-portal

Portal Designer and Portal Management App

Two types of applications are created when Portal from Blank is selected; the portal and the portal management app, a model-driven app. To better customize your portal, since the designer is limiting, I have included resources such as the XrmToolBox for code editing and Liquid Language for access to server-side code.

Portal Designer

This is the WYSIWYG portion of the portal. Some of its capabilities include:

  1. Creating webpages and deleting a component
  2. Customizing page layout, templates, sections, colors
  3. Configuring forms, lists, iframe, image, text, breadcrumb
  4. Viewing the live site

Portal Designer Dashboard

Reference: https://docs.microsoft.com/en-us/powerapps/maker/portals/compose-page

XrmToolBox

The portal designer is limiting your ability to fully customize your site. XrmToolBox has a plugin that will allow you to edit code attributes to your portal.
Download XrmToolBox https://www.xrmtoolbox.com/ to get started.

Plugin: Portal Code Editor

  1. Go into edit mode in the portal
  2. Select a webpage
  3. Select the Portal Code Editor plugin in XrmToolBox
  4. Connect your portal using the URL
  5. Select the webpage from the left column
  6. Make a code change
  7. Hit CTL+S to save
  8. Hit CTL+U to update the portal page
  9. Go back to the Portal
  10. Click Browse Website to see the changes

Liquid Language

Liquid is an open-source template language that can be used to add dynamic content to portals without needing direct access to the server-side code. Liquid can write scripts that take data from databases and create html on a webpage. Liquid can be categorized into objects, tags, and filters.
Below is an example of how to load a Power Apps entity by ID. If the entity exists, an entity object will be returned. If an entity with the given ID is not found, null will be returned.

Liquid Language

Reference: https://docs.microsoft.com/en-us/powerapps/maker/portals/liquid/liquid-objects

Portal Management App

The portal management app that is built alongside the portal app is your advanced configuration. This model-driven app will give you access to the below entities for portal configuration.

Portal Management App

Active Websites

Reference: https://docs.microsoft.com/en-us/powerapps/maker/portals/configure/configure-portal

Authentication

There are several options to configure authentication for your portal. Local authentication will store users inside the contact record of the Common Data Service. External authentication will be handled by a third-party provider such as Yahoo! and Google. OAuth 2.0 can be configured to use Twitter, Facebook, LinkedIn, and Microsoft.
Reference: https://docs.microsoft.com/en-us/powerapps/maker/portals/configure/configure-portal-authentication

Configuration

There are two options to export your portal configuration data and migrate the configuration elsewhere. You can use the XrmToolBox Plugin called Portal Records Mover or you can download the Configuration Migration tool and following these instructions: https://docs.microsoft.com/en-us/powerapps/maker/portals/admin/migrate-portal-configuration

A few things to note if using the Configuration Migration tool:

  • You’ll be able to import your existing configuration once you create a new portal app manually, this tool will not create a new one automatically.
  • When this new portal is hosted, it is configured with all the web pages visible by default and requires manual removal of the unwanted pages.
  • Code added separately is not exported and needs to be configured again.
  • The export creates a reference in the configuration for forms and lists, but the entity is not imported directly. The related entities need to be imported into the environment before starting this process.

Admin Center

To access your portal’s admin center, find your portal under apps, click on the ellipses for your portal, click Settings, click Administration. This will give you access to your portal’s name, the type, changing your base URL, adding a custom domain name using an SSL certificate, ability to download a public key for Live Assist, diagnostic logging, enable maintenance mode, and even resetting your portal.
Reference: https://docs.microsoft.com/en-us/powerapps/maker/portals/admin/admin-overview

Other Notes:

  • Once you have selected a form to be displayed on your portal page, the quick view form might appear on your live site instead.
  • When writing HTML code in Edit mode, the code is saved but deleted the id from the HTML elements. This works fine when completed in play mode as an administrator.
  • When a reference is created on the entity form directly from the Edit mode, it throws an error. When the Create New mode is selected to attach the form, the newly created form must be selected again under the already existing tab.
  • Sometimes the portal reloads the code editor when in Edit mode, this removes the changes that have not yet been saved.

Licensing

  • External users (authenticated) will be $200 for 100 logins/month.
  • External users (anonymous) will be $100 for 100,000 web page views/month.
  • Internal users can access portals using a PowerApps per app or per user plan as per above.
Even before COVID-19, work from home/anywhere was a growing trend across many industries. Being mutually beneficial –  employers could recruit and retain top talent due to the convenience of remote work, flexibility around time, domestic responsibilities, and geographic flexibility – which is all very appealing to employees, not to mention the lack of a commute. For companies, it not only lowers organizational costs but also boosts employee productivity, according to the recent research at Harvard Business School. Now, the coronavirus pandemic has given a big push to work from home and it has caused almost all firms to deploy the work from home practices for employees, and quickly. Companies with a distributed workforce are already in a better position to keep operations running. For employees who have become accustomed to working from home, there is nothing new. However, those who are doing it for the first time may feel disconnected, distracted, and uncertain about the future of their job. There are many potential challenges employees may face while working from home requiring ground rules to stay motivated, organized, and productive.

Working From Home

Your Work Items

When you work from home there is no clear delineation between work and home. When work and home are in the same place, it’s hard to make the shift from professional to personal. You may start work from sun up to sundown or get distracted because you feel you have too much time. You may find yourselves working more hours and logging in work time in the evening and weekends. As Parkinson’s law states, work expands to fill the time available for its completion. This results in anxiousness and you may become frustrated with yourself.

Set up a routine, it is not necessary to follow nine-to-five, you may be in control and have flexible working hours, which may be good in many cases e.g., some people are not made for the morning even if some are physically there at 9 AM they don’t start being productive until later in the day. Studies have shown by allowing for this, productivity is greatly increased. It’s a company investment. No matter what your working hours are, set reasonable limits to work hours and determine how to meet work requirements and still preserve personal time.

Create a daily ritual e.g., begin your morning with exercise, read a newspaper, or actually enjoy your tea or coffee. Prepare for the day! You may also like to get dressed before starting work. Many find it very useful as it helps to switch from personal to professional. Similarly, after long work hours when you switch back to personal life you may take a shower, as a signal that your workday is over.

Your Work Area

Unfortunately, home life comes with distractions. It’s easy to become distracted by the TV, what’s happening on social media or visitors popping over! If you have children or pets at home setting up limits can be especially tough. In addition, there are always other household errands that need doing, but you are best to put these interruptions off until after your structured working hours as if you were not at home.

Ideally, you will work in a room by yourself behind a closed door, but not everyone has a designated home office. However, it’s critical to have a separate space that you use just for work and not for other activities. Working from home can greatly affect your mind, but creating a workspace of some kind allows you to simulate your brain to recognize it. When you walk into that space, you subconsciously know I am turning on work. When you end for the day, you can say, “I have finished working and now into my evening.” Desk clutter and digital clutter can also be distracting. Having a clean space will result in a clean mind. Ensure family members understand that although you are at home, you are working. You must set working boundaries, if possible. If you have frequent phone or video meetings, you may need a closed-door policy. If you do allow children in your office, make some simple (but fun) rules to follow like knock before entering or never enter when you are on the phone or to remember to use quiet voices.

Your Goals

One of the hardest aspects of working from home is concentrating on the task at hand. Although you set up a workspace and time schedule, you need to set goals for what you will accomplish during that time. You should plan your work before you start the day. Create a task list and keep it in front of you, cut those completed tasks, it will give you a sense of accomplishment. Prioritize your tasks and schedule time slots including extra time in between to cater for contingencies like unforeseen work calls and new tasks, etc. When you begin a task, try to continue it until the end. Do not switch between tasks or get pulled away by your need to respond to a new email. Multi-tasking is a productivity killer, but we all have to multi-task sometimes, the trick is to do it effectively in a limited way.

Your Communication Tools

Today we can work effectively from home, if not more because of the communication tools. There are tons of tools to create more productivity around your communication with your team like email, group chat/call tools like Microsoft Teams, Google Hangouts, etc. Good communication is an essential part of any successful home working arrangement. Besides textual exchanges, we should use multiple forms of communication to avoid any misunderstanding. We need to understand that phone calls, emails, and meetings are still part of our workday just as they are in the company offices. Without open communication, collaboration becomes difficult.

Your Time with your Loved Ones

Many of us love to work in solitude but after some time we may start to feel lonely. If you don’t have anything to look forward to, it may be difficult to keep yourself motivated. So, it is important that you schedule (virtual) time with family and friends. Reward yourself with a book, time with your family, or loved ones.

With freedom comes added responsibilities, it is not easier to work from home, just different location. Happy WFH!

Progressive Web Apps (PWAs)

As an introduction to PWAs, let’s build a mobile app the fastest, easiest way, using the Progressive Web App model. A PWA is both a mobile app and a website. Built with HTML, CSS, and javascript, the website can be viewed as either a website on your laptop or a mobile app on your phone or tablet. No need to develop a separate native app for every platform!

Creating Website with Addition of JavaScript files app will behave "app-like"

What does “App-like” mean? PWAs can work on your phone or tablet so much like a native app, it is hard to see the difference. Twitter produces both, a native app you can find in your app store, and a PWA you can “install” from browsing to twitter.com. Check out images of each:

Natie iPhone App vs. PWA - Twitter.com

A PWA can even access device hardware like the camera or microphone! They can produce push notifications and work offline. How does it do these things? It’s all in the site javascript and structure. It is the best way to build a mobile app. Let’s see how this works.

PWA Goals

To work like a native app, the PWA website has to:

  • Work offline
  • Perform fast
  • A display like a native app, responsive, with no browser UI visible
  • Startup from an icon on your home screen

Advantages

Why and when would we want to create a PWA?

  • Fast deployment: Avoids submission to the app stores for review and approval
  • Quicker updates are published immediately because it is served on the web and so no need to wait for the user to download to their device
  • More discoverable: Users can find and link to it via search engines
  • Secure: They use the browser security model, so providing the security of operating within the web browser context
  • Connectivity independent: PWAs can function on very low bandwidth or no connection at all.
  • Installable: Like a mobile app, this is opened from an icon on the home screen.
  • Notifications: Can send push notifications to the device like a native app
  • Hardware access: Can use the mobile device camera, microphone, etc.

Progressive?

“Progressive” in the name refers to that it will work and display in all browsers to a varying degree. Because it is browser dependent, it is affected by the capability of which browser the user has, and what version. PWAs have a goal to display and function at least minimally in all browsers, regardless of version. As such they become progressively more feature-rich in newer versions.

History

The Progressive Web App concept was created by Google in 2015. Its roots go even further back though, to Steve Jobs’ introduction of the iPhone in 2007. He declared back then that development for the iPhone would be done in html5 and have no SDK. His vision was undermined by iPhone jailbreaking which forced Apple to retreat to having an SDK and app store. But the concept lived on and is seeing increasing life in the PWA model.

Let’s Build One

We will look at how to use a manifest and service worker to create a very simple application that provides the features of a PWA. This is exciting to see on your phone, in action!
In your development environment, create a folder in your local for your project, name it anything, and add subfolders for js and images, and an index.html file:

Application Providing Features of a PWA

This demo was edited VS Code and the site was served on IIS in Windows but any favorite editor and dev web server will be fine. Node.js http-server is another easily available web server you could use. The file index.html should look like this, to start. We’ll add more later:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Hello My Phone</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body class="fullscreen">
  <div class="container">
    <h1 class="title">Hello My Phone!</h1>
  </div>
</body>
</html>

Run the app now in your webserver to see how it is shaping up. So far this is just a web app. Push it to your GitHub Pages repo to see it run on the web. To add mobile app functionality, we need to add two files, a Service Worker and a manifest.

Service Worker and Manifest

These change the app from a simple web app to one that can behave like a mobile app, a Progressive Web App. The Service Worker is the middleman between the web page and content. It contains the code to determine whether to deliver static, cached content or dynamic data based on whatever criteria you want, typically whether it finds a connection to the internet or not. The manifest is a json file that provides properties to the browser such as which page to load when the app is first opened, or what is the title of the app. Both the manifest and the service worker live on the root directory for access.

Create a file named manifest.json in the root of the site, and add the following:

{
  "name": "My Phone App",
  "short_name": "My App",
  "icons": [{
    "src": "images/phoneapp-icon-128.png",
      "sizes": "128x128",
      "type": "image/png"
    }, {
      "src": "images/phoneapp-icon-144.png",
      "sizes": "144x144",
      "type": "image/png"
    }, {
      "src": "images/phoneapp-icon-152.png",
      "sizes": "152x152",
      "type": "image/png"
    }, {
      "src": "images/phoneapp-icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    }, {
      "src": "images/phoneapp-icon-256.png",
      "sizes": "256x256",
      "type": "image/png"
    }, {
      "src": "images/phoneapp-icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }],
  "description": "Sample Progressive Web App",
  "lang": "en-US",
  "start_url": "index.html",
  "display": "standalone",
  "background_color": "white",
  "orientation": "any",
  "theme_color": "white"
}

Link the manifest file in your index.html file, in the head section:

&lt;link rel="manifest" href="/manifest.json"&gt;

Service Worker

To add the service worker, create a file in the root folder named sw.js. Add the following to it:

var cacheName = 'phoneApp';
var filesToCache = [
  './',
  './index.html',
  './css/style.css',
  './js/main.js'
];

/* Start the service worker and cache files in filesToCache */
self.addEventListener('install', function(e) {
  e.waitUntil(
    caches.open(cacheName).then(function(cache) {
      return cache.addAll(filesToCache); 
    })
  );
});

/* Serve cached content when offline */
self.addEventListener('fetch', function(e) {
  e.respondWith(
    caches.match(e.request).then(function(response) {
      return response || fetch(e.request);
    })
  );
});

Adding a service worker is the first step towards enabling the following progressive web app features:

  • Offline performance
  • Push notifications
  • Add icon to the home screen

Our service worker file caches specifically named pages to be able to serve them from the cache when the device is offline. The file must be placed in the app root to allow it access to all of the app’s files. Service workers only have permission to access files in their same-level directory and subfolder. In the first function of the sw.js, the first item in the variable filesToCache is “./” because that allows caching of our index.html. The cacheName variable adds the content to the browser cache to be available to be called from the javascript. The following function fetches the cached content. This file works for our demo but to properly fortify the app with error handling and to see what else the service worker can do, check out PWABuilder. It is a handy file generator for your PWA, but even if you don’t use the generator, just looking at this site sums nicely what you can define in the manifest and service worker and what they can do. For now, we’ll keep our service worker simple.

Register the Service Worker

We need one more file to call, or “register”, the service worker. Create a javascript file in your js folder named main.js. This tells the browser where to find your service worker JavaScript file. Add the following:

window.onload = () =&gt; {
‘use strict’;

if (‘serviceWorker’ in navigator) {
navigator.serviceWorker
.register(‘./sw.js’);
}
}

Link to main.js at the bottom of index.html by adding a script link before the closing body tag:

<script src="js/main.js"></script>

App Icons

We need to provide icons that various displays can use, to support the mobile app experience of having an app icon on the device. Images of varying sizes are defined in the manifest above. We will put these images in the Images folder to be available to display the home screen icon when users “install” your app. Sample images for these and for favicon.ico are included in the downloadable files (link below). The file structure looks like this now:

Production of App Icons

Add Responsive HTML

To see the best effect of your sites’ web vs mobile view, add some responsive elements and styles to index.html. This example uses the free W3 Schools templates. It links to their online w3.css stylesheet for responsive styles. A link to the W3 Schools templates is included in the links at the end of this post along with a link to download the project files.

With the addition of the html below, our site now has navigation and layout that changes based on the viewport. The HTML now looks like this:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>PWA Sample</title>
  <link rel="manifest" href="manifest.json">
  <link rel="stylesheet" href="css/style.css"> <!-- optional -->
  <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="theme-color" content="white"/>
  <link rel="icon" href="favicon.ico" type="image/x-icon" />  
<link rel="apple-touch-icon" href="images/pwa-icon-152.jpg">   
<meta name="theme-color" content="white"/>  
<meta name="apple-mobile-web-app-capable" content="yes">  
<meta name="apple-mobile-web-app-status-bar-style" content="black"> 
<meta name="apple-mobile-web-app-title" content="Hello World"> 
<meta name="msapplication-TileImage" content="images/pwa-icon-144.jpg">  
<meta name="msapplication-TileColor" content="#FFFFFF">
</head>
<body class="fullscreen">
<!-- Sidebar/menu -->
<nav class="w3-sidebar w3-collapse w3-top w3-large w3-padding" style="z-index:3;width:300px;font-weight:bold; background-color:#ddd;" id="mySidebar">
  <a href="javascript:void(0)" onclick="w3_close()" class="w3-button w3-hide-large w3-display-topleft" style="width:100%;font-size:22px">Close Menu</a>
<div class="w3-container">
<div style="width: 120px; height:119px; background-color: #fff;">
  <img src="images/logo.jpg"</div>
</div>
<div class="w3-bar-block">
    <a href="#" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">Home</a> 
    <a href="#sectionOne" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">Section One</a> 
    <a href="#sectionTwo" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">Section Two</a> 
    <a href="#simpleForm" onclick="w3_close()" class="w3-bar-item w3-button w3-hover-white">Contact</a>
  </div>
</nav>
<!-- Top menu on small screens -->
<header class="w3-container w3-top w3-hide-large w3-grey w3-xlarge w3-padding">
  <a href="javascript:void(0)" class="w3-button w3-grey w3-margin-right" onclick="w3_open()">☰</a>
  <span>My Mobile App</span>
</header>

<!-- Overlay effect when opening sidebar on small screens -->
<div class="w3-overlay w3-hide-large" onclick="w3_close()" style="cursor:pointer" title="close side menu" id="myOverlay"></div>
<!-- !PAGE CONTENT! -->
<div class="w3-main" style="margin-left:340px;margin-right:40px">
<!-- Header -->
  <div class="w3-container" id="showcase">
   <h1 class="w3-xxlarge">My Mobile App</h1>
   <h1 class="w3-large w3-text-gray"><b>Lorem dolor etc!</b></h1>
  </div>
<!-- Photo grid (modal) -->
<div class="w3-row-padding">
<div class="w3-half">
      <img src="images/circle1.jpg" style="width:100%" onclick="onClick(this)" alt="It's a photo">
      <img src="images/circle4.jpg" style="width:100%" onclick="onClick(this)" alt="It's a photo">
  </div>
<div class="w3-half">
      <img src="images/circle3.jpg" style="width:100%" onclick="onClick(this)" alt="It's a photo">
      <img src="images/circle2.jpg" style="width:100%" onclick="onClick(this)" alt="It's a photo">
  </div>
</div>
  <!-- Top Section -->
<div class="w3-container" id="sectionOne">
<h1 class="w3-large w3-text-gray"><b>Section One</b></h1>
  Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
</div>
<!-- Section of Text -->
<div class="w3-container" id="sectionTwo">
<h1 class="w3-large w3-text-grey"><b>Section Two</b></h1>
 Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
<b>All The Things:</b>
  </div>
  <!-- Images in Boxes -->
<div class="w3-row-padding w3-grayscale">  
  <div class="w3-col m4 w3-margin-bottom"> 
   <div class="w3-light-grey">     
     <div class="w3-container">
        <h3>Thing 1</h3>Thing 1 Phasellus eget enim eu lectus faucibus vestibulum. Suspendisse sodales pellentesque elementum.
     </div>
   </div>
</div>
<div class="w3-col m4 w3-margin-bottom">
  <div class="w3-light-grey">
    <div class="w3-container">
       <h3>Thing 2</h3>Thing 2 Phasellus eget enim eu lectus faucibus vestibulum. Suspendisse sodales pellentesque elementum.
    </div>
  </div>
</div>
<div class="w3-col m4 w3-margin-bottom">
   <div class="w3-light-grey">
     <div class="w3-container">
       <h3>Thing 3</h3>Thing 3 Phasellus eget enim eu lectus faucibus vestibulum. Suspendisse sodales pellentesque elementum.
     </div>
    </div>
  </div>
</div>
<div class="w3-container" id="simpleForm" style="margin-top:75px">
My Form   
   <form action="/myformaction.js" target="_blank">
     <div class="w3-section">
      <label>Name</label>
      <input class="w3-input w3-border" type="text" name="Name" required>
     </div>
     <div class="w3-section">
       <label>Email</label>
       <input class="w3-input w3-border" type="text" name="Email" required>
     </div>
     <div class="w3-section">
       <label>Message</label>
       <input class="w3-input w3-border" type="text" name="Message" required>
     </div>
     <button type="submit" class="w3-button w3-block w3-padding-large w3-grey w3-margin-bottom">Send Message</button>
   </form>
</div>
<!-- End page content -->
</div>
<script>
// Script to open and close sidebar
function w3_open() {
  document.getElementById("mySidebar").style.display = "block";
  document.getElementById("myOverlay").style.display = "block";
}

function w3_close() {
  document.getElementById("mySidebar").style.display = "none";
  document.getElementById("myOverlay").style.display = "none";
}
</script>
<script src="js/main.js"></script>
</body>
</html>

Install On Your Phone

Upload the files to a WebHost or browse to the localhost IP address in your wifi network, to try it on your phone. On the iPhone, browse to the URL and then choose the share button.

iOS Share button

From the context menu, choose “Add to home screen”. Android devices make this even easier by displaying a default button. See links at the end of this post for options to install on Android and other devices.

Summary

Progressive Web Apps offer the best of native mobile apps combined with the advantages of web development. Over time, the increasing embrace of PWA techniques by developers could bring Steve Jobs’ vision for smartphone applications to reality: no need for phone SDKs or and native app deployments or app store constraints, and a world in which web technology can be used to create mobile apps that are faster to build with every bit of functionality equal to their native counterparts.

Links

More about installation of a PWA on other OSs:
With the COVID-19 epidemic, businesses everywhere are rushing to meet the demand for remote work solutions and enable “business as usual” as much, and as quickly, as possible. While many have practiced and planned for business continuity and disaster recovery (BC/DR) scenarios, no one could have ever imagined the current challenges we’re all facing. For some organizations that have already begun their Modern Workplace journey, the change may not be drastic. But many others are scouring Google for technology, solutions, and integrators to support the transition to a productive remote workforce. As we band together to fight COVID-19, keep our people productive and employed, and do what we can for the economy, we wanted to share our knowledge and experience on how organizations can successfully navigate this new landscape. Many of you are searching for technology answers to enable remote work. We hope these tips help guide you.

Deploy Microsoft Teams

Our number one recommendation is to deploy and enable Microsoft Teams. Your employees are most likely accustomed to working in an office together, meeting in conference rooms, walking over to someone’s cube or office for a quick question. With the rapid transition to remote collaboration, a platform must be provided for them to engage and collaborate more intuitively than email communication. Teams will provide your organization with instant messaging, group or team-based chats, a platform for organization-wide updates or announcements, and audio/video conferencing (PC-based included in trials).

While you may not have the time for proper end-user training to ensure that all employees can fully leverage the platform and all of its enhancements, the basic requirements required to enable a remote workforce are intuitive enough that users can onboard quickly. Additionally, Microsoft provides some resources that can be integrated into channels that allow users to train as they go with the product.

Keep in mind, while it is possible to set up your trial tenant and go live, there are some baseline governance options you’ll want to consider, especially with the default option that all users can create new groups and quickly clutter your address list. AIS has even developed a Teams Rapid Adoption engagement so that we can help organizations quickly deploy Teams but also ensure it meets standard governance and compliance requirements before going live.

Addressing Identity Later

A quick note about a rapid deployment: if you don’t have time or your organization isn’t ready to get Azure AD Connect deployed for single sign-on (SSO) functionality prior to deployment, that’s OK! The solution can be deployed later and made to link up to existing Azure AD user accounts and override their Azure AD password with their on-premises Active Directory identities and authentication. There are ways to plan to move from a cloud-only identity to a “hybrid” identity using directory synchronization. In this scenario by verifying proper Active Directory attributes such as UPNs and SMTP addresses, you can ensure that when you do enable synchronization all of your AD objects correspond to Office 365 objects and they “match” to merge the identities. In some cases for Exchange, you might need to rely on an additional method to merge mailboxes using scripting beyond identity matching.

Leverage Existing Office 365 Deployments: SharePoint, OneDrive, & Azure AD

Many organizations have already taken their first steps into the Office 365 ecosystem, most commonly through a mailbox migration to Exchange Online or SharePoint for file storage, and have organically begun using Teams. What we find a lot of the time is that many organizations do not utilize the full range of tools and services available to them. For instance, your organization may have multiple applications configured for Azure AD SSO. Some of these applications may only be accessible inside of your LAN, and with everyone now working remotely you may be seeing some strain on your VPN appliances and datacenter bandwidth. Those applications you have configured for internal use could potentially leverage a solution like Azure AD Conditional Access to temporarily allow external connections to access them but require MFA or you could require that to access an application the user must be on a corporate device.

Sticking with application access, for organizations that have Azure AD premium, the Azure AD Application Proxy provides a solution to make applications that are hosted internally accessible outside of your data center without opening any firewall ports. The product simply needs port 443 out and acts as a reverse proxy of sorts so that your users can access any internally hosted applications through the same Azure AD SSO experience as SaaS applications.

File servers may be another reason for users to be required to remote in. Perhaps your organization was considering a migration of content to SharePoint Online or OneDrive for Business and now is the time to accelerate those plans or explore solution providers who have offerings to assist organizations to rapidly adopt or migrate to either platform.

Zero-Touch Deployment with Intune & AutoPilot

With so many employees working from home consistently, device management and deployment are quickly going to become a major consideration. Administrators need to continue to ensure that corporate devices are securely configured, compliant with your policies and have all the applications end-users need to continue to function. Microsoft Intune can help by setting your compliance and configuration policies from a cloud-based endpoint management solution to handle your workstation and mobile devices. Applications can be quickly packaged and deployed to your end-users across your devices.

Perhaps your organization didn’t finish your desktop to laptop rollout before the outbreak, or your users require a new device due to an accidental spill, or maybe your organization is still hiring and onboarding. Getting new devices into an end user’s hands has typically involved some touch from your IT department before they can be provided to an end-user. Applying a corporate image, joining to the domain, installing baseline apps; these are all things Intune can take care of for you with a zero-touch approach. With AutoPilot, it’s possible to have devices shipped right from your vendors to an end user’s home and once the device is powered on and connected to the internet for the first time, Intune takes over and applies your policies and applications.

To read more on Microsoft Teams Adoption, please reference this blog post.

Additional Remote Resources from Microsoft:

MICROSOFT TEAMS GOVERNANCE QUICK START
Hit the ground running through a guided workshop with Office 365 governance specialists. We'll work with your team to enable the appropriate governance solutions.

The current approach of mass quarantining is to “flatten the curve.” However, learning about how the virus has spread and how it can return with the eventual restoration of our economy is something that is still blurry. Recent work by Abbott Labs (among others) shows that shortening testing times and mass-producing testing kits at affordable prices look promising.

However, despite the advancements by Abbott Labs, it is unattainable to test everyone in America. As of today, April 5th, we have tested, on average, one in every two hundred Americans. This can be compared to a test rate like South Korea. The ramp-up in testing has not allowed moving closer to reopening our economy.

Some have proposed the idea of checking for antibodies. This test would suggest immunity to the virus because of a prior infection. The logic behind this is that people that have these antibodies can safely return to work and take on the critical tasks needed to restart the economy. Nonetheless, recent news from across Asia warns us that patients previously diagnosed with COVID-19 are being readmitted to hospitals after testing positive for the virus again.

So as it stands, our current approach to mass quarantining from what the media outlets have predicted to be up to twelve-months is not only slow but is also pushing us down a path of economic damage. If that continues, this may be difficult to recover from. Scaling up and developing new methods of testing that check for antibodies, while advantageous, will not be by itself enough to reopen our economy.

An Aggressive Data-Vision Approach is Needed

An aggressive data-driven approach to understand how COVID-19 is spreading should be suggested. Based on these insights, we can demarcate safe zones where seminal economic activity can be reinstituted with minimal risk. There are two aspects of this approach:

  1. We can develop more in-depth insights into how the virus is spreading. We must acknowledge that mass quarantining alone is not the best approach.
  2. Based on the insights we develop, we may be able to open parts of our country once again, with a measure of confidence based on the data.

Ultimately, the solution boils down to data and computation problems. Imagine if we took the phone numbers of everyone infected with COVID-19 (of course, using an anonymized identifier rather than the actual phone numbers for protecting the privacy of folks involved). Then, using cell tower data to gather movement of those individuals based on their smart-phones, we will perform location and time-based searches. This would determine who might have come in contact with infected persons in the last forty-five days. Then, we will algorithmically place the search results dataset into bands based on the degree of overlap (time and location). This algorithm will be able to eliminate instances of location proximity where there is minimal chance of spread for example, at a traffic intersection. Conversely, this algorithm will accord a higher weight to location proximity based on where there is a bigger chance of the virus spreading. For example, at a coffee shop or workplace. All these factors will lead to the calculation of a risk factor. Individuals that meet the high-risk criteria will be notified. Any individual who receives the notification will be instructed to self-quarantine immediately. We can go further and penalize them if they don’t follow the suggestion, using the cell phone data. These individuals should be sent a self-test kit on a priority basis.

If these individuals test positive, their immediate family would then receive instant notification to also self-quarantine. The location in which this individual came into contact with the COVID-19 infected patient that initiated this search will be notified as well. If they test negative, we will still learn a vital data point is how the virus is spreading. These learnings, including the test results, will be fed into a continuously retraining machine learning algorithm. This algorithm will keep track of the trajectory of an infected person and common intersection locations. Additionally, this algorithm will also be able to account for an infected person being quarantined, thus neutralizing a virus carrier from the mix. In summary, this algorithm is akin to performing deep automated contact tracing at a level that cannot be matched by armies of volunteers.

Another important byproduct of the trained algorithm is the automatic extraction of “features”. In machine learning, a feature is an individual measurable property or characteristic of a phenomenon being observed [1]. For example, the algorithm will observe that many people are becoming infected, without coming in direct contact with an already infected person. Based on observing millions of such data points, it can, on its own, identify discriminating features such as an infected mailman route and common meeting areas that include certain surfaces like metals where coronavirus can remain active for days.

Using a continuously retraining algorithm, we can start to open parts of the country where the threat of spread is low. Any discovery of a COVID-19 case in a low-risk area will trigger the actions mentioned above and will flow back as input to training. It should be evident that the dataset and algorithm described above is computationally challenging. We are talking about recursive data searches through a dataset comprised of millions of citizens and a continuously learning algorithm with potentially billions of hyperparameters.

But Hasn’t This Approach Already Been Used in Other Countries like Taiwan and Singapore?

There is no question that location tracking capabilities have been highly effective in controlling the spread of coronavirus. In Taiwan and Singapore, location tracking technologies were used very early in the outbreak and mainly used for surveillance. In Korea, officials routinely send text messages to people’s phones alerting them on newly confirmed infections in their neighborhood — in some cases, alongside details of where the unnamed person had traveled before entering quarantine. Based on my research, these countries did not rely on big data and deep learning techniques to derive insights from the data. In the case of Taiwan and Singapore, the dataset of infected persons is not large enough for such an analysis.

Summary

The U.S. Government has broad authority to request personal data in the case of a national emergency like the Coronavirus. In the United States, phone companies such as AT&T and Verizon have extensive records on their customer’s movements. However, it does not appear that we are leveraging the large body of people’s movement data to combat coronavirus. According to a recent Washington Post story, “AT&T said it has not had talks with any government agencies about sharing this data for purposes of combating coronavirus. Verizon did not respond to requests for comment.”

The goal of this post is to engender a collaborative discussion with experts in big data, ML and medicine. Hopefully, there are efforts already underway based on a similar or better idea. Please send your comments via twitter @vlele.