Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

How to Build iPhone & Android Mobile Apps with Angular 14, Ionic Capacitor 3, SwipeClouds & WebRTC

5.00/5 (21 votes)
27 Jan 2021CPOL27 min read 70.2K   1.6K  
XCODE & Android Studio mobile app projects built using Ionic's Capacitor with an Angular 14 UI. Includes WebRTC for video conferencing, and SwipeClouds for Playing Millions of HD Movies & Videos and A Custom Capacitor Plugin for Scraping User Data for Targeted Delivery of video ads to mobile phones.
WebRTC Ionic Capacitor Mobile App with SwipeClouds UI/UX. Includes Custom Capacitor Plugin for iPhone & Android called InfoMagnet that scrapes user data to deliver targeted TV commercials. This Mobile App contains millions of HD movies that you can watch on your phone or stream directly to any smart TV as well as Video Conferencing using WebRTC. Videos include the latest How-To build Russian Viktor Grebennikov anti-gravity platform and How-To build the Russian Alexey Chekurkov anti-gravity device.

Image 1Image 2

Some Features Covered in this Article

   Features & Code Included in Sample iPhone & Andrpid Apps:        

  • Includes WebRTC for Video Conferencing in Angular 14 & React.js
  • Fully Working Rubix Cube. Rotate Cube & Layers Any Way with your Fingers!
  • How to Add Capacitor to Angular 14 or React Apps in VS Code
  • Manage XCODE & Android Studio Using Ionic's Capacitor 3+
  • Ability to Stream Video to Any Smart TV Using Capacitor 3+
  • Custom Capacitor 3+ Plugin for Contacts & Scraping User Data
  • Dozens of Embeded HTML5 Games Like Grand Master Chess & BlackJack
  • Animated & Fullscreen Video Backgrounds
  • PayPal BuyNow & Merchant Processing In App
  • JSONP & Observables for Remote Video Ads Server
  • Delivers Targeted Video Ads Based on Zip Code Radius
  • How to Use JQuery Plugins in Angular
  • Pinch Clouds to Expand & Contract
  • Angular Component Plays Embedded Videos
  • Allows Use of Full-Featured SASS
  • Angular ListView, Toolbars & NavBars
  • iOS7 Frosted Panels & HTML Games Like Chess
  • Angular Dialog Popup Component
  • Angular LocalStorage Component
  • Angular Back Button for External Sites
  • Games That Pay YOU REAL CASH for Shooting Monsters!

Introduction

Ionic describes their Capacitor Framework as "... a spiritual successor to Apache Cordova and Adobe PhoneGap, with inspiration from other popular cross-platform tools like React Native and Turbolinks, but focused entirely on enabling modern web apps to run on all major platforms with ease."

I was very impressed with Ionic's Capacitor 3+ Framework that allows you to create the UI of a mobile app using Visual Studio Code, i.e., VS Code, in Angular, JavaScript, JQuery, JQueru Mobile, React.js, or any JavaScript Framework for your UI. Capacitor will then automatically create and setup XCODE and Android Studio with a two-way bridge to your UI code in VS Code. Both your XCODE project and your Android Studio project are now part of VS Code's project files and the advantages of this are enormous.

You can add Capacitor to React, Angular, Vue, JavaScript, JQuery Mobile, or any JavaScript Framework to create fully Native Language Mobile Apps with a single codebase for your UI/UX but the backend is all Native Language. Capacitor connects XCODE and Android Studio, and VS Code so you can write Native Languages like Objective C, Swiff, Java, C++, or Kotin just inside VS Code and verything is updated automatically in XCODE and Android Studio.

This article is a sample of how to create a mobile app using Capacitor 3 or greater that plays videos and HD movies. Our UI will be written in Angular 14. I choose NOT to add Ionic to this project because one of it's best features is that it isn't necessary to use Ionic. You can add Capacitor to any JavaSCript Framework like React.js, Angular, Ionic, JQuery, JQuery Mobile, Vue,js, etc. 

We will also create a Capacitor plugin to collect user data for statistics and we will modify the Swift code in XCODE and the Java code in Android Studio to add some additional features. And it isn't necessary to know either Swift code or Java code to build an iPhone or Android mobile app using Capacitor.

There are 3 Parts to our Capacitor Mobile App, namely, the Native iPhone App, the Native Android App, and the Angular App that is our UI displayed in the browser interfaces in the native apps.

I decided to use Angular instead of React in the demo project because of the many advantages of Angular over React. The data flow in Angular is two-directional so that when the UI is changed, then the model state changes automatically, and the reverse is also true. On the other hand, React has a one-directional data flow, i.e., changing the UI elements is possible only after changing the model state. Angular updates the real “Document Object Model” (DOM), i.e., the programming interface for HTML and XML documents while React only updates a virtual DOM. Angular uses Typescript, a statically compiled language that provides for static typing, classes, and interfaces. Writing the code in Typescript makes the code more robust since it helps in identifying common errors and eliminating them while React, in comparison, just uses JavaScript.

Create an Angular App

We will start by creating an Angular app using Angular CLI and then adding Capacitor to our Angular app in VS Code. You can do on a Mac where you will also be able to both XCODE and Android at the same with VS Code or you can create your Angular app on Windows where you can open VS Code with Android Studio and when you are ready just open your VS Code Angular project on a Mac to compile the iPhone version of the project in XCODE on your Mac. I am building this sample project on a Mac so most commands will be prefaced with sudo so if you are on Windows just leave out the sudo command that on a Mac means Run As Administrator.

Installing Node.js to Get Started

Most readers will already have Node.js installed on their PC(Window) and OS X (Mac) but, if you don't, then start by downloading Node.js and installing the latest version of node at https://nodejs.org/en/download/ using one of the installers and follow the steps.

At this point, if you tried using npm, some people will get the dreaded and now famous error:

npm ERR! Windows_NT (some version here) 

There are numerous working fixes for this error if you are behind a proxy but if you aren't behind a proxy, then trying to fix this error can make you crazy. To fix this error or if you just updated to the latest version of npm, then run the commands below in a CMD window launched as ADMINISTRATOR:

npm config delete http-proxy
npm config delete https-proxy
npm config delete proxy -g
npm config delete http-proxy -g

// THE REAL MAGIC TO FIXING THIS ERROR IS:
sudo npm config set registry "http://registry.npmjs.org"
sudo npm config set strict-ssl false

NOTE: If you are using a Mac, then you must also do the following to avoid potential errors:

sudo npm install -g --unsafe-perm=true --allow-root

Install Angular CLI

If you already have the latest version of Angular CLI installed you can skip this step, otherwise, we should start by understanding Webpack, System.js and angular-cli. System.js was heavily used in the beginning when Angular 2 was being built. Webpack evolved next and finally, the defacto standard now, Angular CLI evolved as a sort of wrapper for Webpack and to help scaffold new projects and create components. easily. Installing angular-cli which will also install Angular's "ng" command globally on your system: Directions for installing angular-cli are at:

sudo npm uninstall -g angular-cli
sudo npm uninstall --save-dev angular-cli
sudo npm uninstall --save-dev angular/cli
sudo npm uninstall -g @angular/cli
sudo npm cache clean

On Windows do the following:
Delete the C:\Users\YOU\AppData\Roaming\npm\node_modules\@angular folder.

Reboot, then, finally, run:
sudo npm install -g @angular/cli@latest

To verify whether your installation
completed successfully, you can run:
ng --version

NOTE: This is critical... If you are on a Mac, then you MUST do the following:

sudo npm install -g --unsafe-perm=true --allow-root

Next, create a directory to hold our Capacitor projects and give it a name, I caalled it "Capacitor."

On a Mac do the following to create a drirectory for your Capacitor projects:

sudo mkdir /Users/stargate/Documents/Capacitor
sudo chmod a+rwx /Users/stargate/Documents/Capacitor

In the code above remember to replace the term "stargate" with the name of your Mac. On Windows just create a folder called "Capacitor" and set security on that folder and sub folders to "Everyone" or to what identy you want to have read/write priveleges.

Launch VS Code  and open the directory you created to hold your Capacitor projects which in our case is "Capacitor." Next go to the menu "View/Terminal" and enter these commands to create an ordinary Angular 14 app inside your "Capacitor" folder:

sudo ng new SwipeClouds

For more details on command line options for creating an Angular app, you will find a good reference here:

Close VS Code and re-launch VS Code and open the folder "SwipeClouds."

Check the version of Angular by opening the file in VS Code called "package.json" and if it is version 14 then there is nothing more to do. If the angular version is less than version 14 then we update to the next version of Angular in sequence as follows:

sudo ng update @angular/core@8 @angular/cli@8 --allow-dirty --force
sudo ng update @angular/core@9 @angular/cli@9 --allow-dirty --force
sudo ng update @angular/core@10 @angular/cli@10 --allow-dirty --force
sudo ng update @angular/core@11 @angular/cli@11 --allow-dirty --force
sudo ng update @angular/core@12 @angular/cli@12 --allow-dirty --force
sudo ng update @angular/core@13 @angular/cli@13 --allow-dirty --force
sudo ng update @angular/core@14 @angular/cli@14 --allow-dirty --force

Next, let's build and run the Angular app to test it was created without errors. To build it, we can run:

sudo ng serve

Open your web browser to: localhost:4200.

Add Capacitor to Our Angular App

Let's add Capacitor to our Angular app as follows in a new terminal window in VS Code as follows:

npm install --save @capacitor/core @capacitor/cli

In order to create our XCODE and Android Studio projects inside of VS Code, we must do the following after adding Capacitor to our Angular project. Open the file angular.json and change from "outputPath": "dist/{{nameApp}}" which in our case would be:

Find "outputPath":  "dist/swipeclouds",
and change it to:
"outputPath": "www",

Then in the file capacitor.config.json make sure that: "webDir": "www".

"webDir": "www",

THE NEXT STEP IS CRITICAL... YOU MUST CREATE A FOLDER CALLED "www" with  a file called "index.html" and place this file in the "www" folder you just created in order for Capacitor to add your Android Studio project or XCODE project in VS Code. The "index.html" you create can be empty since it is only a place holder for now.

To do this open a cmd Window with Admin rights and run:

mkdir "C:\Capacitor\swipeclouds\www"
copy /Y C:\Capacitor\swipeclouds_fixes\www\index.html C:\Capacitor\swipeclouds\www\index.html
copy /Y C:\Capacitor\swipeclouds_fixes\www\favicon.ico C:\Capacitor\swipeclouds\www\favicon.ico

On Windows, we have the disadvantage that only Android Studio will run so we can run XCODE until we move our project over to Mac. I find it easier to type and work on Windows so I tend to start my Capacitor projects on Windows and finish out my Android Studio code, then I will move the project to my Mac and add ios and add my code to XCODE to build the iPhone version. But most users that have a Mac will probably prefer to do everything on their mac. If you are on Windows, then you can add your Android Studio Project to the Angular app on Windows and later move your project to a Mac and add your XCODE project to your Angular app.

Run the following code in a cmd Window with Admin Rights to create your Android Studio project inside VS Code's project structure as follows:

npx cap init

When you are prompted as shown below answer the following:

Image 3

In the added capacitor.config.ts file make the following change:

webDir: 'www' 

Then let's add Android as follows:

npm install @capacitor/android
npx cap add android
ng b --prod
npx cap sync

An Intentional Bug in Capacitor?

Prior to Capacitor version 3 you would run "npx cap open android" to open your project in Android Stdio BUT that will not open Android Studio any more. In one post on Ionic I read something about "unhooking the IDEs" and I thought they might be saying that in version 3 of Capacitor you have to open your Android project MANUALLY in Android Studio! So far I have NOT been able to get it to open Android Studio automatically any more! C'est Domage!

When you try to run your Android Studio in Capacitor 3 it will NOT load your index.html because of a bug in the file "WebViewLocalServer.java" as follows:

responseStream = jsInjector.getInjectedStream(responseStream);

As every Java programmer knows responseStream MUST be wrapped in a TRY / CATCH BLOCK as follows:

try {
  responseStream = jsInjector.getInjectedStream(responseStream);
} catch (Exception e) {
  Logger.error("Unable to open index.html", e);
}

And if you walk thru the debugger in Android Studio you will see that when loading most local files the responseStream will be null. You will have to fix this bug manually yourself in teh Cpacitor code if you want your app to run at all.

Download swipeclouds_fixes.zip

To make it easy to fix the above bug and other issues and to add more functionality to Capacitor I created a folder called "swipeclouds_fixes" and you download the zipped folder at the top of this article and put it inside "C:|Capacitor" folder to add these changes by simply coping the changes over to your project by opening a cmd Window with admin rights ad running the following code:

cd C:\Capacitor\swipeclouds\node_modules\@capacitor\android
copy /Y C:\Capacitor\swipeclouds_fixes\Bridge.java 
   capacitor\src\main\java\com\getcapacitor\Bridge.java

cd C:\Capacitor\swipeclouds\node_modules\@capacitor\android
copy /Y C:\Capacitor\swipeclouds_fixes\BridgeWebViewClient.java 
   capacitor\src\main\java\com\getcapacitor\BridgeWebViewClient.java

cd C:\Capacitor\swipeclouds\android\app\src\main
copy /Y C:\Capacitor\swipeclouds_fixes\MainActivity.java 
   java\org\redcross\arcemployeemobileapp\MainActivity.java

cd C:\Capacitor\swipeclouds\android\app\src\main
copy /Y C:\Capacitor\swipeclouds_fixes\AndroidManifest.xml 
   AndroidManifest.xml

cd C:\Capacitor\swipeclouds\android\app\src\main
copy /Y C:\Capacitor\swipeclouds_fixes\network_security_config.xml 
   res\xml\network_security_config.xml

copy /Y C:\Capacitor\swipeclouds_fixes\capacitor.config.ts 
   C:\Capacitor\swipeclouds\capacitor.config.ts

Create XCODE Project on Mac Using Capacitor

On a Mac, we have the advantage that BOTH XCODE and Android Studio will run so you can compile your iPhone App in XCODE and compile your Android app in Android Studio on the Mac. If you are on a Mac, do the following to create BOTH your Android Studio and XCODE projects as follows:

sudo npm install -g --unsafe-perm=true --allow-root

After that, we can create your Android Studio project as described above just like on Windows and you can create your XCODE project on Mac as follows after first adding your Android Studio project:

npm install @capacitor/ios
npx cap add ios
npx cap sync

As I mentioned, I prefer to work on Windows so after I move the project to my Mac just to add and sync the XCODE project. I usually then move it back to my Windows machine where I can then write in Swift code in my XCODE project, but I just can't run it on my Windows machine.

iOS Changes You MUST Make to Capacitor File CAPBridgeViewController.swift

// iOS will always complain about invalid certificates, either in debug or
// release mode so I added this public class to CAPBridgeViewController.
public func webView(_ webView: WKWebView, didReceive challenge: 
   URLAuthenticationChallenge, completionHandler: @escaping 
   (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
   let cred = URLCredential(trust: challenge.protectionSpace.serverTrust!)
   completionHandler(.useCredential, cred)
}

Android Studio Changes You MUST Make to Capacitor File BridgeWebViewClient.java

// Android - We need to ovverride all checks for a valid SSL Certificate!
@Override
public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {
   handler.proceed();
   return;
}

Fun with Your Fingers: Move Clouds with Your Finger & Pinch to Resize Clouds

SwipeClouds is both a UI and UX because it looks cool and it engages users to play with the clouds that is just fun. SwipeClouds has a Very Different Look & Feel with A Novel Approach to Navigation where you can move and resize the objects in the SwipeCloud with your fingers. Pinching the SwipeCloud with your fingers will increase and decrease the size of the SwipeCloud. This SwipeCloud is the main means of navigation for the Angular Mobile App and clicking on any of the images in the SwipeCloud will load a different view, which, in most cases will load the VideoComponent View for that particular group of video feeds from any tube server that allows embedding.

Image 3

Resizing the swipe cloud by pinching the mobile screen as shown below

We set pinchZoom = true.

JavaScript
TouchDown(e: any) => {
  var tg = EventToCanvasId(e), tc = (tg && TagCanvas.tc[tg]), p;
  if(tc && e.changedTouches) {
    if(e.touches.length == 1 && tc.touchState == 0) {
      tc.touchState = 1;  tc.BeginDrag(e);
      if(p = EventXY(e, tc.canvas)) {
        tc.mx = p.x; tc.my = p.y;  tc.drawn = 0;
      }
    } else if(e.targetTouches.length == 2 && tc.pinchZoom) {
      tc.touchState = 3; tc.EndDrag();  tc.BeginPinch(e);
    } else { tc.EndDrag();  tc.EndPinch();  tc.touchState = 0; }
  }
}

Sliding iOS7 Frosted Vertical Control Panel

By clicking the "Tools" button in the NavBar on the bottom of the main swipeclouds screen you will slide out the Sliding iOS7 Frosted Vertical Control Panel where you can set other options for this Mobile App. You can slide this panel up and down with your finger.

Image 5

SwipeCloud Shapes

The Sliding iOS7 Frosted Vertical Control Panel shown also aloows you to change the shape of the floating clouds. For example, you can make the round cloud into a Vertical Ring, i.e. VRing Option on the Control Panel, as shown below:

Image 6

Animated & Fullscreen Video Backgrounds

Since our main GUI is a HTML5 Canvas we can easily apply animated gif files as backgrounds or a fullscreen video background. We can fill the entire background of our canvas with a video stream from a variety of sources such as local or remote video files or from the front or back camera on the phone itself.  To add a video background simply use Fabric.js video functions applied to the canvas. To add video or animations INSIDE the SwipeCloud you would use the centreFunc in the options array oopts that allows you to create your own callback function to draw on the canvas between the front and back tags with an animation or video.​​​​​​ as follows.

oopts = { ...  centreFunc: this.RSquare, ... }

RSquare(context2D, width, height, centreX, centreY) {
  // your code here
}

// In our Angular App we have a RSquare function 
// to demonstrate adding an animation:
RSquare(c, w, h, cx, cy) {
  let d = ((new Date).getTime() % 10000) * Math.PI / 2500;
  c.setTransform(1, 0, 0, 1, 0, 0);
  c.translate(cx, cy);
  ... etc.
  c.fill();
}

As shown below you can see an example of such an animation:

Image 7

Video Layouts for Portrait & Landscape Views

Layouts for Portrait vs. LandscapeI decided that the best layout for video and the other views was to retain the Toolbar and Navbar in the Portrait Orientation and to Hide them in the Landscape Orientation. You can see this below. I added a button and code to stream the selected video from your mobile phone to any smart TV set using pairing from the tube server's site.

Image 8

Cloud Collections... Each Cloud is a Theme App

In the figure below, you can see a variety of SwipeClouds where each SwipeCloud represents a Theme, and each floating image, when clicked, can easily load into a ListView MILLIONS of Videos from over 100 tube servers like YouTube.com and others, TV SHows and HD Movies from hundreds of Tube Servers around the world that you can watch either on your mobile phone or on any Smart TV.

Image 9

The Mobile App Themes I added to our SwipeClouds project here are as follows:

  • swipeCloud - General cloud to access other clouds, video & HD movie collections & playlists
  • gamesCloud - Collection of HTML Games & Gaming Sites
  • eduCloud - Collection of Educational Videos to Learn Just About Anything
  • dronesCloud - Collection of Videos on Drone Topics especially Manned Drones
  • solCloud - Collection of Video on Various General Health Topics
  • newsCloud - Collection of Randomly Selected Videos on Topics in the News

Add SwipeClouds Framework to Our Angular App

SwipeClouds is a modified version I created of Graham Breach's TagCanvas that is a JavaScript class which draws and animates a HTML5 canvas based tag cloud. It was released as open source project under the LGPL v3 license.

Image 10

I threw swipeclouds together quickly for this article and I need to clean up and shorten the scss but for the meantime, let's just go into the "angular.json" file and increase our "budgets" to allow more warnings and errors as follows:

JavaScript
"budgets": [
   {
    "type": "initial",
    "maximumWarning": "5mb",
    "maximumError": "7mb"
   },
   {
    "type": "anyComponentStyle",
    "maximumWarning": "60kb",
    "maximumError": "100kb"
   }
]

Let's Add WebRTC for Video Conferencing

I decided to add a basic impplemtation of WebRTC to SwipeClouds in this sample for Video Conferencing using peer.js so you will need to add peer,js as follows in a cmd Window on Windows with Admin priveleges.

npm install peerjs

Image 11

To avoid some common errors with Angular and peer,js you need to add the following to your tsconfig.json file:

<code>{
...
    "esModuleInterop": true,
...
}</code>

I added webRTC quickly so I made it for a video chat between 2 people but it is easy to extend the code I wrote to create video conferencing with rooms. I added peer.js as follow:

import { Injectable } from '@angular/core';
import Peer from 'peerjs';

const constraints: MediaStreamConstraints = {video: true, audio: false};

//@Injectable
export class WebrtcService {
  peer: Peer;
  phoneStream: MediaStream;
  phoneEl: HTMLMediaElement;
  contactEl: HTMLMediaElement;
  stun = 'stun.l.google.com:19302';
  mediaConnection: Peer.MediaConnection;
  options: Peer.PeerJSOption;
  stunServer: RTCIceServer = {
    urls: 'stun:' + this.stun,
  };

In VS Code's Terminal, run the following to test that swipeclouds is working:

ng serve

Then open Chrome to: localhost:4200 and resize to size of a mobile app and you should see the floating swipeclouds. Since the focus of this article is about using Capacitor to create an iPhone and Android mobile app, I will focus on that next and talk about the swipeclouds Angular app towards the end of this article.

SwipeClouds Streams Video to Any Smart TV

You will notice that I added a button at the top of the Videos List and in the links in teh NavBar to stream any video to any smart TV as shown below:

Image 12

SwipeCouds BrowserComponent, Chess & HTML5

I also added to this Angular Mobile App a fantastic html Chess Game Stefano Gioffre. The big advantage to adding JQuery to Angular is that there are hudreds of cool html games like chess that can be easily dropped into the sample prject by simply loading them into an iFrame. So I decided to add a BrowserComponet that contains an iFrame in the html as shown below that can be loaded with local or remote webpage. 

<iframe id="iframe" name="iframe" [src]='page' scrolling="yes"
    marginheight="0" frameborder="0" webkitallowfullscreen
    [attr.width]='_zwidth' [attr.height]='_zheight'
    mozallowfullscreen allowfullscreen></iframe>

And in the BrowserComponent we subscribe to route as follows; 

    this.sub = this.route
      .params
      .subscribe(params => {
        this._zwidth = window.innerWidth;
        this.zurl = params['url'];
    });

And loading the html chess is as simple as the following:

    this.cloudsrouter.navigate(['/browser', {name: 'chess', 
      url: './assets/chess/chess.html'}])

Using the Browser Component you can easily drop in already existing html5 games thata you don't have time to rewrite in Angular. I included access to online games like Entropia Universe that PAY YOU REAL CASH for shooting monsters! The BrowserComponent lets you easily display html that is local or remote from your license agreement to existing games like chess shown below.

Image 13

Create a Capacitor Plugin to Collect User Data

I created a Capacitor Plugin that I called "InfoMagnet" to collect user data that is needed to understand how users are using the app and deliver targeted advertising to our users. Let's first look at some of the things we might want to know about our users like the following:

  • appId: unique id for each iPhone & Android app
  • appName: name of app installed
  • appVersion: app version installed
  • model: model of the mobile phone
  • opSystem: operating system of phone
  • osVersion: version phone operating system
  • phone: phone number
  • email: email address
  • name: name of registered phone user
  • city: city of registered user
  • state: state of registered user
  • ctry: country of registered user
  • pc: postal code of registered user
  • latitude: latitude of phone
  • longitude: longitude of phone
  • ipaddress: first ipaddress of phone
  • ipaddress2: second ipaddress of phone

Please understand that we can't collect all or part of this information on the iPhone and Android without getting the user's permission so it is always the user who decides what information we will collect.

One part of InfoMagnet collects user data and another section retries user contacts. I created this plugin quickly and I didn't have a chance to finish all the swift code in the plugin. But I will be finishing the iPhone part of the code over the next few weeks and updating this article with that code as well as the changes for version 3.0 of Capacitor.

We start by creating a folder to hold our Capacitor Plugin projects using a Command Window running as Administrator on Windows as follows:

md C:\Capacitor_plugins

We create a Capacitor Plugin as follows:

cd C:\Capacitor_plugins
npx @capacitor/cli plugin:generate
Creating new Capacitor plugin
Then answer these questions...
? Plugin NPM name (kebab-case): InfoMagnet
? Plugin id (domain-style syntax. ex: com.example.plugin):
com.sergioapps.infomagnet
? Plugin class name (ex: AwesomePlugin) InfoMagnet
? description: Collects user data with user's permission.
? git repository: https://github.com/tvmogul/InfoMagnet
? author: Bill SerGio, The Infomercial King
? license: MIT
? package.json will be created, do you want to continue? Y

Then we will build our plugin using:

ng build

Add the zip file for the InfoMagnet Plugin.

Install Your Private InfoMagnet NPM Module without Your Own Registry

On Windows you can install InfoMagnet Plugin without a registry to our Angular app as follows:

npm install C:\Capacitor_plugins\infomagnet --save

On Max add our InfoMagnet Plugin to our Angular app as follows:

npm install ~/Documents/Capacitor_plugins/infomagnet --save

Android Studio Settings for InfoMagnet Capacitor Plugin

Open Android Studio and add this to the file MainActivity of the Main App if it isn't already in there:

// Initializes the Bridge
this.init(savedInstanceState, new ArrayList <Class<? extends Plugin>>() {{
    // Additional plugins you've installed go here
    // Ex: add(TotallyAwesomePlugin.class);
    add(Storage.class);
    add(InfoMagnet.class);
}});

Kotlin

It is also possible to develop custom code for your app or Capacitor plugins with Kotlin. When adding new Kotlin files in Android Studio, you will be prompted to configure Kotlin in your project if necessary. When doing this, make sure to only configure Kotlin in your app module, not the Capacitor or third-party modules.

XCODE Settings for InfoMagnet Capacitor Plugin

Open XCODE and add the following code to the file Plugin.m in our InfoMagnet Plugin if it isn't already there. Note that you have to declare EACH method in your plugin with a semi-colon ending as shown below:

#import <Foundation/Foundation.h>
#import <Capacitor/Capacitor.h>

// Define plugin using the CAP_PLUGIN Macro, and each
// method the plugin supports using CAP_PLUGIN_METHOD macro.
CAP_PLUGIN(InfoMagnet, "InfoMagnet",
    CAP_PLUGIN_METHOD(echo, CAPPluginReturnPromise);
    CAP_PLUGIN_METHOD(getUserInfo, CAPPluginReturnPromise);
    CAP_PLUGIN_METHOD(saveUserInfo, CAPPluginReturnPromise);
)

Changes to Our Android App Code to Make Our Plugin Work

I thought it would helpful to create a custom Capacitor Plugin because in all probability, you will need to create one for almost any app you create. I call this plugin "InfoMagnet" because it will scrape user data from user's phones to help us to deliver targeted marketing. Of course, the user must agree to the permissions we need to accomplish this so we will start by adding Java and Swift code to accomplish this. We could put all the permissions calls in our plugin but I have found that things work more smoothly if you add the permissions code to the Native app instead.

Any Android programmer will notice that we don't need all these permissions for the current features in the Android code in our Android app but I added some permissions for future things I might want to add to this app over the next few weeks. We modify our MainActivity.java file in our MAIN APP as follows:

public class MainActivity extends BridgeActivity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    int PERMISSION_ALL = 1;
    String[] PERMISSIONS = new String[]{
      Manifest.permission.INTERNET,
      Manifest.permission.READ_EXTERNAL_STORAGE,
      Manifest.permission.WRITE_EXTERNAL_STORAGE,
      Manifest.permission.ACCESS_COARSE_LOCATION,
      Manifest.permission.ACCESS_FINE_LOCATION,
      Manifest.permission.ACCESS_MEDIA_LOCATION,
      Manifest.permission.ACCESS_BACKGROUND_LOCATION,
      Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS,
      Manifest.permission.LOCATION_HARDWARE,
      Manifest.permission.ACCESS_NETWORK_STATE,
      Manifest.permission.ACCESS_WIFI_STATE,
      Manifest.permission.CHANGE_NETWORK_STATE,
      Manifest.permission.CHANGE_WIFI_STATE,
      Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
      Manifest.permission.READ_SMS,
      Manifest.permission.READ_PHONE_NUMBERS,
      Manifest.permission.READ_PHONE_STATE,
      Manifest.permission.READ_CONTACTS,
      Manifest.permission.WRITE_CONTACTS,
      Manifest.permission.GET_ACCOUNTS,
      Manifest.permission.BROADCAST_STICKY,
      Manifest.permission.DISABLE_KEYGUARD,
      Manifest.permission.EXPAND_STATUS_BAR,
      Manifest.permission.WRITE_SECURE_SETTINGS,
      Manifest.permission.WRITE_SETTINGS,
      Manifest.permission.ACCOUNT_MANAGER,
    };

    // Initializes the Bridge
    this.init(savedInstanceState, new ArrayList<Class<? extends Plugin>>() {{
      // Additional plugins you've installed go here
      // Ex: add(TotallyAwesomePlugin.class);
      add(InfoMagnet.class);
      add(Browser.class);
      add(Network.class);
      add(Storage.class);
      add(App.class);
    }});

    // MUST follow this.init code above!
    if(!hasPermissions(this, PERMISSIONS)){
      ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL);
    }
  }

The Dreaded ERR_CLEARTEXT_NOT_PERMITTED

This is probably the most common error message in Hybrid apps caused by a simple lack of permmissions not being set in Android Hybrid Mobile Apps. Lookat at the Android Manifest file and be sure you have the following code:

<application
  android:allowBackup="true"
  android:icon="@mipmap/ic_launcher"
  android:label="@string/app_name"
  android:roundIcon="@mipmap/ic_launcher_round"
  android:supportsRtl="true"
  android:theme="@style/AppTheme"
  android:allowTaskReparenting="true"
  android:hardwareAccelerated="true"
  android:usesCleartextTraffic="true"
  android:networkSecurityConfig="@xml/network_security_config"
  android:allowClearUserData="true">

Notice the line of code:

android:networkSecurityConfig="@xml/network_security_config"

This external xml file contains the extra permissions we need in Android Hybrid Mobile Apps as follows:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <domain-config cleartextTrnetwork_security_configafficPermitted="true">
    <domain includeSubdomains="true">www.swipeclouds.com</domain>
    <domain includeSubdomains="true">swipeclouds.com</domain>
  </domain-config>
  <base-config cleartextTrafficPermitted="true">
    <trust-anchors>
      <certificates src="system" />
    </trust-anchors>
  </base-config>
</network-security-config>

ByPassing SSL Certificates for Web-Based Apps

So far we have assumed that your JavaScript Framework UI/UX is run from either Android or iPhone but in many cases you will want to simply run a server-based web app written in any language (React.js, Angular, .NET, etc.) that is behind HTTPS. Such a responsive web-based app is referred to Progressive Web App (PWA). 

I would NEVER use Ionic's approach to builing a Progressive Web App (PWA). Always create your web-based app that resides on a remote server as you would create an app that will reside on a mobile phone and our mobile app will load both the server-based code and code residing on the mobile phone. The code on the mobile phone will allow us to load plugins and to access Native code that we can't do from the web-based app.

Ionic intentionally restricted their webserver so webview won't allow navigation if using self signed certificates or certain SSL certificates from non-approved sources which creates a problem for web-based apps so you will need to modify the code in the Bridge.java file as follows:

// Change the following from:
//if (!scheme.equals(Bridge.CAPACITOR_HTTP_SCHEME) && !scheme.equals(CAPACITOR_HTTPS_SCHEME)) {
//  appUrl += "/";
//}

// Remove check for HTTPS: !scheme.equals(CAPACITOR_HTTPS_SCHEME) 
if (!scheme.equals(Bridge.CAPACITOR_HTTP_SCHEME)) {
  appUrl += "/";
}

...
// And at the bottom of this section make this change to load your https URL
// Get to work
appUrl = "https://{SOME DOMIAN THAT HOSTS YOUR WEB-BASED APP}";
webView.loadUrl(appUrl);

In iOS you simply do the same thing to load a web-based app sitting behind https.

Configuring iOS

Configuring Info.plist

iOS developers will be familiar working with the Info.plist file, the main configuration file for the iPhone. The Info.plist is a plain XML file and can be edited directly which I prefer. Make sure to use low-level parameter names for the XML <key> values in Info.plist. To edit the raw XML, right click anywhere in the property list editor and toggle “Show Raw Keys/Values.” Your Capacitor Plugin might require, additional configuration for your app, and for permissions your app will ask for.

Unlike Android, permissions for iOS do not have to be specified in advance. Instead, they are prompted for when using a certain Plugin or SDK. Many iOS permissions require what are known as “Usage Descriptions” defined in Info.plist. Consult the Cocoa Keys list for keys containing UsageDescription to see the various usage description settings that may be required for your app.

For the app in this project, you can download the Info.plist file at the top of this article.

Configuring Android

Configuring AndroidManifest.xml

Android apps manage permissions, device features, and other settings by modifying AndroidManifest.xml. This file references values from other files in res/values/, to make it easy to update them separately, including styles.xml and strings.xml. You can download the AndroidManifest.xml file for this project at the top of this article.

Added Contacts to InfoMagnet

I added Contacts to InfoMagnet that you call as follows:

async getContacts(filter: any): Promise<any> {
  // You can add filtering by alphabet letters
  const textsent = filter;
  const result = await InfoMagnet.getContacts({'filter': textsent});
  this.contacts = [];
  for (const c of result.contacts) {
    if (c.photoThumbnail === undefined) {
      c.photoThumbnail = 'assets/img/1_aliens.png';
    }
    // OR...
    // if(this.isEmpty(c.photoThumbnail)) {
    //   c.photoThumbnail = 'assets/img/1_aliens.png';
    // }
    let firstName = c.displayName.split(' ').slice(0, -1).join(' ');
    if(firstName.length < 1) {
      firstName = c.displayName
    }
    let lastName = c.displayName.split(' ').slice(-1).join(' ');
    let isurname = '';
    if(lastName.length < 1) {
      lastName = c.displayName
    }
    if(lastName.length > 0) {
      isurname = lastName.substring(0, 1);
    }
    c.isurname = isurname.toLowerCase();
    c.firstName = firstName;
    c.lastName = lastName;
    this.contacts.push(c);
  }
  // sort by lastName
  this.contacts.sort(this.sortThings);
  this.updateVideoLayout();
}

And the screen looks something like this where you can make a phone call or send a text message or send an email by clicking on the icon in front of the phone number or email.

Deeplinks (aka Android App Links)

To enable deeplinking through Android App Links, follow the official Android guide on Adding Android App Links. Android Studio comes with a handy wizard for configuring App Links. Deep links to your server can stream server content dynamically created on your server like charts using Chart,js.

Anchors vs. Plugins in XCODE and Android Studio

At this point, we have both XCODE and Android Studio projects inside our Angular project after adding Capacitor. Technically, you don't need to know any Native programming languages like Swift, Objective C, C/C++, Java, or Kotlin to finish building your iPhone or Android app, but I will demonstrate two ways here of modifying XCODE and Android Studio that assumes you know Swift Code and Java to demonstrate the power of Capacitor. One way is to write code Swift or Java code directly in our XCODE and Android Studio projects and another way is to write a custom Capacitor Plugin which we will also do.

For example, in hybrid mobile apps, you always want to use Anchors when you can because when Anchors are called from JavaScript they will execute an instant, synchronous call to a method in your Native language and that is the fastest way to call any Native language method. Anchors are much faster than calling Native language methods using any Capacitor Plugin or Capacitor's Native Bridge. Both Anchors and Plugins have their place. If you don't need to return any data back to JavaScript use an Anchor and if you need to return data back to JavaScript use a Capacitor Plugin.

Let's Add Some Anchors to Android Studio Using Java

You can override "shouldOverrideUrlLoading" in Android Studio and XCODE for this project in several. If you are in Android Studio with the project open, you can just past the code for the BridgeWebViewClient.java at the top of the article into that file from inside Android Studio and Capacitor will automatically update the code in capacitor's node_modules in your Angular app project. This approach modifies the source code of Capacitor's node_modules which many programmers would probably not prefer doing. For this article, I modified the BridgeWebViewClient.java file to demonstrate Capacitor's ability to auto update the node_modules. You can also override "shouldOverrideUrlLoading" in a plugin like our InfoMagnet Plugin and there are samples of this method on the web for Capacitor.

C:\Capacitor\swipeclouds\node_modules\@capacitor\android\capacitor\src\main\
java\com\getcapacitor\BridgeWebViewClient.java

The change I made in the BridgeWebViewClient.java file in Android Studio to add a few anchors looks like this:

JavaScript
 @TargetApi(Build.VERSION_CODES.N)
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
  try {
    handleWebPageComponents(view, request.getUrl().toString());
  } catch (IOException e) {
    e.printStackTrace();
  }
  return true;
}

private void handleWebPageComponents(WebView view, String url) throws IOException {
  if (url.startsWith("close:")) {
    Intent intent = new Intent(Intent.ACTION_MAIN);
    intent.addCategory(Intent.CATEGORY_HOME);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    view.getContext().startActivity(intent);
  } else if (url.startsWith("refresh:")) {
    String newString = url.replace("refresh:", "");
    Uri myUri = Uri.parse(newString);
    bridge.launchIntent(myUri);
  } else if (url.startsWith("mailto:")) {
    view.getContext().startActivity(new Intent(Intent.ACTION_SENDTO, Uri.parse(url)));
  } else if (url.startsWith("tel:")) {
    Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
    view.getContext().startActivity(intent);
  } else if (url.startsWith("map:")) {
    String newString = url.replace("map:", "");
    view.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(newString)));
  } else if (url.startsWith("print:")) {
    // to be added by reader
  } else if (url.startsWith("msg:")) {
    String newString = url.replace("toast:", "");
    // showToast(context, newString);
  } else {
    Uri myUri = Uri.parse(url);
    bridge.launchIntent(myUri);
  }
}

Let's Add Some Anchors to XCODE Using Swift

In the CAPBridgeViewController.swift file in XCODE, add the following Java code:

for XCODE

Inserting TV Ads Into ListViews with Spacing

We insert TV Ads into the ListViews on a repetitive basis where after inserting a TV Ad we skip 4 rows and then repeat. This allows us to include TV commercials in a non-intrusive way as show below:

  getFeeds() {
    this.loading = true;
    this.feedsObservableService
      .getServiceFeedsJsonp('')
      .subscribe(
        (res) => {
          this.loading = false;
          if ((res) && (res !== 'undefined')) {
            this.feeds = res;  // feeds: Object[];
            const ads = this.LocalStorage.get('insert_ads');
            if (ads) {
              const skip_n = 4; // skip every 4 videos then insert ad
              let insertIndex = skip_n;
              for (let adsIndex = 0; adsIndex < ads.length; adsIndex++) {
                if (insertIndex < this.feeds.length) {
                  this.feeds.splice(insertIndex, 0, ads[adsIndex]);
                  insertIndex = insertIndex + skip_n + 1;
                }
              }
            }
            const atabs = res;
            if ( atabs[0].link ) {
              this.selectedLink = atabs[0].link;
              this.page = this.sanitizer.bypassSecurityTrustResourceUrl(this.selectedLink);
              $('#yt_player').attr('src', this.page);
            }
          }
        },
        (err) => { this.loading = false; console.log('error!', err); },
    );
    this.selectedIdx = 0;
  }

The image below shows 5 videos at a time in teh ListView which can hold up to 1,000 videos. To access the next 1,000 video you would press the "Next" button at the bottom in the NavBar. This ListView will display 4 videos between TV Commercials as shown in the code above. You can identify the TV commercials by the "WATCH" band in red where the timecode would normally be on the video in the ListView. SwipeClouds gives you access to MILLIONS of vdieos and HD movies for FREE in your mobile apps from over 100 tubes servers like YouTube.com, Yoku.com, etc.  I included some really cool How-To videos like the latest How-To build Russian Viktor Grebennikov anti-gravity platform and How-To build the Russian Alexey Chekurkov anti-gravity device. 

To replace the timecode shown above with the word "WATCH" with a red background we do this in the html code using *ngIf="feed.category==='tvads'" as shown below:

 <li *ngFor="let feed of feeds;let i = index;" 
    style="height: auto !important;" data-icon="false"  
    [class.active]="i == selectedRow" class="rsslistfetch-link ui-navbar">
    <div class="duration-cover" (click)="clicked($event, feed, i)">
       <div *ngIf="feed.category==='tvads'" class="tvads-left">WATCH</div>
       <div *ngIf="feed.category!=='tvads'"  
          class="duration-left">{{feed.duration}}</div>
       <img class="rounded-img" src="{{feed.image}}" />
    </div>
    <h3 class="ellipsis">{{feed.title}}</h3>
    <p class="ellipsis2">{{feed.shortDescription}}</p>
 </li>

JSONP Server for Playing MILLIONS of HD Movies

The sample Mobile App with this article, i.e., SwipeClouds, stores the video metadata internally in:

./assets/data/*.json

The video metedata is stored as JSON and is retrieved from the file whose name matches the video category requested. In other versions the video metadata is retrieved from a JSONP server. You can run a JSONP server easily on your server with a few lines of PHP code or you can use C# .NET etc.

The design concept I used for my JSONP server was that instead of having multiple generic handlers or a single handler with lots of switch/if statement I decided to use a single generic handler with Factory Design Pattern. The Factory returns a class based upon the methodName that is passed which is used to only handle that request. The Factory reads the methodName and its class from the web.config file and instantiates the handler. The generic handler requests the Factory for the handler and performs some pre/post processing.

Here is a link to an article I wrote on creating a JSONP Videos & TV Commercials server using C# .NET which is what I used in testing the JSONP code in this Angular Mobile App to stream MILLIONS of tube servers videos, movies, TV shows, and TV commercials:

JSONP Server to Stream Videos. Movies, TV Shows, Tube Channels, and TV Ads from Hunndreds of Tube Servers:
 C# .NET JSONP Server

Final Thoughts

The sample Angular Mobile App built using Ionic's Capacitor in this article is designed to deliver MILLIONS of videos, movies, TV shows, and TV commercials from hundreds of tube servers like YouTube.com and the largest television network in the world, namely, Youku.com. If you have a channel on YouTube or any tube server, you can put that channel into this app remotely by placing it on a JSONP server like the one in the link at the top of this article.

I will be updaing this article in the next few weeks with a number of new Capacitor Plugins I have written like OCR (Optical Character Recognition), a Voice Stress Analyser Plugin using the my modified version of the code published that the C.I.A. supposedly uses for detecting stress, i.e. "lying" in the human voice, 

You can download the compiled iPhone and Android SwipeClouds® Mobile App from my website at:

Build Amazing Mobile Apps with SwipeClouds® Angular Mobile Framework[^]

History

  • 7th March, 2017: Initial version
  • 27th January, 2021: Updated article to use Ionic Capacitor

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)