65.9K
CodeProject 正在变化。 阅读更多。
Home

如何使用 Angular 14、Ionic Capacitor 3、SwipeClouds 和 WebRTC 构建 iPhone 和 Android 移动应用程序

2017年3月7日

CPOL

27分钟阅读

viewsIcon

71143

downloadIcon

1754

使用 Ionic 的 Capacitor 和 Angular 14 UI 构建的 XCODE 和 Android Studio 移动应用程序项目。包括用于视频会议的 WebRTC,用于播放数百万高清电影和视频的 SwipeClouds,以及一个用于抓取用户数据以向手机定向投放视频广告的自定义 Capacitor 插件。

本文涵盖的一些功能

 样本 iPhone 和 Android 应用程序中包含的功能和代码:      

  • 包括用于 Angular 14 和 React.js 中视频会议的 WebRTC
  • 功能齐全的魔方。用手指随意旋转魔方和各层!
  • 如何在 VS Code 中将 Capacitor 添加到 Angular 14React 应用程序
  • 使用 Ionic 的 Capacitor 3+ 管理 XCODEAndroid Studio
  • 使用 Capacitor 3+ 将视频流式传输到任何智能电视的能力
  • 用于联系人和抓取用户数据的自定义 Capacitor 3+ 插件
  • 数十款嵌入式 HTML5 游戏,如国际象棋大师二十一点
  • 动画和全屏视频背景
  • PayPal BuyNow 和应用内商家处理
  • JSONP 和 Observables 用于远程视频广告服务器
  • 根据邮政编码半径投放定向视频广告
  • 如何在 Angular 中使用 JQuery 插件
  • 捏合云以展开和收缩
  • Angular 组件播放嵌入式视频
  • 允许使用功能齐全的 SASS
  • Angular ListView、工具栏和导航栏
  • iOS7 磨砂面板和国际象棋等 HTML 游戏
  • Angular 对话框弹出组件
  • Angular LocalStorage 组件
  • 用于外部网站的 Angular 后退按钮
  • 射击怪物可以获得真实现金的游戏!

引言

Ionic 将其 Capacitor 框架描述为“... Apache Cordova 和 Adobe PhoneGap 的精神继承者,并从 React Native 和 Turbolinks 等其他流行的跨平台工具中汲取灵感,但完全专注于让现代 Web 应用程序轻松地在所有主要平台上运行。

我对 Ionic 的 Capacitor 3+ 框架印象深刻,它允许您使用 Visual Studio Code(即 VS Code)在 Angular、JavaScript、JQuery、JQuery Mobile、React.js 或任何 JavaScript 框架中创建移动应用程序的 UI。然后,Capacitor 将自动创建和设置 XCODEAndroid Studio,并与 VS Code 中的 UI 代码建立双向桥接。您的 XCODE 项目和 Android Studio 项目现在都成为 VS Code 项目文件的一部分,这带来了巨大的优势。

您可以将 Capacitor 添加到 React、Angular、Vue、JavaScript、JQuery Mobile 或任何 JavaScript 框架中,以创建完全原生语言移动应用程序,为您的 UI/UX 提供单一代码库,但后端都是原生语言。Capacitor 连接 XCODEAndroid StudioVS Code,因此您可以在 VS Code 中编写 Objective C、Swift、Java、C++ 或 Kotlin 等原生语言,所有内容都会在 XCODE 和 Android Studio 中自动更新。

本文是关于如何使用 Capacitor 3 或更高版本创建播放视频和高清电影的移动应用程序的示例。我们的 UI 将用 Angular 14 编写。我选择Ionic 添加到此项目中,因为它的最佳功能之一是不必使用 Ionic。您可以将 Capacitor 添加到任何 JavaScript 框架,例如 React.js、Angular、Ionic、JQuery、JQuery Mobile、Vue.js 等。

我们还将创建一个 Capacitor 插件来收集用户数据以进行统计,并且我们将修改 XCODE 中的 Swift 代码和 Android Studio 中的 Java 代码以添加一些附加功能。而且,使用 Capacitor 构建 iPhone 或 Android 移动应用程序不需要了解 Swift 代码或 Java 代码。

我们的 Capacitor 移动应用程序3 个部分,即原生 iPhone 应用程序原生 Android 应用程序和在原生应用程序的浏览器界面中显示的 Angular 应用程序,即我们的 UI。

我决定在演示项目中使用 Angular 而不是 React,因为 Angular 相对于 React 有许多优势。Angular 中的数据流是双向的,因此当 UI 更改时,模型状态会自动更改,反之亦然。另一方面,React 具有单向数据流,即只有在更改模型状态后才能更改 UI 元素。Angular 更新真实的“文档对象模型”(DOM),即 HTML 和 XML 文档的编程接口,而 React 只更新虚拟 DOM。Angular 使用 Typescript,这是一种静态编译语言,提供静态类型、类和接口。用 Typescript 编写代码使代码更健壮,因为它有助于识别常见错误并消除它们,而 React 相比之下只使用 JavaScript。

创建 Angular 应用程序

我们将首先使用 Angular CLI 创建一个 Angular 应用程序,然后在 VS Code 中将 Capacitor 添加到我们的 Angular 应用程序中。您可以在 Mac 上完成此操作,您将能够同时使用 XCODE 和 Android 以及 VS Code,或者您可以在 Windows 上创建 Angular 应用程序,在那里您可以打开带有 Android Studio 的 VS Code,当您准备好时,只需在 Mac 上打开您的 VS Code Angular 项目,即可在 Mac 上的 XCODE 中编译项目的 iPhone 版本。我正在 Mac 上构建这个示例项目,所以大多数命令都会以 sudo 开头,如果您在 Windows 上,只需省略 sudo 命令即可,在 Mac 上表示以管理员身份运行

安装 Node.js 以开始

大多数读者已经在他们的 PC (Window) 和 OS X (Mac) 上安装了 Node.js,但是,如果您没有,请先下载 Node.js 并使用其中一个安装程序从 https://node.org.cn/en/download/ 安装最新版本的 node 并按照步骤操作。

此时,如果您尝试使用 npm,有些人会收到可怕且现在臭名昭著的错误

npm ERR! Windows_NT (some version here) 

如果您在代理后面,则有许多可行的修复方法可以解决此错误,但如果您不在代理后面,则尝试修复此错误可能会让您抓狂。要修复此错误,或者如果您刚刚更新到最新版本的 npm,请在以管理员身份启动的 CMD 窗口中运行以下命令

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

注意:如果您使用的是 Mac,则还必须执行以下操作以避免潜在错误

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

安装 Angular CLI

如果您已经安装了最新版本的 Angular CLI,则可以跳过此步骤,否则,我们应该首先了解 Webpack、System.js 和 angular-cliSystem.js 在 Angular 2 构建之初被大量使用。Webpack 紧随其后发展,最后,现在的事实标准 Angular CLI 演变为 Webpack 的一种包装器,并帮助搭建新项目并轻松创建组件。安装 angular-cli 也将在您的系统上全局安装 Angular 的“ng”命令:安装 angular-cli 的说明位于

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

注意这很关键... 如果您使用的是 Mac,则必须执行以下操作

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

接下来,创建一个目录来存放我们的 Capacitor 项目并给它一个名称,我称之为“Capacitor”。

在 Mac 上,执行以下操作来为您的 Capacitor 项目创建一个目录

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

在上面的代码中,请记住将“stargate”替换为您的 Mac 的名称。在 Windows 上,只需创建一个名为“Capacitor”的文件夹,并将该文件夹及其子文件夹的安全权限设置为“Everyone”或您希望拥有读/写权限的身份。

启动 VS Code 并打开您创建的用于存放 Capacitor 项目的目录,在本例中为“Capacitor”。接下来,转到“视图/终端”菜单并输入以下命令,在您的“Capacitor”文件夹内创建一个普通的 Angular 14 应用程序

sudo ng new SwipeClouds

有关创建 Angular 应用程序的命令行选项的更多详细信息,您可以在此处找到很好的参考资料

关闭 VS Code 并重新启动 VS Code,然后打开“SwipeClouds”文件夹。

通过打开 VS Code 中名为“package.json”的文件来检查 Angular 版本,如果它是版本 14,则无需再做任何事情。如果 angular 版本小于版本 14,则我们按顺序更新到下一个 Angular 版本,如下所示

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

接下来,让我们构建并运行 Angular 应用程序,以测试它是否创建成功且没有错误。要构建它,我们可以运行

sudo ng serve

打开您的网络浏览器到:localhost:4200。

将 Capacitor 添加到我们的 Angular 应用程序中

让我们在 VS Code 的新终端窗口中按以下方式将 Capacitor 添加到我们的 Angular 应用程序中

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

为了在 VS Code 中创建我们的 XCODEAndroid Studio 项目,在将 Capacitor 添加到我们的 Angular 项目后,我们必须执行以下操作。打开文件 angular.json 并将 "outputPath": "dist/{{nameApp}}" 更改为,在本例中为

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

然后在文件 capacitor.config.json 中确保:"webDir": "www"

"webDir": "www",

下一步至关重要... 您必须创建一个名为 "www" 的文件夹,其中包含一个名为 "index.html" 的文件,并将此文件放入您刚刚创建的 "www" 文件夹中,以便 Capacitor 能够将您的 Android Studio 项目或 XCODE 项目添加到 VS Code 中。您创建的 "index.html" 可以是空的,因为它现在只是一个占位符。

要执行此操作,请以管理员权限打开命令窗口并运行

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

在 Windows 上,我们有一个缺点,就是只有 Android Studio 可以运行,所以直到我们将项目移到 Mac 上,我们才能运行 XCODE。我发现我在 Windows 上输入和工作更容易,所以我倾向于在 Windows 上开始我的 Capacitor 项目并完成我的 Android Studio 代码,然后我会将项目移到我的 Mac 上并添加 iOS 并将我的代码添加到 XCODE 中以构建 iPhone 版本。但是大多数拥有 Mac 的用户可能更喜欢在他们的 Mac 上完成所有操作。如果您在 Windows 上,那么您可以将您的 Android Studio 项目添加到 Windows 上的 Angular 应用程序中,然后将您的项目移到 Mac 上并添加您的 XCODE 项目到您的 Angular 应用程序中。

在具有管理员权限的命令窗口中运行以下代码,以在 VS Code 的项目结构中创建您的 Android Studio 项目,如下所示

npx cap init

当提示如下所示时,回答以下内容

在添加的 capacitor.config.ts 文件中进行以下更改

webDir: 'www' 

然后我们按如下方式添加 Android

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

Capacitor 中一个故意的 Bug?

在 Capacitor 3 版本之前,您会运行 "npx cap open android" 来在 Android Studio 中打开您的项目,但是它现在不再会自动打开 Android Studio 了。我在 Ionic 的一个帖子中读到一些关于“解耦 IDE”的内容,我想他们可能是在说在 Capacitor 3 版本中您必须手动在 Android Studio 中打开您的 Android 项目!到目前为止,我一直无法让它自动打开 Android Studio 了!真是可惜!

当您尝试在 Capacitor 3 中运行您的 Android Studio 时,它将无法加载您的 index.html,因为文件 "WebViewLocalServer.java" 中存在一个 bug,如下所示

responseStream = jsInjector.getInjectedStream(responseStream);

正如每个 Java 程序员所知,responseStream 必须被包装在 TRY / CATCH BLOCK 中,如下所示

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

如果你在 Android Studio 中通过调试器进行单步调试,你会发现加载大多数本地文件时 responseStream 将为 null。如果你希望你的应用程序能够运行,你将不得不手动修复 Cpacitor 代码中的这个 bug。

下载 swipeclouds_fixes.zip

为了方便修复上述错误和其他问题,并为 Capacitor 添加更多功能,我创建了一个名为 "swipeclouds_fixes" 的文件夹,您可以从本文顶部下载压缩文件夹,并将其放在 "C:|Capacitor" 文件夹内,通过以管理员权限打开命令窗口并运行以下代码,只需将更改复制到您的项目中即可添加这些更改

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

使用 Capacitor 在 Mac 上创建 XCODE 项目

在 Mac 上,我们有一个优势,即 XCODEAndroid Studio 都可以运行,因此您可以在 Mac 上编译您的 iPhone 应用程序并在 Android Studio 中编译您的 Android 应用程序。如果您在 Mac 上,请执行以下操作来创建您的 Android StudioXCODE 项目

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

之后,我们可以像在 Windows 上一样创建您的 Android Studio 项目,并且在首先添加 Android Studio 项目之后,您可以在 Mac 上创建您的 XCODE 项目,如下所示

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

正如我所说,我更喜欢在 Windows 上工作,所以当我将项目移到我的 Mac 上时,只是为了添加和同步 XCODE 项目。然后我通常会把它移回我的 Windows 机器,在那里我可以在我的 XCODE 项目中编写 Swift 代码,但我无法在我的 Windows 机器上运行它。

您必须对 Capacitor 文件 CAPBridgeViewController.swift 进行的 iOS 更改

// 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)
}

您必须对 Capacitor 文件 BridgeWebViewClient.java 进行的 Android Studio 更改

// 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;
}

指尖乐趣:用手指移动云朵并捏合调整云朵大小

SwipeClouds 兼具 UI 和 UX,因为它看起来很酷,并且吸引用户玩云,这本身就很有趣。SwipeClouds 具有非常不同的外观和感受,以及一种新颖的导航方法,您可以用手指移动和调整 SwipeCloud 中的对象大小。用手指捏合 SwipeCloud 将增加和减小 SwipeCloud 的大小。这个 SwipeCloud 是 Angular 移动应用程序的主要导航方式,点击 SwipeCloud 中的任何图像都将加载不同的视图,在大多数情况下,这将加载来自任何允许嵌入的视频服务器的特定视频组的 VideoComponent 视图。

Image 3

通过捏合手机屏幕来调整滑动云的大小,如下图所示

我们设置 pinchZoom = true

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; }
  }
}

滑动 iOS7 磨砂垂直控制面板

通过点击主滑动云屏幕底部导航栏中的“工具”按钮,您将滑出滑动 iOS7 磨砂垂直控制面板,您可以在其中为该移动应用程序设置其他选项。您可以用手指上下滑动此面板。

滑动云形状

显示的滑动 iOS7 磨砂垂直控制面板还允许您更改浮动云的形状。例如,您可以将圆形云变成垂直环,即控制面板上的 VRing 选项,如下图所示

动画和全屏视频背景

由于我们的主 GUI 是一个 HTML5 Canvas,我们可以轻松地应用动画 gif 文件作为背景或全屏视频背景。我们可以用来自各种来源的视频流(例如本地或远程视频文件,或来自手机本身的前置或后置摄像头)填充画布的整个背景。要添加视频背景,只需使用应用于画布的 Fabric.js 视频功能。要在 SwipeCloud 内部添加视频或动画,您可以在选项数组 oopts 中使用 centreFunc,它允许您创建自己的回调函数,在前后标签之间绘制画布,带有动画或视频。​​​​​如下所示。

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();
}

如下图所示,您可以看到这种动画的一个示例

视频布局适用于纵向和横向视图

纵向与横向布局我决定视频和其他视图的最佳布局是:在纵向模式下保留工具栏和导航栏,而在横向模式下隐藏它们。您可以在下面看到这一点。我添加了一个按钮和代码,用于将手机中选定的视频通过配对从视频服务器站点流式传输到任何智能电视。

云集合... 每个云都是一个主题应用

在下图中,您可以看到各种 SwipeClouds,其中每个 SwipeCloud 代表一个主题,并且每个浮动图像在点击时,可以轻松地将来自 YouTube.com100 多个视频服务器、电视节目以及来自世界各地数百个视频服务器的数百万高清电影加载到 ListView 中,您可以在您的手机上或任何智能电视上观看。

我在此处的 SwipeClouds 项目中添加的移动应用程序主题如下所示

  • swipeCloud - 用于访问其他云、视频和高清电影集以及播放列表的通用云
  • gamesCloud - HTML 游戏和游戏网站的集合
  • eduCloud - 用于学习几乎任何东西的教育视频集合
  • dronesCloud - 关于无人机主题的视频集合,特别是载人无人机
  • solCloud - 关于各种一般健康主题的视频集合
  • newsCloud - 关于新闻主题的随机视频集合

将 SwipeClouds 框架添加到我们的 Angular 应用

SwipeClouds 是我创建的 Graham Breach 的 TagCanvas 的修改版本,它是一个 JavaScript 类,用于绘制和动画基于 HTML5 canvas 的标签云。它作为开源项目在 LGPL v3 许可下发布。

我为这篇文章快速地把 swipeclouds 整合在一起,我需要清理并缩短 scss,但与此同时,我们先进入 "angular.json" 文件,增加我们的 "budgets" 以允许更多的警告和错误,如下所示

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

让我们添加 WebRTC 进行视频会议

我决定在此示例中向 SwipeClouds 添加一个基本的 WebRTC 实现,用于使用 peer.js 进行视频会议,因此您需要在 Windows 上以管理员权限在命令窗口中添加 peer.js,如下所示。

npm install peerjs

为了避免 Angular 和 peer.js 的一些常见错误,您需要在 tsconfig.json 文件中添加以下内容

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

我很快添加了 webRTC,所以它适用于两个人之间的视频聊天,但我编写的代码很容易扩展,可以创建带有房间的视频会议。我按以下方式添加了 peer.js

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,
  };

在 VS Code 的终端中,运行以下命令以测试 swipeclouds 是否正常工作

ng serve

然后打开 Chrome 浏览器,访问:localhost:4200 并调整大小到移动应用程序的大小,您应该会看到浮动的 swipeclouds。由于本文的重点是关于使用 Capacitor 创建 iPhone 和 Android 移动应用程序,我接下来将重点讨论这一点,并在本文的结尾讨论 swipeclouds Angular 应用程序。

SwipeClouds 将视频流式传输到任何智能电视

您会注意到,我在视频列表顶部和导航栏链接中添加了一个按钮,用于将任何视频流式传输到任何智能电视,如下图所示

SwipeCouds 浏览器组件、国际象棋和 HTML5

我还为这个 Angular 移动应用添加了一款出色的 HTML 国际象棋游戏 Stefano Gioffre。将 JQuery 添加到 Angular 的巨大优势在于,有数百款很酷的 HTML 游戏(如国际象棋)可以轻松地通过简单地将它们加载到 iFrame 中来放入示例项目中。因此,我决定添加一个 BrowserComponent,它在 HTML 中包含一个 iFrame,如下图所示,可以加载本地或远程网页。

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

在 BrowserComponent 中,我们按如下方式订阅路由;

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

加载 html 国际象棋非常简单,如下所示

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

使用浏览器组件,您可以轻松地嵌入已经存在的 HTML5 游戏,而您没有时间用 Angular 重写。我包含了对在线游戏(例如《Entropia Universe》)的访问,这些游戏会为射击怪物奖励您真金白银!浏览器组件让您轻松显示本地或远程的 HTML 内容,从您的许可协议到下面显示的国际象棋等现有游戏。

创建 Capacitor 插件以收集用户数据

我创建了一个名为 "InfoMagnet" 的 Capacitor 插件,用于收集用户数据,这些数据对于了解用户如何使用应用程序以及向我们的用户投放目标广告至关重要。让我们首先看看我们可能想了解的用户信息,例如以下内容

  • appId:每个 iPhone 和 Android 应用程序的唯一 ID
  • appName:安装的应用程序名称
  • appVersion:安装的应用程序版本
  • model:手机型号
  • opSystem:手机操作系统
  • osVersion:手机操作系统版本
  • phone:电话号码
  • email:电子邮件地址
  • name:注册手机用户姓名
  • city:注册用户所在城市
  • state:注册用户所在州
  • ctry:注册用户所在国家
  • pc:注册用户的邮政编码
  • latitude:手机纬度
  • longitude:手机经度
  • ipaddress:手机的第一个 IP 地址
  • ipaddress2:手机的第二个 IP 地址

请理解,未经用户许可,我们无法在 iPhone 和 Android 上收集全部或部分这些信息,因此始终由用户决定我们将收集哪些信息。

InfoMagnet 的一部分收集用户数据,另一部分重试用户联系人。我很快创建了这个插件,没有机会完成插件中的所有 swift 代码。但我将在接下来的几周内完成代码的 iPhone 部分,并更新本文以包含该代码以及 Capacitor 3.0 版本的更改。

我们首先创建一个文件夹来存放我们的 Capacitor 插件项目,在 Windows 上使用以管理员身份运行的命令窗口,如下所示

md C:\Capacitor_plugins

我们按如下方式创建 Capacitor 插件

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

然后我们将使用以下命令构建我们的插件

ng build

添加 InfoMagnet 插件的 zip 文件。

在没有自己的注册表的情况下安装您的私有 InfoMagnet NPM 模块

在 Windows 上,您可以如下所示,在没有注册表的情况下将 InfoMagnet 插件安装到我们的 Angular 应用程序中

npm install C:\Capacitor_plugins\infomagnet --save

在 Max 上,将我们的 InfoMagnet 插件添加到我们的 Angular 应用程序中,如下所示

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

InfoMagnet Capacitor 插件的 Android Studio 设置

打开 Android Studio,如果主应用程序的 MainActivity 文件中还没有这段代码,则将其添加到其中

// 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

也可以使用 Kotlin 为您的应用程序或 Capacitor 插件开发自定义代码。在 Android Studio 中添加新的 Kotlin 文件时,如果需要,系统会提示您在项目中配置 Kotlin。执行此操作时,请确保仅在您的应用程序模块中配置 Kotlin,而不是在 Capacitor 或第三方模块中。

InfoMagnet Capacitor 插件的 XCODE 设置

打开 XCODE,并将以下代码添加到我们的 InfoMagnet 插件中的 Plugin.m 文件中(如果尚未存在)。请注意,您必须使用以分号结尾的方式声明插件中的每个方法,如下所示

#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);
)

对我们的 Android 应用程序代码的更改,以使我们的插件工作

我认为创建一个自定义 Capacitor 插件会很有帮助,因为很可能您几乎为您创建的任何应用程序都需要创建一个。我称这个插件为 "InfoMagnet",因为它会从用户的手机上抓取用户数据,以帮助我们投放目标营销。当然,用户必须同意我们实现此目的所需的权限,所以我们将首先添加 Java 和 Swift 代码来完成此操作。我们可以将所有权限调用放在我们的插件中,但我发现如果您将权限代码添加到原生应用程序中,事情会更顺利。

任何 Android 程序员都会注意到,我们的 Android 应用程序中的 Android 代码的当前功能不需要所有这些权限,但我添加了一些权限,以备将来几周内可能想添加到此应用程序中的功能。我们按如下方式修改主应用程序中的 MainActivity.java 文件

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);
    }
  }

可怕的 ERR_CLEARTEXT_NOT_PERMITTED

这可能是混合应用程序中最常见的错误消息,它是由 Android 混合移动应用程序中未设置权限的简单原因引起的。请查看 Android Manifest 文件,确保您有以下代码

<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">

注意代码行

android:networkSecurityConfig="@xml/network_security_config"

此外部 XML 文件包含 Android 混合移动应用程序中所需的额外权限,如下所示

<?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>

绕过基于网络的应用程序的 SSL 证书

到目前为止,我们假设您的 JavaScript 框架 UI/UX 是从 Android 或 iPhone 运行的,但在许多情况下,您会希望简单地运行一个用任何语言(React.js、Angular、.NET 等)编写的基于服务器的 Web 应用程序,该应用程序位于 HTTPS 之后。这种响应式基于 Web 的应用程序被称为渐进式 Web 应用程序 (PWA)。

绝不会使用 Ionic 的方法来构建渐进式 Web 应用程序 (PWA)。始终按照您创建将驻留在移动电话上的应用程序的方式来创建驻留在远程服务器上的基于 Web 的应用程序,并且我们的移动应用程序将加载基于服务器的代码和驻留在移动电话上的代码。移动电话上的代码将允许我们加载插件并访问我们无法从基于 Web 的应用程序执行的原生代码。

Ionic 故意限制了他们的网络服务器,因此如果使用自签名证书或来自未经批准来源的某些 SSL 证书,WebView 将不允许导航,这给基于 Web 的应用程序带来了问题,因此您需要修改 Bridge.java 文件中的代码,如下所示

// 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);

在 iOS 中,您只需执行相同的操作即可加载位于 https 后面的基于 Web 的应用程序。

配置 iOS

配置 Info.plist

iOS 开发者会熟悉使用 Info.plist 文件,它是 iPhone 的主要配置文件。Info.plist 是一个纯 XML 文件,可以直接编辑,我更喜欢这种方式。请务必在 Info.plist 中为 XML <key> 值使用低级参数名称。要编辑原始 XML,请右键单击属性列表编辑器中的任意位置并切换“显示原始键/值”。您的 Capacitor 插件可能需要额外的应用程序配置,以及应用程序将请求的权限。

与 Android 不同,iOS 的权限无需预先指定。相反,它们在使用特定插件或 SDK 时会提示。许多 iOS 权限需要 Info.plist 中定义的所谓“使用说明”。请查阅Cocoa Keys列表,查找包含UsageDescription的键,以查看您的应用可能需要的各种使用说明设置。

对于本项目中的应用程序,您可以在本文顶部下载 Info.plist 文件。

配置 Android

配置 AndroidManifest.xml

Android 应用程序通过修改 AndroidManifest.xml 来管理权限、设备功能和其他设置。此文件引用了 res/values/ 中其他文件的值,以便于单独更新它们,包括 styles.xmlstrings.xml。您可以从本文顶部下载此项目的 AndroidManifest.xml 文件。

已将联系人添加到 InfoMagnet

我已将联系人添加到 InfoMagnet,您可以按如下方式调用

  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();
  }

屏幕看起来像这样,您可以通过点击电话号码或电子邮件前面的图标来拨打电话、发送短信或发送电子邮件。

深层链接(又称 Android 应用链接)

要通过 Android 应用链接启用深层链接,请按照官方 Android 指南添加 Android 应用链接。Android Studio 附带一个方便的向导,用于配置应用链接。指向您服务器的深层链接可以动态流式传输您服务器上使用 Chart.js 创建的服务器内容,例如图表。

XCODE 和 Android Studio 中的锚点与插件

此时,在添加 Capacitor 后,我们的 Angular 项目中同时拥有 XCODEAndroid Studio 项目。从技术上讲,您不需要了解任何 Swift、Objective C、C/C++、Java 或 Kotlin 等原生编程语言即可完成您的 iPhone 或 Android 应用程序的构建,但我将在此处演示两种修改 XCODE 和 Android Studio 的方法,这些方法假设您了解 Swift 代码和 Java,以展示 Capacitor 的强大功能。一种方法是直接在我们的 XCODE 和 Android Studio 项目中编写 Swift 或 Java 代码,另一种方法是编写一个自定义 Capacitor 插件,我们也将这样做。

例如,在混合移动应用程序中,您总是希望在可能的情况下使用锚点,因为当从 JavaScript 调用锚点时,它们将即时、同步地调用您的原生语言中的方法,这是调用任何原生语言方法的最快方式。锚点比使用任何 Capacitor 插件或 Capacitor 的原生桥接调用原生语言方法要快得多。锚点和插件都有其用武之地。如果您不需要将任何数据返回给 JavaScript,请使用锚点;如果您需要将数据返回给 JavaScript,请使用 Capacitor 插件。

让我们使用 Java 在 Android Studio 中添加一些锚点

您可以在 Android Studio 和 XCODE 中多次覆盖此项目的 "shouldOverrideUrlLoading"。如果您在 Android Studio 中打开项目,您可以直接将文章顶部的 BridgeWebViewClient.java 代码粘贴到 Android Studio 内的该文件中,Capacitor 将自动更新您 Angular 应用程序项目中的 Capacitor 的 node_modules 中的代码。这种方法修改了 Capacitor 的 node_modules 的源代码,许多程序员可能不愿这样做。对于本文,我修改了 BridgeWebViewClient.java 文件,以演示 Capacitor 自动更新 node_modules 的能力。您还可以在我们的 InfoMagnet 插件等插件中覆盖 "shouldOverrideUrlLoading",网上有 Capacitor 的此方法的示例。

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

我在 Android Studio 的 BridgeWebViewClient.java 文件中添加了一些锚点,更改如下

   @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);
    }
  }

让我们使用 Swift 在 XCODE 中添加一些锚点

XCODECAPBridgeViewController.swift 文件中,添加以下 Java 代码

    for XCODE

在 ListView 中插入带间隔的电视广告

我们以重复的方式在 ListView 中插入电视广告,插入一个电视广告后,跳过 4 行,然后重复。这允许我们以非侵入式的方式包含电视广告,如下图所示

  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;
  }

下图显示了 ListView 中一次显示 5 个视频,该 ListView 最多可容纳 1,000 个视频。要访问接下来的 1,000 个视频,您需要按下导航栏底部的“下一个”按钮。如上图代码所示,该 ListView 将在电视广告之间显示 4 个视频。您可以通过视频中通常显示时间码的位置的红色“WATCH”条带识别电视广告。SwipeClouds 允许您在您的移动应用程序中免费访问来自 YouTube.com、Yoku.com 等 100 多个视频服务器的数百万视频和高清电影。我收录了一些非常酷的操作视频,例如如何建造俄罗斯 Viktor Grebennikov 反重力平台和如何建造俄罗斯 Alexey Chekurkov 反重力装置。

为了用红色背景的单词“WATCH”替换上面显示的时间码,我们在 HTML 代码中使用 *ngIf="feed.category==='tvads'",如下图所示

 <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 服务器播放数百万部高清电影

本文中的示例移动应用程序,即 SwipeClouds,将视频元数据内部存储在

./assets/data/*.json

视频元数据以 JSON 格式存储,并从名称与所请求视频类别匹配的文件中检索。在其他版本中,视频元数据从 JSONP 服务器检索。您可以通过几行 PHP 代码轻松地在服务器上运行 JSONP 服务器,也可以使用 C# .NET 等。

我用于 JSONP 服务器的设计理念是,不是拥有多个通用处理程序或一个具有大量 switch/if 语句的单个处理程序,我决定使用一个带有工厂设计模式的单个通用处理程序。工厂根据传递的 methodName 返回一个类,该类仅用于处理该请求。工厂从 web.config 文件中读取 methodName 及其类,并实例化处理程序。通用处理程序向工厂请求处理程序,并执行一些前/后处理。

这里有一个我写的关于使用 C# .NET 创建 JSONP 视频和电视广告服务器的文章链接,我用它来测试这个 Angular 移动应用程序中的 JSONP 代码,以流式传输数百万个视频服务器视频、电影、电视节目和电视广告

JSONP 服务器,用于从数百个视频服务器流式传输视频、电影、电视节目、视频频道和电视广告
 C# .NET JSONP 服务器

最终想法

本文中构建的基于 Ionic's Capacitor 的 Angular 移动应用旨在从数百个视频服务器(例如 YouTube.com 和全球最大的电视网络 优酷网 (Youku.com))提供数百万个视频、电影、电视节目和电视广告。如果您在 YouTube 或任何视频服务器上有一个频道,您可以通过将其放置在像本文顶部链接中那样的 JSONP 服务器上来远程将该频道添加到此应用中。

我将在未来几周内更新本文,其中包含我编写的许多新的 Capacitor 插件,例如 OCR (光学字符识别),一个使用我修改过的据称 CIA 用于检测人类语音中压力(即“撒谎”)的代码的语音压力分析仪插件

您可以从我的网站下载已编译的 iPhone 和 Android SwipeClouds® 移动应用程序

使用 SwipeClouds® Angular 移动框架构建出色的移动应用程序[^]

历史

  • 2017年3月7日:初始版本
  • 2021年1月27日:更新文章以使用 Ionic Capacitor
如何使用 Angular 14、Ionic Capacitor 3、SwipeClouds 和 WebRTC 构建 iPhone 和 Android 移动应用程序 - CodeProject - 代码之家
© . All rights reserved.