Compare commits

..

No commits in common. "master" and "accel_hack" have entirely different histories.

340 changed files with 5884 additions and 35131 deletions

View File

@ -1,46 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
github:
description: Apache Cordova Android
homepage: https://cordova.apache.org/
labels:
- cordova
- cordova-platform
- android
- java
- mobile
- javascript
- nodejs
- hacktoberfest
features:
wiki: false
issues: true
projects: true
enabled_merge_buttons:
squash: true
merge: false
rebase: false
notifications:
commits: commits@cordova.apache.org
issues: issues@cordova.apache.org
pullrequests_status: issues@cordova.apache.org
pullrequests_comment: issues@cordova.apache.org

View File

@ -1,3 +0,0 @@
templates/project/assets/www/cordova.js
test/android/app
test/androidx/app

View File

@ -1,27 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
extends: '@cordova/eslint-config/node'
overrides:
- files: [spec/**/*.js]
extends: '@cordova/eslint-config/node-tests'
rules:
prefer-promise-reject-errors: off
- files: [cordova-js-src/**/*.js]
extends: '@cordova/eslint-config/browser'

94
.gitattributes vendored
View File

@ -1,94 +0,0 @@
* text eol=lf
# source code
*.php text
*.css text
*.sass text
*.scss text
*.less text
*.styl text
*.js text
*.coffee text
*.json text
*.htm text
*.html text
*.xml text
*.svg text
*.txt text
*.ini text
*.inc text
*.pl text
*.rb text
*.py text
*.scm text
*.sql text
*.sh text
*.bat text
# templates
*.ejs text
*.hbt text
*.jade text
*.haml text
*.hbs text
*.dot text
*.tmpl text
*.phtml text
# server config
.htaccess text
# git config
.gitattributes text
.gitignore text
.gitconfig text
# code analysis config
.jshintrc text
.jscsrc text
.jshintignore text
.csslintrc text
# misc config
*.yaml text
*.yml text
.editorconfig text
# build config
*.npmignore text
*.bowerrc text
# Heroku
Procfile text
.slugignore text
# Documentation
*.md text
LICENSE text
AUTHORS text
#
## These files are binary and should be left untouched
#
# (binary is a macro for -text -diff)
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.mov binary
*.mp4 binary
*.mp3 binary
*.flv binary
*.fla binary
*.swf binary
*.gz binary
*.zip binary
*.7z binary
*.ttf binary
*.eot binary
*.woff binary
*.pyc binary
*.pdf binary

View File

@ -1,42 +0,0 @@
<!--
Please have a look at the issue templates you get when you click "New issue" in the GitHub UI.
We very much prefer issues created by using one of these templates.
-->
### Issue Type
<!-- Please check the boxes by putting an x in the [ ] like so: [x] -->
- [ ] Bug Report
- [ ] Feature Request
- [ ] Support Question
## Description
## Information
<!-- Include all relevant information that might help understand and reproduce the problem -->
### Command or Code
<!-- What command or code is needed to reproduce the problem? -->
### Environment, Platform, Device
<!-- In what environment, on what platform or on which device are you experiencing the issue? -->
### Version information
<!--
What are relevant versions you are using?
For example:
Cordova: Cordova CLI, Cordova Platforms, Cordova Plugins
Other Frameworks: Ionic Framework and CLI version
Operating System, Android Studio, Xcode etc.
-->
## Checklist
<!-- Please check the boxes by putting an `x` in the `[ ]` like so: `[x]` -->
- [ ] I searched for already existing GitHub issues about this
- [ ] I updated all Cordova tooling to their most recent version
- [ ] I included all the necessary information above

View File

@ -1,50 +0,0 @@
---
name: 🐛 Bug Report
about: If something isn't working as expected.
---
# Bug Report
## Problem
### What is expected to happen?
### What does actually happen?
## Information
<!-- Include all relevant information that might help understand and reproduce the problem -->
### Command or Code
<!-- What command or code is needed to reproduce the problem? -->
### Environment, Platform, Device
<!-- In what environment, on what platform or on which device are you experiencing the issue? -->
### Version information
<!--
What are relevant versions you are using?
For example:
Cordova: Cordova CLI, Cordova Platforms, Cordova Plugins
Other Frameworks: Ionic Framework and CLI version
Operating System, Android Studio, Xcode etc.
-->
## Checklist
<!-- Please check the boxes by putting an x in the [ ] like so: [x] -->
- [ ] I searched for existing GitHub issues
- [ ] I updated all Cordova tooling to most recent version
- [ ] I included all the necessary information above

View File

@ -1,29 +0,0 @@
---
name: 🚀 Feature Request
about: A suggestion for a new functionality
---
# Feature Request
## Motivation Behind Feature
<!-- Why should this feature be implemented? What problem does it solve? -->
## Feature Description
<!--
Describe your feature request in detail
Please provide any code examples or screenshots of what this feature would look like
Are there any drawbacks? Will this break anything for existing users?
-->
## Alternatives or Workarounds
<!--
Describe alternatives or workarounds you are currently using
Are there ways to do this with existing functionality?
-->

View File

@ -1,27 +0,0 @@
---
name: 💬 Support Question
about: If you have a question, please check out our Slack or StackOverflow!
---
<!------------^ Click "Preview" for a nicer view! -->
Apache Cordova uses GitHub Issues as a feature request and bug tracker _only_.
For usage and support questions, please check out the resources below. Thanks!
---
You can get answers to your usage and support questions about **Apache Cordova** on:
* Slack Community Chat: https://cordova.slack.com (you can sign-up at http://slack.cordova.io/)
* StackOverflow: https://stackoverflow.com/questions/tagged/cordova using the tag `cordova`
---
If you are using a tool that uses Cordova internally, like e.g. Ionic, check their support channels:
* **Ionic Framework**
* [Ionic Community Forum](https://forum.ionicframework.com/)
* [Ionic Worldwide Slack](https://ionicworldwide.herokuapp.com/)
* **PhoneGap**
* [PhoneGap Developer Community](https://forums.adobe.com/community/phonegap)

View File

@ -1,35 +0,0 @@
<!--
Please make sure the checklist boxes are all checked before submitting the PR. The checklist is intended as a quick reference, for complete details please see our Contributor Guidelines:
http://cordova.apache.org/contribute/contribute_guidelines.html
Thanks!
-->
### Platforms affected
### Motivation and Context
<!-- Why is this change required? What problem does it solve? -->
<!-- If it fixes an open issue, please link to the issue here. -->
### Description
<!-- Describe your changes in detail -->
### Testing
<!-- Please describe in detail how you tested your changes. -->
### Checklist
- [ ] I've run the tests to see all new and existing tests pass
- [ ] I added automated test coverage as appropriate for this change
- [ ] Commit is prefixed with `(platform)` if this change only applies to one platform (e.g. `(android)`)
- [ ] If this Pull Request resolves an issue, I linked to the issue in the text above (and used the correct [keyword to close issues using keywords](https://help.github.com/articles/closing-issues-using-keywords/))
- [ ] I've updated the documentation if necessary

View File

@ -1,64 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
name: Node CI
on: [push, pull_request]
jobs:
test:
name: NodeJS ${{ matrix.node-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
node-version: [16.x, 18.x, 20.x]
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: set up JDK 11
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '11'
- name: Environment Information
run: |
node --version
npm --version
gradle --version
- name: npm install and test
run: |
npm i
npm t
env:
CI: true
- name: upload coverage
if: success()
uses: codecov/codecov-action@v3
with:
name: ${{ runner.os }} node.js ${{ matrix.node-version }}

57
.gitignore vendored
View File

@ -1,57 +1,6 @@
.DS_Store
.gradle
.metadata
Thumbs.db
Desktop.ini
*.tmp
*.bak
*.swp
*.class
*.jar
default.properties
bin
gen
assets/www/cordova.js
assets/www/phonegap.js
local.properties
proguard.cfg
proguard-project.txt
example
/coverage
/framework/lib
/framework/build
/framework/bin
/framework/assets/www/.DS_Store
/framework/assets/www/cordova-*.js
/framework/assets/www/phonegap-*.js
/framework/libs
/framework/javadoc-public
/framework/javadoc-private
**/assets/www/cordova.js
/test/.externalNativeBuild
/test/androidx/gradle
/test/androidx/gradlew
/test/androidx/gradlew.bat
/test/androidx/cdv-gradle-config.json
/test/assets/www/.tmp*
/test/assets/www/cordova.js
/test/bin
/test/build
/test/captures
/test/libs
tmp/**
tmp/**/*
!/spec/fixtures/org.test.plugins.dummyplugin/src/android/TestLib.jar
# IntelliJ IDEA files
**/.idea/**/*
*.iml
npm-debug.log
node_modules/
coverage/
.nyc_output/
# Eclipse Buildship files
.project
.settings
.classpath
framework/phonegap.jar

View File

@ -1,6 +0,0 @@
.*
coverage
test
spec
framework/build
cordova-js-src

2
.npmrc
View File

@ -1,2 +0,0 @@
registry=https://registry.npmjs.org

View File

@ -1,8 +0,0 @@
*.properties
templates
gen
proguard-project.txt
spec
framework/build
ic_launcher.png
build

View File

@ -1,8 +0,0 @@
#
# Settings for post-review (used for uploading diffs to reviews.apache.org).
#
GUESS_FIELDS = True
OPEN_BROWSER = True
TARGET_GROUPS = 'cordova'
REVIEWBOARD_URL = 'https://reviews.apache.org'

View File

@ -1,37 +0,0 @@
<!--
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
-->
# Contributing to Apache Cordova
Anyone can contribute to Cordova. And we need your contributions.
There are multiple ways to contribute: report bugs, improve the docs, and
contribute code.
For instructions on this, start with the
[contribution overview](http://cordova.apache.org/contribute/).
The details are explained there, but the important items are:
- Check for Github issues that corresponds to your contribution and link or create them if necessary.
- Run the tests so your patch doesn't break existing functionality.
We look forward to your contributions!

219
LICENSE
View File

@ -1,202 +1,21 @@
Copyright (c) 2010 Nitobi
website: http://phonegap.com
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Software), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015-2020 Apache Cordova
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

5
NOTICE
View File

@ -1,5 +0,0 @@
Apache Cordova
Copyright 2015-2020 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

View File

@ -1,71 +1,46 @@
<!--
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
-->
PhoneGap/Android
================
PhoneGap/Android is an Android application library that allows for PhoneGap based projects to be built for the Android Platform. PhoneGap based applications are, at the core, an application written with web technology: HTML, CSS and JavaScript.
# Cordova Android
Pre-requisites
--------------
- Java JDK 1.5
- Android SDK Package [http://developer.android.com](http://developer.android.com)
- Apache ANT
- Ruby
[![NPM](https://nodei.co/npm/cordova-android.png)](https://nodei.co/npm/cordova-android/)
Recommended
-----------
- Eclipse (Great for debugging and extending PhoneGap/Android with your own Java plugins.)
[![Node CI](https://github.com/apache/cordova-android/workflows/Node%20CI/badge.svg?branch=master)](https://github.com/apache/cordova-android/actions?query=branch%3Amaster)
[![codecov.io](https://codecov.io/github/apache/cordova-android/coverage.svg?branch=master)](https://codecov.io/github/apache/cordova-android?branch=master)
Getting Started with PhoneGap/Android
--------------------------------------
Cordova Android is an Android application library that allows for Cordova-based projects to be built for the Android Platform. Cordova based applications are, at the core, applications written with web technology: HTML, CSS and JavaScript.
1. From the root of this repo run the following command to generate a new PhoneGap/Android app:
[Apache Cordova](https://cordova.apache.org/) is a project of [The Apache Software Foundation (ASF)](https://apache.org/).
<pre>
./droidgap [android_sdk_path] [name] [package_name] [www] [path]
## Requirements
android_sdk_path ... The path to your Android SDK install.
name ............... The name of your application.
package_name ....... The name of your package (For example: com.nitobi.demo)
www ................ The path to your www folder. (Wherein your HTML, CSS and JS app is.)
path ............... The path to generate the application.
</pre>
* Java Development Kit (JDK) 11
* [Android SDK](https://developer.android.com/)
* [Node.js](https://nodejs.org)
Thats it!
## Create a Cordova project
Importing a PhoneGap/Android app into Eclipse
---------------------------------------------
Follow the instructions in the [**Create your first Cordova app**](https://cordova.apache.org/docs/en/latest/guide/cli/index.html) section of [Apache Cordova Docs](https://cordova.apache.org/docs/en/latest/)
1. File > New > Project...
2. Android > Android Project
3. Create project from existing source (point to the generated app). This should import the project into Eclipse. Almost done!
4. Right click on libs/phonegap.jar and add to build path.
5. Right click on the project root: Run as > Run Configurations
6. Click on the Target tab and select Manual (this way you can choose the emulator or device to build to).
To use a **shared framework**, for example in development, link the appropriate cordova-android platform folder path:
```bash
cordova platform add --link /path/to/cordova-android
```
## Updating a Cordova project
When you install a new version of the [`Cordova CLI`](https://www.npmjs.com/package/cordova) that pins a new version of the [`Cordova-Android`](https://www.npmjs.com/package/cordova-android) platform, you can follow these simple upgrade steps within your project:
```bash
cordova platform rm android
cordova platform add android
```
## Debugging in Android Studio
Import project in Android Studio through _File > Open_ and targeting `/path/to/your-cdv-project/platforms/android/`.
## How to Test Repo Development
```bash
npm install
npm test
```
## Further reading
* [Apache Cordova](https://cordova.apache.org/)
For more info see
-----------------
- [http://docs.phonegap.com](http://docs.phonegap.com)
- [http://wiki.phonegap.com](http://wiki.phonegap.com)

File diff suppressed because it is too large Load Diff

147
assets/www/index.html Normal file
View File

@ -0,0 +1,147 @@
<!DOCTYPE HTML>
<html>
<head>
<meta name="viewport" content="width=320; user-scalable=no" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>PhoneGap</title>
<link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title" charset="utf-8">
<script type="text/javascript" charset="utf-8" src="phonegap.js"></script>
<script type="text/javascript" charset="utf-8">
var deviceInfo = function(){
document.getElementById("platform").innerHTML = device.platform;
document.getElementById("version").innerHTML = device.version;
document.getElementById("uuid").innerHTML = device.uuid;
console.log("Height:" + window.innerHeight);
console.log("Width:" + window.innerWidth);
}
var getLocation = function() {
var suc = function(p){
alert(p.coords.latitude + " " + p.coords.longitude);
};
var fail = function(){};
navigator.geolocation.getCurrentPosition(suc,fail);
}
var beep = function(){
navigator.notification.beep(2);
}
var vibrate = function(){
navigator.notification.vibrate(0);
}
var getContact = function(){
var suc = function(c){ alert("Contact 4: " + c.contacts[3].name); };
var fail = function(){};
navigator.ContactManager.get(suc, fail);
}
var watchAccel = function() {
var suc = function(a){
document.getElementById('x').innerHTML = roundNumber(a.x);
document.getElementById('y').innerHTML = roundNumber(a.y);
document.getElementById('z').innerHTML = roundNumber(a.z);
};
var fail = function(){};
var opt = {};
opt.frequency = 100;
timer = navigator.accelerometer.watchAcceleration(suc,fail,opt);
}
function roundNumber(num) {
var dec = 3;
var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
return result;
}
var preventBehavior = function(e) {
e.preventDefault();
};
function show_pic()
{
var viewport = document.getElementById('viewport');
viewport.style.display = "";
navigator.camera.getPicture(dump_pic, fail, { quality: 50 });
}
function dump_pic(data)
{
var viewport = document.getElementById('viewport');
console.log(data);
viewport.style.display = "";
viewport.style.position = "absolute";
viewport.style.top = "10px";
viewport.style.left = "10px";
document.getElementById("test_img").src = "data:image/jpeg;base64," + data;
}
function close()
{
var viewport = document.getElementById('viewport');
viewport.style.position = "relative";
viewport.style.display = "none";
}
function fail(fail)
{
alert(fail);
}
// This is just to do this.
function readFile()
{
navigator.file.read('/sdcard/phonegap.txt', fail , fail);
}
function writeFile()
{
navigator.file.write('foo.txt', "This is a test of writing to a file", fail, fail);
}
function get_contacts()
{
navigator.ContactManager.getAllContacts(count_contacts, fail, null);
}
function count_contacts(contacts)
{
alert(contacts.length);
}
function init(){
document.addEventListener("touchmove", preventBehavior, false);
document.addEventListener("deviceready", deviceInfo, true);
}
</script>
</head>
<body onload="init();" id="stage" class="theme">
<h1>Welcome to PhoneGap!</h1>
<h2>this file is located at assets/index.html</h2>
<div id="info">
<h4>Platform: <span id="platform">&nbsp;</span></h4>
<h4>Version: <span id="version">&nbsp;</span></h4>
<h4>UUID: <span id="uuid">&nbsp;</span></h4>
</div>
<dl id="accel-data">
<dt>X:</dt><dd id="x">&nbsp;</dd>
<dt>Y:</dt><dd id="y">&nbsp;</dd>
<dt>Z:</dt><dd id="z">&nbsp;</dd>
</dl>
<a href="#" class="btn large" onclick="watchAccel();">Watch Accelerometer</a>
<a href="#" class="btn large" onclick="getLocation();">Get Location</a>
<a href="tel://411" class="btn large">Call 411</a>
<a href="#" class="btn large" onclick="beep();">Beep</a>
<a href="#" class="btn large" onclick="vibrate();">Vibrate</a>
<a href="#" class="btn large" onclick="show_pic();">Get a Picture</a>
<a href="#" class="btn large" onclick="get_contacts();">Get phone's contacts</a>
<div id="viewport" class="viewport" style="display: none;">
<img style="width:60px;height:60px" id="test_img" src="" />
</div>
</body>
</html>

View File

@ -1,36 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Exports the ExposedJsApi.java object if available, otherwise exports the PromptBasedNativeApi.
*/
var nativeApi = this._cordovaNative || require('cordova/android/promptbasednativeapi');
var currentApi = nativeApi;
module.exports = {
get: function () { return currentApi; },
setPreferPrompt: function (value) {
currentApi = value ? require('cordova/android/promptbasednativeapi') : nativeApi;
},
// Used only by tests.
set: function (value) {
currentApi = value;
}
};

View File

@ -1,35 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* Implements the API of ExposedJsApi.java, but uses prompt() to communicate.
* This is used pre-JellyBean, where addJavascriptInterface() is disabled.
*/
module.exports = {
exec: function (bridgeSecret, service, action, callbackId, argsJson) {
return prompt(argsJson, 'gap:' + JSON.stringify([bridgeSecret, service, action, callbackId]));
},
setNativeToJsBridgeMode: function (bridgeSecret, value) {
prompt(value, 'gap_bridge_mode:' + bridgeSecret);
},
retrieveJsMessages: function (bridgeSecret, fromOnlineEvent) {
return prompt(+fromOnlineEvent, 'gap_poll:' + bridgeSecret);
}
};

287
cordova-js-src/exec.js vendored
View File

@ -1,287 +0,0 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
/**
* Execute a cordova command. It is up to the native side whether this action
* is synchronous or asynchronous. The native side can return:
* Synchronous: PluginResult object as a JSON string
* Asynchronous: Empty string ""
* If async, the native side will cordova.callbackSuccess or cordova.callbackError,
* depending upon the result of the action.
*
* @param {Function} success The success callback
* @param {Function} fail The fail callback
* @param {String} service The name of the service to use
* @param {String} action Action to be run in cordova
* @param {String[]} [args] Zero or more arguments to pass to the method
*/
var cordova = require('cordova');
var nativeApiProvider = require('cordova/android/nativeapiprovider');
var utils = require('cordova/utils');
var base64 = require('cordova/base64');
var channel = require('cordova/channel');
var jsToNativeModes = {
PROMPT: 0,
JS_OBJECT: 1
};
var nativeToJsModes = {
// Polls for messages using the JS->Native bridge.
POLLING: 0,
// For LOAD_URL to be viable, it would need to have a work-around for
// the bug where the soft-keyboard gets dismissed when a message is sent.
LOAD_URL: 1,
// For the ONLINE_EVENT to be viable, it would need to intercept all event
// listeners (both through addEventListener and window.ononline) as well
// as set the navigator property itself.
ONLINE_EVENT: 2,
EVAL_BRIDGE: 3
};
var jsToNativeBridgeMode; // Set lazily.
var nativeToJsBridgeMode = nativeToJsModes.EVAL_BRIDGE;
var pollEnabled = false;
var bridgeSecret = -1;
var messagesFromNative = [];
var isProcessing = false;
var resolvedPromise = typeof Promise === 'undefined' ? null : Promise.resolve();
var nextTick = resolvedPromise ? function (fn) { resolvedPromise.then(fn); } : function (fn) { setTimeout(fn); };
function androidExec (success, fail, service, action, args) {
if (bridgeSecret < 0) {
// If we ever catch this firing, we'll need to queue up exec()s
// and fire them once we get a secret. For now, I don't think
// it's possible for exec() to be called since plugins are parsed but
// not run until until after onNativeReady.
throw new Error('exec() called without bridgeSecret');
}
// Set default bridge modes if they have not already been set.
// By default, we use the failsafe, since addJavascriptInterface breaks too often
if (jsToNativeBridgeMode === undefined) {
androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
}
// If args is not provided, default to an empty array
args = args || [];
// Process any ArrayBuffers in the args into a string.
for (var i = 0; i < args.length; i++) {
if (utils.typeName(args[i]) === 'ArrayBuffer') {
args[i] = base64.fromArrayBuffer(args[i]);
}
}
var callbackId = service + cordova.callbackId++;
var argsJson = JSON.stringify(args);
if (success || fail) {
cordova.callbacks[callbackId] = { success: success, fail: fail };
}
var msgs = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson);
// If argsJson was received by Java as null, try again with the PROMPT bridge mode.
// This happens in rare circumstances, such as when certain Unicode characters are passed over the bridge on a Galaxy S2. See CB-2666.
if (jsToNativeBridgeMode === jsToNativeModes.JS_OBJECT && msgs === '@Null arguments.') {
androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT);
androidExec(success, fail, service, action, args);
androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
} else if (msgs) {
messagesFromNative.push(msgs);
// Always process async to avoid exceptions messing up stack.
nextTick(processMessages);
}
}
androidExec.init = function () {
bridgeSecret = +prompt('', 'gap_init:' + nativeToJsBridgeMode);
channel.onNativeReady.fire();
};
function pollOnceFromOnlineEvent () {
pollOnce(true);
}
function pollOnce (opt_fromOnlineEvent) {
if (bridgeSecret < 0) {
// This can happen when the NativeToJsMessageQueue resets the online state on page transitions.
// We know there's nothing to retrieve, so no need to poll.
return;
}
var msgs = nativeApiProvider.get().retrieveJsMessages(bridgeSecret, !!opt_fromOnlineEvent);
if (msgs) {
messagesFromNative.push(msgs);
// Process sync since we know we're already top-of-stack.
processMessages();
}
}
function pollingTimerFunc () {
if (pollEnabled) {
pollOnce();
setTimeout(pollingTimerFunc, 50);
}
}
function hookOnlineApis () {
function proxyEvent (e) {
cordova.fireWindowEvent(e.type);
}
// The network module takes care of firing online and offline events.
// It currently fires them only on document though, so we bridge them
// to window here (while first listening for exec()-releated online/offline
// events).
window.addEventListener('online', pollOnceFromOnlineEvent, false);
window.addEventListener('offline', pollOnceFromOnlineEvent, false);
cordova.addWindowEventHandler('online');
cordova.addWindowEventHandler('offline');
document.addEventListener('online', proxyEvent, false);
document.addEventListener('offline', proxyEvent, false);
}
hookOnlineApis();
androidExec.jsToNativeModes = jsToNativeModes;
androidExec.nativeToJsModes = nativeToJsModes;
androidExec.setJsToNativeBridgeMode = function (mode) {
if (mode === jsToNativeModes.JS_OBJECT && !window._cordovaNative) {
mode = jsToNativeModes.PROMPT;
}
nativeApiProvider.setPreferPrompt(mode === jsToNativeModes.PROMPT);
jsToNativeBridgeMode = mode;
};
androidExec.setNativeToJsBridgeMode = function (mode) {
if (mode === nativeToJsBridgeMode) {
return;
}
if (nativeToJsBridgeMode === nativeToJsModes.POLLING) {
pollEnabled = false;
}
nativeToJsBridgeMode = mode;
// Tell the native side to switch modes.
// Otherwise, it will be set by androidExec.init()
if (bridgeSecret >= 0) {
nativeApiProvider.get().setNativeToJsBridgeMode(bridgeSecret, mode);
}
if (mode === nativeToJsModes.POLLING) {
pollEnabled = true;
setTimeout(pollingTimerFunc, 1);
}
};
function buildPayload (payload, message) {
var payloadKind = message.charAt(0);
if (payloadKind === 's') {
payload.push(message.slice(1));
} else if (payloadKind === 't') {
payload.push(true);
} else if (payloadKind === 'f') {
payload.push(false);
} else if (payloadKind === 'N') {
payload.push(null);
} else if (payloadKind === 'n') {
payload.push(+message.slice(1));
} else if (payloadKind === 'A') {
var data = message.slice(1);
payload.push(base64.toArrayBuffer(data));
} else if (payloadKind === 'S') {
payload.push(window.atob(message.slice(1)));
} else if (payloadKind === 'M') {
var multipartMessages = message.slice(1);
while (multipartMessages !== '') {
var spaceIdx = multipartMessages.indexOf(' ');
var msgLen = +multipartMessages.slice(0, spaceIdx);
var multipartMessage = multipartMessages.substr(spaceIdx + 1, msgLen);
multipartMessages = multipartMessages.slice(spaceIdx + msgLen + 1);
buildPayload(payload, multipartMessage);
}
} else {
payload.push(JSON.parse(message));
}
}
// Processes a single message, as encoded by NativeToJsMessageQueue.java.
function processMessage (message) {
var firstChar = message.charAt(0);
if (firstChar === 'J') {
// This is deprecated on the .java side. It doesn't work with CSP enabled.
// eslint-disable-next-line no-eval
eval(message.slice(1));
} else if (firstChar === 'S' || firstChar === 'F') {
var success = firstChar === 'S';
var keepCallback = message.charAt(1) === '1';
var spaceIdx = message.indexOf(' ', 2);
var status = +message.slice(2, spaceIdx);
var nextSpaceIdx = message.indexOf(' ', spaceIdx + 1);
var callbackId = message.slice(spaceIdx + 1, nextSpaceIdx);
var payloadMessage = message.slice(nextSpaceIdx + 1);
var payload = [];
buildPayload(payload, payloadMessage);
cordova.callbackFromNative(callbackId, success, status, payload, keepCallback);
} else {
console.log('processMessage failed: invalid message: ' + JSON.stringify(message));
}
}
function processMessages () {
// Check for the reentrant case.
if (isProcessing) {
return;
}
if (messagesFromNative.length === 0) {
return;
}
isProcessing = true;
try {
var msg = popMessageFromQueue();
// The Java side can send a * message to indicate that it
// still has messages waiting to be retrieved.
if (msg === '*' && messagesFromNative.length === 0) {
nextTick(pollOnce);
return;
}
processMessage(msg);
} finally {
isProcessing = false;
if (messagesFromNative.length > 0) {
nextTick(processMessages);
}
}
}
function popMessageFromQueue () {
var messageBatch = messagesFromNative.shift();
if (messageBatch === '*') {
return '*';
}
var spaceIdx = messageBatch.indexOf(' ');
var msgLen = +messageBatch.slice(0, spaceIdx);
var message = messageBatch.substr(spaceIdx + 1, msgLen);
messageBatch = messageBatch.slice(spaceIdx + msgLen + 1);
if (messageBatch) {
messagesFromNative.unshift(messageBatch);
}
return message;
}
module.exports = androidExec;

View File

@ -1,125 +0,0 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
// The last resume event that was received that had the result of a plugin call.
var lastResumeEvent = null;
module.exports = {
id: 'android',
bootstrap: function () {
var channel = require('cordova/channel');
var cordova = require('cordova');
var exec = require('cordova/exec');
var modulemapper = require('cordova/modulemapper');
// Get the shared secret needed to use the bridge.
exec.init();
// TODO: Extract this as a proper plugin.
modulemapper.clobbers('cordova/plugin/android/app', 'navigator.app');
// Core Splash Screen
modulemapper.clobbers('cordova/plugin/android/splashscreen', 'navigator.splashscreen');
var APP_PLUGIN_NAME = Number(cordova.platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App';
// Inject a listener for the backbutton on the document.
var backButtonChannel = cordova.addDocumentEventHandler('backbutton');
backButtonChannel.onHasSubscribersChange = function () {
// If we just attached the first handler or detached the last handler,
// let native know we need to override the back button.
exec(null, null, APP_PLUGIN_NAME, 'overrideBackbutton', [this.numHandlers === 1]);
};
// Add hardware MENU and SEARCH button handlers
cordova.addDocumentEventHandler('menubutton');
cordova.addDocumentEventHandler('searchbutton');
function bindButtonChannel (buttonName) {
// generic button bind used for volumeup/volumedown buttons
var volumeButtonChannel = cordova.addDocumentEventHandler(buttonName + 'button');
volumeButtonChannel.onHasSubscribersChange = function () {
exec(null, null, APP_PLUGIN_NAME, 'overrideButton', [buttonName, this.numHandlers === 1]);
};
}
// Inject a listener for the volume buttons on the document.
bindButtonChannel('volumeup');
bindButtonChannel('volumedown');
// The resume event is not "sticky", but it is possible that the event
// will contain the result of a plugin call. We need to ensure that the
// plugin result is delivered even after the event is fired (CB-10498)
var cordovaAddEventListener = document.addEventListener;
document.addEventListener = function (evt, handler, capture) {
cordovaAddEventListener(evt, handler, capture);
if (evt === 'resume' && lastResumeEvent) {
handler(lastResumeEvent);
}
};
// Let native code know we are all done on the JS side.
// Native code will then un-hide the WebView.
channel.onCordovaReady.subscribe(function () {
exec(onMessageFromNative, null, APP_PLUGIN_NAME, 'messageChannel', []);
exec(null, null, APP_PLUGIN_NAME, 'show', []);
});
}
};
function onMessageFromNative (msg) {
var cordova = require('cordova');
var action = msg.action;
switch (action) {
// pause and resume are Android app life cycle events
case 'backbutton':
case 'menubutton':
case 'searchbutton':
case 'pause':
case 'volumedownbutton':
case 'volumeupbutton':
cordova.fireDocumentEvent(action);
break;
case 'resume':
if (arguments.length > 1 && msg.pendingResult) {
if (arguments.length === 2) {
msg.pendingResult.result = arguments[1];
} else {
// The plugin returned a multipart message
var res = [];
for (var i = 1; i < arguments.length; i++) {
res.push(arguments[i]);
}
msg.pendingResult.result = res;
}
// Save the plugin result so that it can be delivered to the js
// even if they miss the initial firing of the event
lastResumeEvent = msg;
}
cordova.fireDocumentEvent(action, msg);
break;
default:
throw new Error('Unknown event action ' + action);
}
}

View File

@ -1,108 +0,0 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
var exec = require('cordova/exec');
var APP_PLUGIN_NAME = Number(require('cordova').platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App';
module.exports = {
/**
* Clear the resource cache.
*/
clearCache: function () {
exec(null, null, APP_PLUGIN_NAME, 'clearCache', []);
},
/**
* Load the url into the webview or into new browser instance.
*
* @param url The URL to load
* @param props Properties that can be passed in to the activity:
* wait: int => wait msec before loading URL
* loadingDialog: "Title,Message" => display a native loading dialog
* loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error
* clearHistory: boolean => clear webview history (default=false)
* openExternal: boolean => open in a new browser (default=false)
*
* Example:
* navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
*/
loadUrl: function (url, props) {
exec(null, null, APP_PLUGIN_NAME, 'loadUrl', [url, props]);
},
/**
* Cancel loadUrl that is waiting to be loaded.
*/
cancelLoadUrl: function () {
exec(null, null, APP_PLUGIN_NAME, 'cancelLoadUrl', []);
},
/**
* Clear web history in this web view.
* Instead of BACK button loading the previous web page, it will exit the app.
*/
clearHistory: function () {
exec(null, null, APP_PLUGIN_NAME, 'clearHistory', []);
},
/**
* Go to previous page displayed.
* This is the same as pressing the backbutton on Android device.
*/
backHistory: function () {
exec(null, null, APP_PLUGIN_NAME, 'backHistory', []);
},
/**
* Override the default behavior of the Android back button.
* If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
*
* Note: The user should not have to call this method. Instead, when the user
* registers for the "backbutton" event, this is automatically done.
*
* @param override T=override, F=cancel override
*/
overrideBackbutton: function (override) {
exec(null, null, APP_PLUGIN_NAME, 'overrideBackbutton', [override]);
},
/**
* Override the default behavior of the Android volume button.
* If overridden, when the volume button is pressed, the "volume[up|down]button"
* JavaScript event will be fired.
*
* Note: The user should not have to call this method. Instead, when the user
* registers for the "volume[up|down]button" event, this is automatically done.
*
* @param button volumeup, volumedown
* @param override T=override, F=cancel override
*/
overrideButton: function (button, override) {
exec(null, null, APP_PLUGIN_NAME, 'overrideButton', [button, override]);
},
/**
* Exit and terminate the application.
*/
exitApp: function () {
return exec(null, null, APP_PLUGIN_NAME, 'exitApp', []);
}
};

View File

@ -1,33 +0,0 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
var exec = require('cordova/exec');
var splashscreen = {
show: function () {
console.log('"navigator.splashscreen.show()" is unsupported on Android.');
},
hide: function () {
exec(null, null, 'CordovaSplashScreenPlugin', 'hide', []);
}
};
module.exports = splashscreen;

179
droidgap Executable file
View File

@ -0,0 +1,179 @@
#!/usr/bin/env ruby
require 'fileutils'
# ./droidgap /Users/brianleroux/Code/android-sdk-mac MyApp com.westcoastlogic example /Users/brianleroux/Desktop/MyApp
class Build
attr_reader :android_sdk_path, :name, :pkg, :www, :path
def initialize(*a)
@android_sdk_path, @name, @pkg, @www, @path = a
@android_dir = File.expand_path(File.dirname(__FILE__))
@framework_dir = File.join(@android_dir, "framework")
end
# runs the build script
def run
build_jar
create_android
include_www
generate_manifest
copy_libs
add_name_to_strings
write_java
puts "Complete!"
end
# removes local.properties and recreates based on android_sdk_path
# then generates framework/phonegap.jar
def build_jar
puts "Building the JAR..."
%w(local.properties phonegap.js phonegap.jar).each do |f|
FileUtils.rm File.join(@framework_dir, f) if File.exists? File.join(@framework_dir, f)
end
open(File.join(@framework_dir, "local.properties"), 'w') do |f|
f.puts "sdk.dir=#{ @android_sdk_path }"
end
Dir.chdir(@framework_dir)
`ant jar`
Dir.chdir(@android_dir)
end
# runs android create project
# TODO need to allow more flexible SDK targetting
# TODO validate Android SDK
# TODO fix 'android' shell call so that it works on Windows. Can't prepend android command with path to it.
def create_android
android_exec = File.join(@android_sdk_path, "tools", "android");
target_id = 5
puts "Creating Android project for target level #{ target_id }"
`android create project -t #{ target_id } -k #{ @pkg } -a #{ @name } -n #{ @name } -p #{ @path }`
end
def include_www
puts "Adding www folder to project..."
FileUtils.mkdir_p File.join(@path, "assets", "www")
FileUtils.cp_r File.join(@www, "."), File.join(@path, "assets", "www")
end
# creates an AndroidManifest.xml for the project
def generate_manifest
puts "Generating manifest..."
manifest = ""
open(File.join(@framework_dir, "AndroidManifest.xml"), 'r') do |old|
manifest = old.read
manifest.gsub! 'android:versionCode="5"', 'android:versionCode="1"'
manifest.gsub! 'package="com.phonegap"', "package=\"#{ @pkg }\""
manifest.gsub! 'android:name=".StandAlone"', "android:name=\".#{ @name }\""
manifest.gsub! 'android:minSdkVersion="5"', 'android:minSdkVersion="3"'
end
open(File.join(@path, "AndroidManifest.xml"), 'w') { |x| x.puts manifest }
end
# copies stuff from framework into the project
# TODO need to allow for www import inc icon
def copy_libs
puts "Copying over libraries and assets and creating phonegap.js..."
framework_res_dir = File.join(@framework_dir, "res")
app_res_dir = File.join(@path, "res")
FileUtils.mkdir_p File.join(@path, "libs")
FileUtils.cp File.join(@framework_dir, "phonegap.jar"), File.join(@path, "libs")
FileUtils.mkdir_p File.join(app_res_dir, "values")
FileUtils.cp File.join(framework_res_dir, "values","strings.xml"), File.join(app_res_dir, "values", "strings.xml")
FileUtils.mkdir_p File.join(app_res_dir, "layout")
%w(main.xml preview.xml).each do |f|
FileUtils.cp File.join(framework_res_dir, "layout", f), File.join(app_res_dir, "layout", f)
end
%w(drawable-hdpi drawable-ldpi drawable-mdpi).each do |e|
FileUtils.mkdir_p File.join(app_res_dir, e)
FileUtils.cp File.join(framework_res_dir, "drawable", "icon.png"), File.join(app_res_dir, e, "icon.png")
end
# concat JS and put into www folder.
js_dir = File.join(@framework_dir, "assets", "js")
phonegapjs = IO.read(File.join(js_dir, 'phonegap.js.base'))
Dir.new(js_dir).entries.each do |script|
next if script[0].chr == "." or script == "phonegap.js.base"
phonegapjs << IO.read(File.join(js_dir, script))
phonegapjs << "\n\n"
end
File.open(File.join(@path, "assets", "www", "phonegap.js"), 'w') {|f| f.write(phonegapjs) }
end
# puts app name in strings
def add_name_to_strings
puts "Adding some application name to strings.xml..."
x = "<?xml version=\"1.0\" encoding=\"utf-8\"?>
<resources>
<string name=\"app_name\">#{ @name }</string>
<string name=\"go\">Snap</string>
</resources>
"
open(File.join(@path, "res", "values", "strings.xml"), 'w') do |f|
f.puts x.gsub(' ','')
end
end
# this is so fucking unholy yet oddly beautiful
# not sure if I should thank Ruby or apologize for this abusive use of string interpolation
def write_java
puts "Writing application Java code..."
j = "
package #{ @pkg };
import android.app.Activity;
import android.os.Bundle;
import com.phonegap.*;
public class #{ @name } extends DroidGap
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
super.loadUrl(\"file:///android_asset/www/index.html\");
}
}
"
code_dir = File.join(@path, "src", @pkg.gsub('.', File::SEPARATOR))
FileUtils.mkdir_p(code_dir)
open(File.join(code_dir, "#{@name}.java"),'w') { |f| f.puts j.gsub(' ','') }
end
#
end
if ARGV.length == 5
Build.new(*ARGV).run
else
puts <<-EOF
DroidGap: PhoneGap/Android Project Generator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Creates a fresh app for hybrid mobile web hacking. Delicious robot!
Usage:
./droidgap <android_sdk_path> <name> <package_name> <www> <path>
Params:
android_sdk_path ... The path to your Android SDK install.
name ............... The name of your application.
package_name ....... The name of your package (For example: com.nitobi.demo)
www ................ The path to your www folder. (Wherein your HTML, CSS and JS app is.)
path ............... The path to generate the Android application.
EOF
end

147
example/index.html Normal file
View File

@ -0,0 +1,147 @@
<!DOCTYPE HTML>
<html>
<head>
<meta name="viewport" content="width=320; user-scalable=no" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>PhoneGap</title>
<link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title" charset="utf-8">
<script type="text/javascript" charset="utf-8" src="phonegap.js"></script>
<script type="text/javascript" charset="utf-8">
var deviceInfo = function(){
document.getElementById("platform").innerHTML = device.platform;
document.getElementById("version").innerHTML = device.version;
document.getElementById("uuid").innerHTML = device.uuid;
console.log("Height:" + window.innerHeight);
console.log("Width:" + window.innerWidth);
}
var getLocation = function() {
var suc = function(p){
alert(p.coords.latitude + " " + p.coords.longitude);
};
var fail = function(){};
navigator.geolocation.getCurrentPosition(suc,fail);
}
var beep = function(){
navigator.notification.beep(2);
}
var vibrate = function(){
navigator.notification.vibrate(0);
}
var getContact = function(){
var suc = function(c){ alert("Contact 4: " + c.contacts[3].name); };
var fail = function(){};
navigator.ContactManager.get(suc, fail);
}
var watchAccel = function() {
var suc = function(a){
document.getElementById('x').innerHTML = roundNumber(a.x);
document.getElementById('y').innerHTML = roundNumber(a.y);
document.getElementById('z').innerHTML = roundNumber(a.z);
};
var fail = function(){};
var opt = {};
opt.frequency = 100;
timer = navigator.accelerometer.watchAcceleration(suc,fail,opt);
}
function roundNumber(num) {
var dec = 3;
var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
return result;
}
var preventBehavior = function(e) {
e.preventDefault();
};
function show_pic()
{
var viewport = document.getElementById('viewport');
viewport.style.display = "";
navigator.camera.getPicture(dump_pic, fail, { quality: 50 });
}
function dump_pic(data)
{
var viewport = document.getElementById('viewport');
console.log(data);
viewport.style.display = "";
viewport.style.position = "absolute";
viewport.style.top = "10px";
viewport.style.left = "10px";
document.getElementById("test_img").src = "data:image/jpeg;base64," + data;
}
function close()
{
var viewport = document.getElementById('viewport');
viewport.style.position = "relative";
viewport.style.display = "none";
}
function fail(fail)
{
alert(fail);
}
// This is just to do this.
function readFile()
{
navigator.file.read('/sdcard/phonegap.txt', fail , fail);
}
function writeFile()
{
navigator.file.write('foo.txt', "This is a test of writing to a file", fail, fail);
}
function get_contacts()
{
navigator.ContactManager.getAllContacts(count_contacts, fail, null);
}
function count_contacts(contacts)
{
alert(contacts.length);
}
function init(){
document.addEventListener("touchmove", preventBehavior, false);
document.addEventListener("deviceReady", deviceInfo, true);
}
</script>
</head>
<body onload="init();" id="stage" class="theme">
<h1>Welcome to PhoneGap!</h1>
<h2>this file is located at assets/index.html</h2>
<div id="info">
<h4>Platform: <span id="platform">&nbsp;</span></h4>
<h4>Version: <span id="version">&nbsp;</span></h4>
<h4>UUID: <span id="uuid">&nbsp;</span></h4>
</div>
<dl id="accel-data">
<dt>X:</dt><dd id="x">&nbsp;</dd>
<dt>Y:</dt><dd id="y">&nbsp;</dd>
<dt>Z:</dt><dd id="z">&nbsp;</dd>
</dl>
<a href="#" class="btn large" onclick="watchAccel();">Watch Accelerometer</a>
<a href="#" class="btn large" onclick="getLocation();">Get Location</a>
<a href="tel://411" class="btn large">Call 411</a>
<a href="#" class="btn large" onclick="beep();">Beep</a>
<a href="#" class="btn large" onclick="vibrate();">Vibrate</a>
<a href="#" class="btn large" onclick="show_pic();">Get a Picture</a>
<a href="#" class="btn large" onclick="get_contacts();">Get phone's contacts</a>
<div id="viewport" class="viewport" style="display: none;">
<img style="width:60px;height:60px" id="test_img" src="" />
</div>
</body>
</html>

96
example/master.css Normal file
View File

@ -0,0 +1,96 @@
body {
background:#222 none repeat scroll 0 0;
color:#666;
font-family:Helvetica;
font-size:72%;
line-height:1.5em;
margin:0;
border-top:1px solid #393939;
}
#info{
background:#ffa;
border: 1px solid #ffd324;
-webkit-border-radius: 5px;
border-radius: 5px;
clear:both;
margin:15px 6px 0;
width:295px;
padding:4px 0px 2px 10px;
}
#info > h4{
font-size:.95em;
margin:5px 0;
}
#stage.theme{
padding-top:3px;
}
/* Definition List */
#stage.theme > dl{
padding-top:10px;
clear:both;
margin:0;
list-style-type:none;
padding-left:10px;
overflow:auto;
}
#stage.theme > dl > dt{
font-weight:bold;
float:left;
margin-left:5px;
}
#stage.theme > dl > dd{
width:45px;
float:left;
color:#a87;
font-weight:bold;
}
/* Content Styling */
#stage.theme > h1, #stage.theme > h2, #stage.theme > p{
margin:1em 0 .5em 13px;
}
#stage.theme > h1{
color:#eee;
font-size:1.6em;
text-align:center;
margin:0;
margin-top:15px;
padding:0;
}
#stage.theme > h2{
clear:both;
margin:0;
padding:3px;
font-size:1em;
text-align:center;
}
/* Stage Buttons */
#stage.theme a.btn{
border: 1px solid #555;
-webkit-border-radius: 5px;
border-radius: 5px;
text-align:center;
display:block;
float:left;
background:#444;
width:150px;
color:#9ab;
font-size:1.1em;
text-decoration:none;
padding:1.2em 0;
margin:3px 0px 3px 5px;
}
#stage.theme a.btn.large{
width:308px;
padding:1.2em 0;
}

1188
example/phonegap.js Normal file

File diff suppressed because it is too large Load Diff

8
framework/.classpath Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="lib" path="libs/commons-codec-1.3.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

33
framework/.project Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>PhoneGap</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

62
framework/AndroidManifest.xml Executable file → Normal file
View File

@ -1,23 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionName="1.0"
android:versionCode="1">
</manifest>
package="com.phonegap" android:versionName="1.1" android:versionCode="5">
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:resizeable="true"
android:anyDensity="true"
/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application android:icon="@drawable/icon" android:label="@string/app_name"
android:debuggable="true">
<activity android:name=".StandAlone"
android:label="@string/app_name" android:configChanges="orientation|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.phonegap.CameraPreview"
android:label="@string/app_name" android:screenOrientation="landscape"
android:configChanges="orientation|keyboardHidden">
<action android:name="android.intent.action.PICK" />
</activity>
</application>
<uses-sdk android:minSdkVersion="2" />
</manifest>

View File

@ -0,0 +1,102 @@
function Acceleration(x, y, z)
{
this.x = x;
this.y = y;
this.z = z;
this.timestamp = new Date().getTime();
this.win = null;
this.fail = null;
}
var accelListeners = [];
/**
* This class provides access to device accelerometer data.
* @constructor
*/
function Accelerometer() {
/**
* The last known acceleration.
*/
this.lastAcceleration = null;
}
/**
* Asynchronously aquires the current acceleration.
* @param {Function} successCallback The function to call when the acceleration
* data is available
* @param {Function} errorCallback The function to call when there is an error
* getting the acceleration data.
* @param {AccelerationOptions} options The options for getting the accelerometer data
* such as timeout.
*/
Accelerometer.prototype.getCurrentAcceleration = function(successCallback, errorCallback, options) {
// If the acceleration is available then call success
// If the acceleration is not available then call error
// Created for iPhone, Iphone passes back _accel obj litteral
if (typeof successCallback == "function") {
if(this.lastAcceleration)
successCallback(accel);
else
{
watchAcceleration(this.gotCurrentAcceleration, this.fail);
}
}
}
Accelerometer.prototype.gotAccel = function(key, x, y, z)
{
console.log('we won');
var a = new Acceleration(x,y,z);
a.x = x;
a.y = y;
a.x = z;
a.win = accelListeners[key].win;
a.fail = accelListeners[key].fail;
this.timestamp = new Date().getTime();
this.lastAcceleration = a;
accelListeners[key] = a;
if (typeof a.win == "function") {
a.win(a);
}
}
/**
* Asynchronously aquires the acceleration repeatedly at a given interval.
* @param {Function} successCallback The function to call each time the acceleration
* data is available
* @param {Function} errorCallback The function to call when there is an error
* getting the acceleration data.
* @param {AccelerationOptions} options The options for getting the accelerometer data
* such as timeout.
*/
Accelerometer.prototype.watchAcceleration = function(successCallback, errorCallback, options) {
// TODO: add the interval id to a list so we can clear all watches
var frequency = (options != undefined)? options.frequency : 10000;
var accel = Acceleration(0,0,0);
accel.win = successCallback;
accel.fail = errorCallback;
accel.opts = options;
var key = accelListeners.push( accel ) - 1;
Accel.start(frequency, key);
}
/**
* Clears the specified accelerometer watch.
* @param {String} watchId The ID of the watch returned from #watchAcceleration.
*/
Accelerometer.prototype.clearWatch = function(watchId) {
Accel.stop(watchId);
}
Accelerometer.prototype.epicFail = function(watchId, message) {
accelWatcher[key].fail();
}
PhoneGap.addConstructor(function() {
if (typeof navigator.accelerometer == "undefined") navigator.accelerometer = new Accelerometer();
});

View File

@ -0,0 +1,41 @@
/**
* This class provides access to the device camera.
* @constructor
*/
function Camera() {
}
/**
*
* @param {Function} successCallback
* @param {Function} errorCallback
* @param {Object} options
*/
Camera.prototype.getPicture = function(successCallback, errorCallback, options) {
this.winCallback = successCallback;
this.failCallback = errorCallback;
if (options.quality)
{
GapCam.takePicture(options.quality);
}
else
{
GapCam.takePicture(80);
}
}
Camera.prototype.win = function(picture)
{
this.winCallback(picture);
}
Camera.prototype.fail = function(err)
{
this.failCallback(err);
}
PhoneGap.addConstructor(function() {
if (typeof navigator.camera == "undefined") navigator.camera = new Camera();
});

View File

@ -0,0 +1,96 @@
/**
* This class provides access to device Compass data.
* @constructor
*/
function Compass() {
/**
* The last known Compass position.
*/
this.lastHeading = null;
this.lastError = null;
this.callbacks = {
onHeadingChanged: [],
onError: []
};
};
/**
* Asynchronously aquires the current heading.
* @param {Function} successCallback The function to call when the heading
* data is available
* @param {Function} errorCallback The function to call when there is an error
* getting the heading data.
* @param {PositionOptions} options The options for getting the heading data
* such as timeout.
*/
Compass.prototype.getCurrentHeading = function(successCallback, errorCallback, options) {
if (this.lastHeading == null) {
this.start(options);
}
else
if (typeof successCallback == "function") {
successCallback(this.lastHeading);
}
};
/**
* Asynchronously aquires the heading repeatedly at a given interval.
* @param {Function} successCallback The function to call each time the heading
* data is available
* @param {Function} errorCallback The function to call when there is an error
* getting the heading data.
* @param {HeadingOptions} options The options for getting the heading data
* such as timeout and the frequency of the watch.
*/
Compass.prototype.watchHeading= function(successCallback, errorCallback, options) {
// Invoke the appropriate callback with a new Position object every time the implementation
// determines that the position of the hosting device has changed.
this.getCurrentHeading(successCallback, errorCallback, options);
var frequency = 100;
if (typeof(options) == 'object' && options.frequency)
frequency = options.frequency;
var self = this;
return setInterval(function() {
self.getCurrentHeading(successCallback, errorCallback, options);
}, frequency);
};
/**
* Clears the specified heading watch.
* @param {String} watchId The ID of the watch returned from #watchHeading.
*/
Compass.prototype.clearWatch = function(watchId) {
clearInterval(watchId);
};
/**
* Called by the geolocation framework when the current heading is found.
* @param {HeadingOptions} position The current heading.
*/
Compass.prototype.setHeading = function(heading) {
this.lastHeading = heading;
for (var i = 0; i < this.callbacks.onHeadingChanged.length; i++) {
var f = this.callbacks.onHeadingChanged.shift();
f(heading);
}
};
/**
* Called by the geolocation framework when an error occurs while looking up the current position.
* @param {String} message The text of the error message.
*/
Compass.prototype.setError = function(message) {
this.lastError = message;
for (var i = 0; i < this.callbacks.onError.length; i++) {
var f = this.callbacks.onError.shift();
f(message);
}
};
PhoneGap.addConstructor(function() {
if (typeof navigator.compass == "undefined") navigator.compass = new Compass();
});

View File

@ -0,0 +1,81 @@
var Contact = function(){
this.name = new ContactName();
this.emails = [];
this.phones = [];
}
var ContactName = function()
{
this.formatted = "";
this.familyName = "";
this.givenName = "";
this.additionalNames = [];
this.prefixes = [];
this.suffixes = [];
}
var ContactEmail = function()
{
this.types = [];
this.address = "";
}
var ContactPhoneNumber = function()
{
this.types = [];
this.number = "";
}
var Contacts = function()
{
this.records = [];
}
Contacts.prototype.find = function(obj, win, fail)
{
if(obj.name != null)
{
// Build up the search term that we'll use in SQL, based on the structure/contents of the contact object passed into find.
var searchTerm = '';
if (obj.name.givenName && obj.name.givenName.length > 0) {
searchTerm = obj.name.givenName.split(' ').join('%');
}
if (obj.name.familyName && obj.name.familyName.length > 0) {
searchTerm += obj.name.familyName.split(' ').join('%');
}
if (!obj.name.familyName && !obj.name.givenName && obj.name.formatted) {
searchTerm = obj.name.formatted;
}
ContactHook.search(searchTerm, "", "");
}
this.win = win;
this.fail = fail;
}
Contacts.prototype.droidFoundContact = function(name, npa, email)
{
var contact = new Contact();
contact.name = new ContactName();
contact.name.formatted = name;
contact.name.givenName = name;
var mail = new ContactEmail();
mail.types.push("home");
mail.address = email;
contact.emails.push(mail);
phone = new ContactPhoneNumber();
phone.types.push("home");
phone.number = npa;
contact.phones.push(phone);
this.records.push(contact);
}
Contacts.prototype.droidDone = function()
{
this.win(this.records);
}
PhoneGap.addConstructor(function() {
if(typeof navigator.contacts == "undefined") navigator.contacts = new Contacts();
});

View File

@ -0,0 +1,33 @@
var Crypto = function()
{
}
Crypto.prototype.encrypt = function(seed, string, callback)
{
GapCrypto.encrypt(seed, string);
this.encryptWin = callback;
}
Crypto.prototype.decrypt = function(seed, string, callback)
{
GapCrypto.decrypt(seed, string);
this.decryptWin = callback;
}
Crypto.prototype.gotCryptedString = function(string)
{
this.encryptWin(string);
}
Crypto.prototype.getPlainString = function(string)
{
this.decryptWin(string);
}
PhoneGap.addConstructor(function() {
if (typeof navigator.Crypto == "undefined")
{
navigator.Crypto = new Crypto();
}
});

View File

@ -0,0 +1,33 @@
/**
* this represents the mobile device, and provides properties for inspecting the model, version, UUID of the
* phone, etc.
* @constructor
*/
function Device() {
this.available = PhoneGap.available;
this.platform = null;
this.version = null;
this.name = null;
this.gap = null;
this.uuid = null;
try {
if (window.DroidGap) {
this.available = true;
this.uuid = window.DroidGap.getUuid();
this.version = window.DroidGap.getOSVersion();
this.gapVersion = window.DroidGap.getVersion();
this.platform = window.DroidGap.getPlatform();
this.name = window.DroidGap.getProductName();
this.line1Number = window.DroidGap.getLine1Number();
this.deviceId = window.DroidGap.getDeviceId();
this.simSerialNumber = window.DroidGap.getSimSerialNumber();
this.subscriberId = window.DroidGap.getSubscriberId();
}
} catch(e) {
this.available = false;
}
}
PhoneGap.addConstructor(function() {
navigator.device = window.device = new Device();
});

226
framework/assets/js/file.js Normal file
View File

@ -0,0 +1,226 @@
PhoneGap.addConstructor(function() { if (typeof navigator.fileMgr == "undefined") navigator.fileMgr = new FileMgr();});
/**
* This class provides iPhone read and write access to the mobile device file system.
* Based loosely on http://www.w3.org/TR/2009/WD-FileAPI-20091117/#dfn-empty
*/
function FileMgr()
{
this.fileWriters = {}; // empty maps
this.fileReaders = {};
this.docsFolderPath = "../../Documents";
this.tempFolderPath = "../../tmp";
this.freeDiskSpace = -1;
this.getFileBasePaths();
}
// private, called from Native Code
FileMgr.prototype._setPaths = function(docs,temp)
{
this.docsFolderPath = docs;
this.tempFolderPath = temp;
}
// private, called from Native Code
FileMgr.prototype._setFreeDiskSpace = function(val)
{
this.freeDiskSpace = val;
}
// FileWriters add/remove
// called internally by writers
FileMgr.prototype.addFileWriter = function(filePath,fileWriter)
{
this.fileWriters[filePath] = fileWriter;
}
FileMgr.prototype.removeFileWriter = function(filePath)
{
this.fileWriters[filePath] = null;
}
// File readers add/remove
// called internally by readers
FileMgr.prototype.addFileReader = function(filePath,fileReader)
{
this.fileReaders[filePath] = fileReader;
}
FileMgr.prototype.removeFileReader = function(filePath)
{
this.fileReaders[filePath] = null;
}
/*******************************************
*
* private reader callback delegation
* called from native code
*/
FileMgr.prototype.reader_onloadstart = function(filePath,result)
{
this.fileReaders[filePath].onloadstart(result);
}
FileMgr.prototype.reader_onprogress = function(filePath,result)
{
this.fileReaders[filePath].onprogress(result);
}
FileMgr.prototype.reader_onload = function(filePath,result)
{
this.fileReaders[filePath].result = unescape(result);
this.fileReaders[filePath].onload(this.fileReaders[filePath].result);
}
FileMgr.prototype.reader_onerror = function(filePath,err)
{
this.fileReaders[filePath].result = err;
this.fileReaders[filePath].onerror(err);
}
FileMgr.prototype.reader_onloadend = function(filePath,result)
{
this.fileReaders[filePath].onloadend(result);
}
/*******************************************
*
* private writer callback delegation
* called from native code
*/
FileMgr.prototype.writer_onerror = function(filePath,err)
{
this.fileWriters[filePath].onerror(err);
}
FileMgr.prototype.writer_oncomplete = function(filePath,result)
{
this.fileWriters[filePath].oncomplete(result); // result contains bytes written
}
FileMgr.prototype.getFileBasePaths = function()
{
//PhoneGap.exec("File.getFileBasePaths");
}
FileMgr.prototype.testFileExists = function(fileName, successCallback, errorCallback)
{
var test = FileUtil.testFileExists(fileName);
test ? successCallback() : errorCallback();
}
FileMgr.prototype.testDirectoryExists = function(dirName, successCallback, errorCallback)
{
this.successCallback = successCallback;
this.errorCallback = errorCallback;
var test = FileUtil.testDirectoryExists(dirName);
test ? successCallback() : errorCallback();
}
FileMgr.prototype.createDirectory = function(dirName, successCallback, errorCallback)
{
this.successCallback = successCallback;
this.errorCallback = errorCallback;
var test = FileUtils.createDirectory(dirName);
test ? successCallback() : errorCallback();
}
FileMgr.prototype.deleteDirectory = function(dirName, successCallback, errorCallback)
{
this.successCallback = successCallback;
this.errorCallback = errorCallback;
var test = FileUtils.deleteDirectory(dirName);
test ? successCallback() : errorCallback();
}
FileMgr.prototype.deleteFile = function(fileName, successCallback, errorCallback)
{
this.successCallback = successCallback;
this.errorCallback = errorCallback;
FileUtils.deleteFile(fileName);
test ? successCallback() : errorCallback();
}
FileMgr.prototype.getFreeDiskSpace = function(successCallback, errorCallback)
{
if(this.freeDiskSpace > 0)
{
return this.freeDiskSpace;
}
else
{
this.successCallback = successCallback;
this.errorCallback = errorCallback;
this.freeDiskSpace = FileUtils.getFreeDiskSpace();
(this.freeDiskSpace > 0) ? successCallback() : errorCallback();
}
}
// File Reader
function FileReader()
{
this.fileName = "";
this.result = null;
this.onloadstart = null;
this.onprogress = null;
this.onload = null;
this.onerror = null;
this.onloadend = null;
}
FileReader.prototype.abort = function()
{
// Not Implemented
}
FileReader.prototype.readAsText = function(file)
{
if(this.fileName && this.fileName.length > 0)
{
navigator.fileMgr.removeFileReader(this.fileName,this);
}
this.fileName = file;
navigator.fileMgr.addFileReader(this.fileName,this);
return FileUtil.read(this.fileName);
}
// File Writer
function FileWriter()
{
this.fileName = "";
this.result = null;
this.readyState = 0; // EMPTY
this.result = null;
this.onerror = null;
this.oncomplete = null;
}
FileWriter.prototype.writeAsText = function(file,text,bAppend)
{
if(this.fileName && this.fileName.length > 0)
{
navigator.fileMgr.removeFileWriter(this.fileName,this);
}
this.fileName = file;
if(bAppend != true)
{
bAppend = false; // for null values
}
navigator.fileMgr.addFileWriter(file,this);
this.readyState = 0; // EMPTY
var call = FileUtil.write(file, text, bAppend);
this.result = null;
}

View File

@ -0,0 +1,90 @@
/**
* This class provides access to device GPS data.
* @constructor
*/
function Geolocation() {
/**
* The last known GPS position.
*/
this.lastPosition = null;
this.lastError = null;
this.listeners = null;
};
var geoListeners = [];
Geolocation.prototype.getCurrentPosition = function(successCallback, errorCallback, options)
{
var position = Geo.getCurrentLocation();
this.global_success = successCallback;
this.fail = errorCallback;
}
// Run the global callback
Geolocation.prototype.gotCurrentPosition = function(lat, lng, alt, altacc, head, vel, stamp)
{
if (lat == "undefined" || lng == "undefined")
{
this.fail();
}
else
{
coords = new Coordinates(lat, lng, alt, altacc, head, vel);
loc = new Position(coords, stamp);
this.lastPosition = loc;
this.global_success(loc);
}
}
/*
* This turns on the GeoLocator class, which has two listeners.
* The listeners have their own timeouts, and run independently of this process
* In this case, we return the key to the watch hash
*/
Geolocation.prototype.watchPosition = function(successCallback, errorCallback, options)
{
var frequency = (options != undefined)? options.frequency : 10000;
var key = geoListeners.push( {"success" : successCallback, "fail" : errorCallback }) - 1;
// TO-DO: Get the names of the method and pass them as strings to the Java.
return Geo.start(frequency, key);
}
/*
* Retrieve and stop this listener from listening to the GPS
*
*/
Geolocation.prototype.success = function(key, lat, lng, alt, altacc, head, vel, stamp)
{
var coords = new Coordinates(lat, lng, alt, altacc, head, vel);
var loc = new Position(coords, stamp);
geoListeners[key].success(loc);
}
Geolocation.prototype.fail = function(key)
{
geoListeners[key].fail();
}
Geolocation.prototype.clearWatch = function(watchId)
{
Geo.stop(watchId);
}
PhoneGap.addConstructor(function() {
// Taken from Jesse's geo fix (similar problem) in PhoneGap iPhone. Go figure, same browser!
function __proxyObj(origObj, proxyObj, funkList) {
for (var v in funkList) {
origObj[funkList[v]] = proxyObj[funkList[v]];
}
}
// In case a native geolocation object exists, proxy the native one over to a diff object so that we can overwrite the native implementation.
if (typeof navigator.geolocation != 'undefined') {
navigator._geo = new Geolocation();
__proxyObj(navigator.geolocation, navigator._geo, ["setLocation", "getCurrentPosition", "watchPosition", "clearWatch", "setError", "start", "stop", "gotCurrentPosition"]);
} else {
navigator.geolocation = new Geolocation();
}
});

View File

@ -0,0 +1,22 @@
function KeyEvent()
{
}
KeyEvent.prototype.menuTrigger = function()
{
var e = document.createEvent('Events');
e.initEvent('menuKeyDown');
document.dispatchEvent(e);
}
KeyEvent.prototype.searchTrigger= function()
{
var e = document.createEvent('Events');
e.initEvent('searchKeyDown');
document.dispatchEvent(e);
}
if (document.keyEvent == null || typeof document.keyEvent == 'undefined')
{
window.keyEvent = document.keyEvent = new KeyEvent();
}

View File

@ -0,0 +1,62 @@
/**
* This class provides access to the device media, interfaces to both sound and video
* @constructor
*/
function Media(src, successCallback, errorCallback) {
this.src = src;
this.successCallback = successCallback;
this.errorCallback = errorCallback;
}
Media.prototype.record = function() {
}
Media.prototype.play = function() {
}
Media.prototype.pause = function() {
}
Media.prototype.stop = function() {
}
/**
* This class contains information about any Media errors.
* @constructor
*/
function MediaError() {
this.code = null,
this.message = "";
}
MediaError.MEDIA_ERR_ABORTED = 1;
MediaError.MEDIA_ERR_NETWORK = 2;
MediaError.MEDIA_ERR_DECODE = 3;
MediaError.MEDIA_ERR_NONE_SUPPORTED = 4;
//if (typeof navigator.audio == "undefined") navigator.audio = new Media(src);
/**
* This class provides access to the device media, interfaces to both sound and video
* @constructor
*/
Media.prototype.play = function() {
DroidGap.startPlayingAudio(this.src);
}
Media.prototype.stop = function() {
DroidGap.stopPlayingAudio();
}
Media.prototype.startRecord = function() {
DroidGap.startRecordingAudio(this.src);
}
Media.prototype.stopRecordingAudio = function() {
DroidGap.stopRecordingAudio();
}

View File

@ -0,0 +1,54 @@
/**
* This class contains information about any NetworkStatus.
* @constructor
*/
function NetworkStatus() {
this.code = null;
this.message = "";
}
NetworkStatus.NOT_REACHABLE = 0;
NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
NetworkStatus.REACHABLE_VIA_WIFI_NETWORK = 2;
/**
* This class provides access to device Network data (reachability).
* @constructor
*/
function Network() {
/**
* The last known Network status.
* { hostName: string, ipAddress: string,
remoteHostStatus: int(0/1/2), internetConnectionStatus: int(0/1/2), localWiFiConnectionStatus: int (0/2) }
*/
this.lastReachability = null;
};
/**
* Called by the geolocation framework when the reachability status has changed.
* @param {Reachibility} reachability The current reachability status.
*/
Network.prototype.updateReachability = function(reachability) {
this.lastReachability = reachability;
};
/**
*
* @param {Object} uri
* @param {Function} win
* @param {Object} options (isIpAddress:boolean)
*/
Network.prototype.isReachable = function(uri, win, options)
{
var status = new NetworkStatus();
if(NetworkManager.isReachable(uri))
{
if (NetworkManager.isWifiActive()) {
status.code = NetworkStatus.REACHABLE_VIA_WIFI_NETWORK;
} else {
status.code = NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK;
}
} else {
status.code = NetworkStatus.NOT_REACHABLE;
}
win(status);
};
PhoneGap.addConstructor(function() {
if (typeof navigator.network == "undefined") navigator.network = new Network();
});

View File

@ -0,0 +1,77 @@
/**
* This class provides access to notifications on the device.
*/
function Notification() {
}
/**
* Open a native alert dialog, with a customizable title and button text.
* @param {String} message Message to print in the body of the alert
* @param {String} [title="Alert"] Title of the alert dialog (default: Alert)
* @param {String} [buttonLabel="OK"] Label of the close button (default: OK)
*/
Notification.prototype.alert = function(message, title, buttonLabel) {
// Default is to use a browser alert; this will use "index.html" as the title though
alert(message);
};
/**
* Start spinning the activity indicator on the statusbar
*/
Notification.prototype.activityStart = function() {
};
/**
* Stop spinning the activity indicator on the statusbar, if it's currently spinning
*/
Notification.prototype.activityStop = function() {
};
/**
* Causes the device to blink a status LED.
* @param {Integer} count The number of blinks.
* @param {String} colour The colour of the light.
*/
Notification.prototype.blink = function(count, colour) {
};
/**
* Causes the device to vibrate.
* @param {Integer} mills The number of milliseconds to vibrate for.
*/
Notification.prototype.vibrate = function(mills) {
};
/**
* Causes the device to beep.
* @param {Integer} count The number of beeps.
* @param {Integer} volume The volume of the beep.
*/
Notification.prototype.beep = function(count, volume) {
};
// TODO: of course on Blackberry and Android there notifications in the UI as well
PhoneGap.addConstructor(function() {
if (typeof navigator.notification == "undefined") navigator.notification = new Notification();
});
Notification.prototype.vibrate = function(mills)
{
DroidGap.vibrate(mills);
}
/*
* On the Android, we don't beep, we notify you with your
* notification! We shouldn't keep hammering on this, and should
* review what we want beep to do.
*/
Notification.prototype.beep = function(count, volume)
{
DroidGap.beep(count);
}

View File

@ -0,0 +1,135 @@
if (typeof(DeviceInfo) != 'object')
DeviceInfo = {};
/**
* This represents the PhoneGap API itself, and provides a global namespace for accessing
* information about the state of PhoneGap.
* @class
*/
PhoneGap = {
queue: {
ready: true,
commands: [],
timer: null
},
_constructors: []
};
/**
* Boolean flag indicating if the PhoneGap API is available and initialized.
*/ // TODO: Remove this, it is unused here ... -jm
PhoneGap.available = DeviceInfo.uuid != undefined;
/**
* Add an initialization function to a queue that ensures it will run and initialize
* application constructors only once PhoneGap has been initialized.
* @param {Function} func The function callback you want run once PhoneGap is initialized
*/
PhoneGap.addConstructor = function(func) {
var state = document.readyState;
if ( state == 'loaded' || state == 'complete' )
{
func();
}
else
{
PhoneGap._constructors.push(func);
}
};
(function()
{
var timer = setInterval(function()
{
var state = document.readyState;
if ( state == 'loaded' || state == 'complete' )
{
clearInterval(timer); // stop looking
// run our constructors list
while (PhoneGap._constructors.length > 0)
{
var constructor = PhoneGap._constructors.shift();
try
{
constructor();
}
catch(e)
{
if (typeof(debug['log']) == 'function')
{
debug.log("Failed to run constructor: " + debug.processMessage(e));
}
else
{
alert("Failed to run constructor: " + e.message);
}
}
}
// all constructors run, now fire the deviceready event
var e = document.createEvent('Events');
e.initEvent('deviceready');
document.dispatchEvent(e);
}
}, 5);
})();
/**
* Execute a PhoneGap command in a queued fashion, to ensure commands do not
* execute with any race conditions, and only run when PhoneGap is ready to
* recieve them.
* @param {String} command Command to be run in PhoneGap, e.g. "ClassName.method"
* @param {String[]} [args] Zero or more arguments to pass to the method
*/
PhoneGap.exec = function() {
PhoneGap.queue.commands.push(arguments);
if (PhoneGap.queue.timer == null)
PhoneGap.queue.timer = setInterval(PhoneGap.run_command, 10);
};
/**
* Internal function used to dispatch the request to PhoneGap. It processes the
* command queue and executes the next command on the list. If one of the
* arguments is a JavaScript object, it will be passed on the QueryString of the
* url, which will be turned into a dictionary on the other end.
* @private
*/
PhoneGap.run_command = function() {
if (!PhoneGap.available || !PhoneGap.queue.ready)
return;
PhoneGap.queue.ready = false;
var args = PhoneGap.queue.commands.shift();
if (PhoneGap.queue.commands.length == 0) {
clearInterval(PhoneGap.queue.timer);
PhoneGap.queue.timer = null;
}
var uri = [];
var dict = null;
for (var i = 1; i < args.length; i++) {
var arg = args[i];
if (arg == undefined || arg == null)
arg = '';
if (typeof(arg) == 'object')
dict = arg;
else
uri.push(encodeURIComponent(arg));
}
var url = "gap://" + args[0] + "/" + uri.join("/");
if (dict != null) {
var query_args = [];
for (var name in dict) {
if (typeof(name) != 'string')
continue;
query_args.push(encodeURIComponent(name) + "=" + encodeURIComponent(dict[name]));
}
if (query_args.length > 0)
url += "?" + query_args.join("&");
}
document.location = url;
};

View File

@ -0,0 +1,72 @@
/**
* This class contains position information.
* @param {Object} lat
* @param {Object} lng
* @param {Object} acc
* @param {Object} alt
* @param {Object} altacc
* @param {Object} head
* @param {Object} vel
* @constructor
*/
function Position(coords, timestamp) {
this.coords = coords;
this.timestamp = new Date().getTime();
}
function Coordinates(lat, lng, alt, acc, head, vel) {
/**
* The latitude of the position.
*/
this.latitude = lat;
/**
* The longitude of the position,
*/
this.longitude = lng;
/**
* The accuracy of the position.
*/
this.accuracy = acc;
/**
* The altitude of the position.
*/
this.altitude = alt;
/**
* The direction the device is moving at the position.
*/
this.heading = head;
/**
* The velocity with which the device is moving at the position.
*/
this.speed = vel;
}
/**
* This class specifies the options for requesting position data.
* @constructor
*/
function PositionOptions() {
/**
* Specifies the desired position accuracy.
*/
this.enableHighAccuracy = true;
/**
* The timeout after which if position data cannot be obtained the errorCallback
* is called.
*/
this.timeout = 10000;
}
/**
* This class contains information about any GSP errors.
* @constructor
*/
function PositionError() {
this.code = null;
this.message = "";
}
PositionError.UNKNOWN_ERROR = 0;
PositionError.PERMISSION_DENIED = 1;
PositionError.POSITION_UNAVAILABLE = 2;
PositionError.TIMEOUT = 3;

View File

@ -0,0 +1,89 @@
/*
* This is purely for the Android 1.5/1.6 HTML 5 Storage
* I was hoping that Android 2.0 would deprecate this, but given the fact that
* most manufacturers ship with Android 1.5 and do not do OTA Updates, this is required
*/
var DroidDB = function()
{
this.txQueue = [];
}
DroidDB.prototype.addResult = function(rawdata, tx_id)
{
eval("var data = " + rawdata);
var tx = this.txQueue[tx_id];
tx.resultSet.push(data);
}
DroidDB.prototype.completeQuery = function(tx_id)
{
var tx = this.txQueue[tx_id];
var r = new result();
r.rows.resultSet = tx.resultSet;
r.rows.length = tx.resultSet.length;
tx.win(r);
}
DroidDB.prototype.fail = function(reason, tx_id)
{
var tx = this.txQueue[tx_id];
tx.fail(reason);
}
var DatabaseShell = function()
{
}
DatabaseShell.prototype.transaction = function(process)
{
tx = new Tx();
process(tx);
}
var Tx = function()
{
droiddb.txQueue.push(this);
this.id = droiddb.txQueue.length - 1;
this.resultSet = [];
}
Tx.prototype.executeSql = function(query, params, win, fail)
{
droidStorage.executeSql(query, params, this.id);
tx.win = win;
tx.fail = fail;
}
var result = function()
{
this.rows = new Rows();
}
var Rows = function()
{
this.resultSet = [];
this.length = 0;
}
Rows.prototype.item = function(row_id)
{
return this.resultSet[id];
}
var dbSetup = function(name, version, display_name, size)
{
droidStorage.openDatabase(name, version, display_name, size)
db_object = new DatabaseShell();
return db_object;
}
PhoneGap.addConstructor(function() {
if (typeof window.openDatabase == "undefined")
{
navigator.openDatabase = window.openDatabase = dbSetup;
window.droiddb = new DroidDB();
}
});

View File

@ -1,102 +0,0 @@
/* Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
buildscript {
apply from: 'cordova.gradle'
apply from: 'repositories.gradle'
repositories repos
dependencies {
// Android Gradle Plugin (AGP) Build Tools
classpath "com.android.tools.build:gradle:${cordovaConfig.AGP_VERSION}"
}
cdvHelpers.verifyCordovaConfigForBuild()
}
allprojects {
def hasRepositoriesGradle = file('repositories.gradle').exists()
if (hasRepositoriesGradle) {
apply from: 'repositories.gradle'
} else {
apply from: "${project.rootDir}/repositories.gradle"
}
repositories repos
}
apply plugin: 'com.android.library'
android {
namespace 'org.apache.cordova'
compileSdkVersion cordovaConfig.COMPILE_SDK_VERSION
buildToolsVersion cordovaConfig.BUILD_TOOLS_VERSION
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
// For the Android Cordova Lib, we allow changing the minSdkVersion, but it is at the users own risk
defaultConfig {
minSdkVersion cordovaConfig.MIN_SDK_VERSION
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
}
packagingOptions {
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/NOTICE'
}
publishing {
singleVariant('release') {
withSourcesJar()
}
}
}
dependencies {
api "androidx.appcompat:appcompat:${cordovaConfig.ANDROIDX_APP_COMPAT_VERSION}"
implementation "androidx.webkit:webkit:${cordovaConfig.ANDROIDX_WEBKIT_VERSION}"
implementation "androidx.core:core-splashscreen:${cordovaConfig.ANDROIDX_CORE_SPLASHSCREEN_VERSION}"
}
/**
* In a project created though CLI, the `cordova-publish.gradle` file is not copied to the `framework` dir.
* App development (CLI) projects can not and should not publish our framework.
* In this case, there is no need for the gradle build process to know about the publish process.
*/
def cordovaPublishGradle = './cordova-publish.gradle'
if(file(cordovaPublishGradle).exists()) {
apply from: cordovaPublishGradle
}

85
framework/build.xml Normal file
View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="PhoneGap" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked in in Version
Control Systems. -->
<property file="local.properties" />
<!-- The build.properties file can be created by you and is never touched
by the 'android' tool. This is the place to change some of the default property values
used by the Ant rules.
Here are some properties you may want to change/update:
application.package
the name of your application package as defined in the manifest. Used by the
'uninstall' rule.
source.dir
the name of the source directory. Default is 'src'.
out.dir
the name of the output directory. Default is 'bin'.
Properties related to the SDK location or the project target should be updated
using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your application and
should be checked in in Version Control Systems.
-->
<property file="build.properties" />
<!-- The default.properties file is created and updated by the 'android' tool, as well
as ADT.
This file is an integral part of the build system for your application and
should be checked in in Version Control Systems. -->
<property file="default.properties" />
<!-- Custom Android task to deal with the project target, and import the proper rules.
This requires ant 1.6.0 or above. -->
<path id="android.antlibs">
<pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
<pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
<pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
<pathelement path="${sdk.dir}/tools/lib/apkbuilder.jar" />
<pathelement path="${sdk.dir}/tools/lib/jarutils.jar" />
</path>
<taskdef name="setup"
classname="com.android.ant.SetupTask"
classpathref="android.antlibs" />
<!-- Execute the Android Setup task that will setup some properties specific to the target,
and import the build rules files.
The rules file is imported from
<SDK>/platforms/<target_platform>/templates/android_rules.xml
To customize some build steps for your project:
- copy the content of the main node <project> from android_rules.xml
- paste it in this build.xml below the <setup /> task.
- disable the import by changing the setup task below to <setup import="false" />
This will ensure that the properties are setup correctly but that your customized
build steps are used.
-->
<setup />
<target name="move_files">
<concat destfile="../example/phonegap.js">
<fileset dir="assets/js" includes="phonegap.js.base" />
<fileset dir="assets/js" includes="*.js" />
</concat>
</target>
<target name="jar" depends="move_files, compile">
<jar jarfile="phonegap.jar" basedir="bin/classes" excludes="**/R*.class" />
</target>
<target name="phonegap_debug" depends="move_files, debug">
</target>
<target name="phonegap_release" depends="move_files, release">
</target>
</project>

View File

@ -1,16 +0,0 @@
{
"MIN_SDK_VERSION": 24,
"SDK_VERSION": 33,
"COMPILE_SDK_VERSION": null,
"GRADLE_VERSION": "7.6",
"MIN_BUILD_TOOLS_VERSION": "33.0.2",
"AGP_VERSION": "7.4.2",
"KOTLIN_VERSION": "1.7.21",
"ANDROIDX_APP_COMPAT_VERSION": "1.6.1",
"ANDROIDX_WEBKIT_VERSION": "1.6.0",
"ANDROIDX_CORE_SPLASHSCREEN_VERSION": "1.0.0",
"GRADLE_PLUGIN_GOOGLE_SERVICES_VERSION": "4.3.15",
"IS_GRADLE_PLUGIN_GOOGLE_SERVICES_ENABLED": false,
"IS_GRADLE_PLUGIN_KOTLIN_ENABLED": false,
"PACKAGE_NAMESPACE": "io.cordova.helloCordova"
}

View File

@ -1,121 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
apply plugin: 'maven-publish'
apply plugin: 'signing'
String getCordovaAndroidVersion() {
// Fetch Data from Cordova-Android package.json (Used only by framework build/publishing)
def cordovaAndroidRepoPackageJson = "$projectDir/../package.json"
if(file(cordovaAndroidRepoPackageJson).exists()) {
def packageJsonFile = new File(cordovaAndroidRepoPackageJson)
def packageJson = new groovy.json.JsonSlurper().parseText(packageJsonFile.text)
return packageJson.version
}
}
// Enable signing by default when keyId and secretKeyRingFile is defined.
ext.cdvEnableSigning = project.hasProperty('signing.keyId') && project.hasProperty('signing.secretKeyRingFile')
if (cdvEnableSigning) {
logger.debug('[Cordova] Signing has been enabled by default because the signing keyId & secretKeyRingFile has been defined.')
}
if (project.hasProperty('signEnabled')) {
if(!cdvEnableSigning && Boolean.valueOf(signEnabled)) {
logger.debug("[Cordova] The \"signEnabled\" override can not be set to \"true\" when the signing properties are missing.")
} else {
// Override the default setting with the "signEnabled" property. (In this case it should only accept false)
logger.debug("[Cordova] The \"signEnabled\" property has been detected and forcing enabled signing to \"$signEnabled\".")
cdvEnableSigning = signEnabled
}
}
afterEvaluate {
publishing {
publications {
mavenJava(MavenPublication) {
groupId = 'org.apache.cordova'
artifactId = 'framework'
version = getCordovaAndroidVersion()
from components.release
pom {
name = 'Cordova'
description = 'A library to build Cordova-based projects for the Android platform.'
url = 'https://cordova.apache.org'
licenses {
license {
name = 'Apache License, Version 2.0'
url = 'https://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id = 'stevengill'
name = 'Steve Gill'
}
developer {
id = 'erisu'
name = 'Bryan Ellis'
email = 'erisu@apache.org'
}
}
scm {
connection = 'scm:git:https://github.com/apache/cordova-android.git'
developerConnection = 'scm:git:git@github.com:apache/cordova-android.git'
url = 'https://github.com/apache/cordova-android'
}
}
}
}
repositories {
maven {
def releasesRepoUrl = 'https://repository.apache.org/content/repositories/releases'
def snapshotsRepoUrl = 'https://repository.apache.org/content/repositories/snapshots'
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
credentials {
if (project.hasProperty('apacheUsername') && project.hasProperty('apachePassword')) {
username apacheUsername
password apachePassword
}
}
}
}
signing {
if (Boolean.valueOf(cdvEnableSigning)) {
sign publishing.publications.mavenJava
}
}
}
}
tasks.whenTaskAdded {task ->
if(task.name.contains('sign')) {
logger.debug("[Cordova] The task \"${task.name}\" is enabled? ${cdvEnableSigning}")
task.enabled = cdvEnableSigning
}
}

View File

@ -1,247 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
import java.util.regex.Pattern
import io.github.g00fy2.versioncompare.Version
String doEnsureValueExists(filePath, props, key) {
if (props.get(key) == null) {
throw new GradleException(filePath + ': Missing key required "' + key + '"')
}
return props.get(key)
}
String doGetProjectTarget() {
def props = new Properties()
def propertiesFile = 'project.properties';
if(!(file(propertiesFile).exists())) {
propertiesFile = '../project.properties';
}
file(propertiesFile).withReader { reader ->
props.load(reader)
}
return doEnsureValueExists('project.properties', props, 'target')
}
Boolean isVersionValid(version) {
return !(new Version(version)).isEqual('0.0.0')
}
String doFindLatestInstalledBuildTools(String minBuildToolsVersionString) {
def buildToolsDirContents
try {
def buildToolsDir = new File(getAndroidSdkDir(), "build-tools")
buildToolsDirContents = buildToolsDir.list()
} catch (e) {
println "An exception occurred while trying to find the Android build tools."
throw e
}
def minBuildToolsVersion = new Version(minBuildToolsVersionString)
def maxVersion = new Version((minBuildToolsVersion.getMajor() + 1) + ".0.0")
def highestBuildToolsVersion = buildToolsDirContents
.collect { new Version(it) }
// Invalid inputs will be handled as 0.0.0
.findAll { it.isHigherThan('0.0.0') && it.isLowerThan(maxVersion) }
.max()
if (highestBuildToolsVersion == null) {
throw new RuntimeException("""
No installed build tools found. Please install the Android build tools
version ${minBuildToolsVersionString}.
""".replaceAll(/\s+/, ' ').trim())
}
if (highestBuildToolsVersion.isLowerThan(minBuildToolsVersionString)) {
throw new RuntimeException("""
No usable Android build tools found. Highest ${minBuildToolsVersion.getMajor()}.x installed version is
${highestBuildToolsVersion.getOriginalString()}; Recommended version
is ${minBuildToolsVersionString}.
""".replaceAll(/\s+/, ' ').trim())
}
highestBuildToolsVersion.getOriginalString()
}
String getAndroidSdkDir() {
def rootDir = project.rootDir
def androidSdkDir = null
String envVar = System.getenv("ANDROID_HOME")
if (envVar == null) {
envVar = System.getenv("ANDROID_SDK_ROOT")
}
def localProperties = new File(rootDir, 'local.properties')
String systemProperty = System.getProperty("android.home")
if (envVar != null) {
androidSdkDir = envVar
} else if (localProperties.exists()) {
Properties properties = new Properties()
localProperties.withInputStream { instr ->
properties.load(instr)
}
def sdkDirProp = properties.getProperty('sdk.dir')
if (sdkDirProp != null) {
androidSdkDir = sdkDirProp
} else {
sdkDirProp = properties.getProperty('android.dir')
if (sdkDirProp != null) {
androidSdkDir = (new File(rootDir, sdkDirProp)).getAbsolutePath()
}
}
}
if (androidSdkDir == null && systemProperty != null) {
androidSdkDir = systemProperty
}
if (androidSdkDir == null) {
throw new RuntimeException(
"Unable to determine Android SDK directory.")
}
androidSdkDir
}
def doExtractIntFromManifest(name) {
def manifestFile = file(android.sourceSets.main.manifest.srcFile)
def pattern = Pattern.compile(name + "=\"(\\d+)\"")
def matcher = pattern.matcher(manifestFile.getText())
matcher.find()
return new BigInteger(matcher.group(1))
}
def doGetConfigXml() {
def xml = file("src/main/res/xml/config.xml").getText()
// Disable namespace awareness since Cordova doesn't use them properly
return new XmlParser(false, false).parseText(xml)
}
def doGetConfigPreference(name, defaultValue) {
name = name.toLowerCase()
def root = doGetConfigXml()
def ret = defaultValue
root.preference.each { it ->
def attrName = it.attribute("name")
if (attrName && attrName.toLowerCase() == name) {
ret = it.attribute("value")
}
}
return ret
}
def doApplyCordovaConfigCustomization() {
// Apply user overide properties that comes from the "--gradleArg=-P" parameters
if (project.hasProperty('cdvMinSdkVersion')) {
cordovaConfig.MIN_SDK_VERSION = Integer.parseInt('' + cdvMinSdkVersion)
}
if (project.hasProperty('cdvSdkVersion')) {
cordovaConfig.SDK_VERSION = Integer.parseInt('' + cdvSdkVersion)
}
if (project.hasProperty('cdvCompileSdkVersion')) {
cordovaConfig.COMPILE_SDK_VERSION = Integer.parseInt('' + cdvCompileSdkVersion)
}
if (project.hasProperty('cdvMaxSdkVersion')) {
cordovaConfig.MAX_SDK_VERSION = Integer.parseInt('' + cdvMaxSdkVersion)
}
if (project.hasProperty('cdvBuildToolsVersion')) {
cordovaConfig.BUILD_TOOLS_VERSION = cdvBuildToolsVersion
}
if (project.hasProperty('cdvAndroidXAppCompatVersion')) {
cordovaConfig.ANDROIDX_APP_COMPAT_VERSION = cdvAndroidXAppCompatVersion
}
if (project.hasProperty('cdvAndroidXWebKitVersion')) {
cordovaConfig.ANDROIDX_WEBKIT_VERSION = cdvAndroidXWebKitVersion
}
if (!cordovaConfig.BUILD_TOOLS_VERSION) {
cordovaConfig.BUILD_TOOLS_VERSION = doFindLatestInstalledBuildTools(
cordovaConfig.MIN_BUILD_TOOLS_VERSION
)
}
// Ensure the configured build tools version is at least our declared minimum
def buildToolsVersion = new Version(cordovaConfig.BUILD_TOOLS_VERSION)
if (buildToolsVersion.isLowerThan(cordovaConfig.MIN_BUILD_TOOLS_VERSION)) {
throw new RuntimeException("""
Expected Android Build Tools version >= ${cordovaConfig.MIN_BUILD_TOOLS_VERSION},
but got Android Build Tools version ${cordovaConfig.BUILD_TOOLS_VERSION}. Please use version ${cordovaConfig.MIN_BUILD_TOOLS_VERSION} or later.
""".replaceAll(/\s+/, ' ').trim())
}
}
def doVerifyCordovaConfigForBuild() {
if (cordovaConfig.COMPILE_SDK_VERSION < cordovaConfig.SDK_VERSION) {
println "The \"compileSdkVersion\" (${cordovaConfig.COMPILE_SDK_VERSION}) should be greater than or equal to the the \"targetSdkVersion\" (${cordovaConfig.SDK_VERSION})."
}
}
// Properties exported here are visible to all plugins.
ext {
def defaultsFilePath = './cdv-gradle-config-defaults.json'
def projectConfigFilePath = "$rootDir/cdv-gradle-config.json"
def targetConfigFilePath = null
/**
* Check if the project config file path exists. This file will exist if coming from CLI project.
* If this file does not exist, falls back onto the default file.
* This scenario can occur if building the framework's AAR package for publishing.
*/
if(file(projectConfigFilePath).exists()) {
targetConfigFilePath = projectConfigFilePath
} else {
targetConfigFilePath = defaultsFilePath
}
def jsonFile = new File(targetConfigFilePath)
cordovaConfig = new groovy.json.JsonSlurper().parseText(jsonFile.text)
if (cordovaConfig.COMPILE_SDK_VERSION == null) {
cordovaConfig.COMPILE_SDK_VERSION = cordovaConfig.SDK_VERSION
}
// Apply Gradle Properties
doApplyCordovaConfigCustomization()
// These helpers are shared, but are not guaranteed to be stable / unchanged.
privateHelpers = {}
privateHelpers.getProjectTarget = { doGetProjectTarget() }
privateHelpers.applyCordovaConfigCustomization = { doApplyCordovaConfigCustomization() }
privateHelpers.extractIntFromManifest = { name -> doExtractIntFromManifest(name) }
privateHelpers.ensureValueExists = { filePath, props, key -> doEnsureValueExists(filePath, props, key) }
// These helpers can be used by plugins / projects and will not change.
cdvHelpers = {}
// Returns a XmlParser for the config.xml. Added in 4.1.0.
cdvHelpers.getConfigXml = { doGetConfigXml() }
// Returns the value for the desired <preference>. Added in 4.1.0.
cdvHelpers.getConfigPreference = { name, defaultValue -> doGetConfigPreference(name, defaultValue) }
// Display warnings if any cordova config is not proper for build.
cdvHelpers.verifyCordovaConfigForBuild = { doVerifyCordovaConfigForBuild() }
}
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'io.github.g00fy2:versioncompare:1.4.1@jar'
}
}

View File

@ -0,0 +1,14 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "build.properties", and override values to adapt the script to your
# project structure.
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=android-8
apk-configurations=

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<jardesc>
<jar path="PhoneGap/PhoneGap.jar"/>
<options buildIfNeeded="true" compress="true" descriptionLocation="/PhoneGap/export-phonegap.jardesc" exportErrors="true" exportWarnings="true" includeDirectoryEntries="false" overwrite="false" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
<storedRefactorings deprecationInfo="true" structuralOnly="false"/>
<selectedProjects/>
<manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
<sealing sealJar="false">
<packagesToSeal/>
<packagesToUnSeal/>
</sealing>
</manifest>
<selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false">
<javaElement handleIdentifier="=PhoneGap/src"/>
<javaElement handleIdentifier="=PhoneGap/gen"/>
</selectedElements>
</jardesc>

View File

@ -0,0 +1,29 @@
/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package com.phonegap;
public final class R {
public static final class attr {
}
public static final class drawable {
public static final int icon=0x7f020000;
}
public static final class id {
public static final int appView=0x7f050000;
public static final int go=0x7f050002;
public static final int surface=0x7f050001;
}
public static final class layout {
public static final int main=0x7f030000;
public static final int preview=0x7f030001;
}
public static final class string {
public static final int app_name=0x7f040000;
public static final int go=0x7f040001;
}
}

View File

@ -1,19 +0,0 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
android.useAndroidX=true
android.enableJetifier=true
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

View File

@ -1,5 +0,0 @@
#Thu Nov 09 10:50:25 PST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

Binary file not shown.

View File

@ -1,12 +0,0 @@
# GENERATED FILE! DO NOT EDIT!
# This file was originally created by the Android Tools, but is now
# used by cordova-android to manage the project configuration.
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
apk-configurations=
renderscript.opt.level=O0
android.library=true

View File

@ -1,22 +0,0 @@
/* Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
ext.repos = {
google()
mavenCentral()
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<WebView android:id="@+id/appView"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
/>
</LinearLayout>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="horizontal">
<SurfaceView android:id="@+id/surface"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:layout_weight="1">
</SurfaceView>
<Button
android:id="@+id/go"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/go"
android:minWidth="50dip"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginRight="5dip"
android:layout_marginTop="5dip"
/>
</RelativeLayout>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">PhoneGap</string>
<string name="go">Snap</string>
</resources>

View File

@ -0,0 +1,34 @@
package com.phonegap;
import java.util.HashMap;
import android.content.Context;
import android.webkit.WebView;
public class AccelBroker {
private WebView mAppView;
private Context mCtx;
private HashMap<String, AccelListener> accelListeners;
public AccelBroker(WebView view, Context ctx)
{
mCtx = ctx;
mAppView = view;
accelListeners = new HashMap<String, AccelListener>();
}
public String start(int freq, String key)
{
AccelListener listener = new AccelListener(key, freq, mCtx, mAppView);
listener.start(freq);
accelListeners.put(key, listener);
return key;
}
public void stop(String key)
{
AccelListener acc = accelListeners.get(key);
acc.stop();
}
}

View File

@ -0,0 +1,80 @@
package com.phonegap;
import java.util.List;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.content.Context;
import android.webkit.WebView;
public class AccelListener implements SensorEventListener{
WebView mAppView;
Context mCtx;
String mKey;
Sensor mSensor;
int mTime = 10000;
boolean started = false;
private SensorManager sensorManager;
private long lastUpdate = -1;
public AccelListener(String key, int freq, Context ctx, WebView appView)
{
mCtx = ctx;
mAppView = appView;
mKey = key;
mTime = freq;
sensorManager = (SensorManager) mCtx.getSystemService(Context.SENSOR_SERVICE);
}
public void start(int time)
{
mTime = time;
List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
if (list.size() > 0)
{
this.mSensor = list.get(0);
this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_FASTEST);
}
else
{
mAppView.loadUrl("javascript:navigator.accelerometer.epicFail(" + mKey + ", 'Failed to start')");
}
}
public void stop()
{
if(started)
sensorManager.unregisterListener(this);
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
return;
long curTime = System.currentTimeMillis();
if (lastUpdate == -1 || (curTime - lastUpdate) > mTime) {
lastUpdate = curTime;
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
//mAppView.loadUrl("javascript:gotAccel(" + x + ", " + y + "," + z + " )");
mAppView.loadUrl("javascript:navigator.accelerometer.gotAccel(" + mKey + "," + x + "," + y + "," + z + ")");
}
}
}

View File

@ -0,0 +1,193 @@
package com.phonegap;
import java.io.File;
import java.io.IOException;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaRecorder;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.util.Log;
public class AudioHandler implements OnCompletionListener, OnPreparedListener, OnErrorListener {
private MediaRecorder recorder;
private boolean isRecording = false;
MediaPlayer mPlayer;
private boolean isPlaying = false;
private String recording;
private String saveFile;
private Context mCtx;
public AudioHandler(String file, Context ctx) {
this.recording = file;
this.mCtx = ctx;
}
protected void startRecording(String file){
if (!isRecording){
saveFile=file;
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(this.recording);
try {
recorder.prepare();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
isRecording = true;
recorder.start();
}
}
private void moveFile(String file) {
/* this is a hack to save the file as the specified name */
File f = new File (this.recording);
f.renameTo(new File("/sdcard" + file));
}
protected void stopRecording(){
try{
if((recorder != null)&&(isRecording))
{
isRecording = false;
recorder.stop();
recorder.release();
}
moveFile(saveFile);
}
catch (Exception e)
{
e.printStackTrace();
}
}
protected void startPlaying(String file) {
if (isPlaying==false) {
try {
mPlayer = new MediaPlayer();
isPlaying=true;
Log.d("Audio startPlaying", "audio: " + file);
if (isStreaming(file))
{
Log.d("AudioStartPlaying", "Streaming");
// Streaming prepare async
mPlayer.setDataSource(file);
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.prepareAsync();
} else {
Log.d("AudioStartPlaying", "File");
// Not streaming prepare synchronous, abstract base directory
mPlayer.setDataSource("/sdcard/" + file);
mPlayer.prepare();
}
mPlayer.setOnPreparedListener(this);
} catch (Exception e)
{
e.printStackTrace();
}
}
}
public void stopPlaying() {
if (isPlaying) {
mPlayer.stop();
mPlayer.release();
isPlaying=false;
}
}
public void onCompletion(MediaPlayer mPlayer) {
mPlayer.stop();
mPlayer.release();
isPlaying=false;
}
protected long getCurrentPosition() {
if (isPlaying)
{
return(mPlayer.getCurrentPosition());
} else { return(-1); }
}
private boolean isStreaming(String file)
{
if (file.contains("http://")) {
return true;
} else {
return false;
}
}
protected long getDuration(String file) {
long duration = -2;
if (!isPlaying & !isStreaming(file)) {
try {
mPlayer = new MediaPlayer();
mPlayer.setDataSource("/sdcard/" + file);
mPlayer.prepare();
duration = mPlayer.getDuration();
mPlayer.release();
} catch (Exception e) { e.printStackTrace(); return(-3); }
} else
if (isPlaying & !isStreaming(file)) {
duration = mPlayer.getDuration();
} else
if (isPlaying & isStreaming(file)) {
try {
duration = mPlayer.getDuration();
} catch (Exception e) { e.printStackTrace(); return(-4); }
}else { return -1; }
return duration;
}
public void onPrepared(MediaPlayer mPlayer) {
if (isPlaying) {
mPlayer.setOnCompletionListener(this);
mPlayer.setOnBufferingUpdateListener(new OnBufferingUpdateListener()
{
public void onBufferingUpdate(MediaPlayer mPlayer, int percent)
{
/* TODO: call back, e.g. update outer progress bar */
Log.d("AudioOnBufferingUpdate", "percent: " + percent);
}
});
mPlayer.start();
}
}
public boolean onError(MediaPlayer mPlayer, int arg1, int arg2) {
Log.e("AUDIO onError", "error " + arg1 + " " + arg2);
return false;
}
protected void setAudioOutputDevice(int output){
// Changes the default audio output device to speaker or earpiece
AudioManager audiMgr = (AudioManager) mCtx.getSystemService(Context.AUDIO_SERVICE);
if (output == (2))
audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_SPEAKER, AudioManager.ROUTE_ALL);
else if (output == (1)){
audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_EARPIECE, AudioManager.ROUTE_ALL);
}else
Log.e("AudioHandler setAudioOutputDevice", " unknown output device");
}
protected int getAudioOutputDevice(){
AudioManager audiMgr = (AudioManager) mCtx.getSystemService(Context.AUDIO_SERVICE);
if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_EARPIECE)
return 1;
else if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_SPEAKER)
return 2;
else
return -1;
}
}

View File

@ -0,0 +1,34 @@
package com.phonegap;
import android.webkit.WebView;
public class CameraLauncher {
private WebView mAppView;
private DroidGap mGap;
int quality;
CameraLauncher(WebView view, DroidGap gap)
{
mAppView = view;
mGap = gap;
}
public void takePicture(int quality)
{
mGap.startCamera(quality);
}
/* Return Base64 Encoded String to Javascript */
public void processPicture( String js_out )
{
mAppView.loadUrl("javascript:navigator.camera.win('" + js_out + "');");
}
public void failPicture(String err)
{
mAppView.loadUrl("javascript:navigator.camera.fail('" + err + "');");
}
}

View File

@ -0,0 +1,218 @@
package com.phonegap;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.commons.codec.binary.Base64;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.graphics.Bitmap.CompressFormat;
import android.hardware.Camera;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
public class CameraPreview extends Activity implements SurfaceHolder.Callback{
private static final String TAG = "PhoneGapCamera";
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private RelativeLayout root;
Camera mCamera;
boolean mPreviewRunning = false;
int quality;
Intent mIntent;
public void onCreate(Bundle icicle)
{
super.onCreate(icicle);
Log.e(TAG, "onCreate");
getWindow().setFormat(PixelFormat.TRANSLUCENT);
RelativeLayout.LayoutParams containerParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT);
LinearLayout.LayoutParams surfaceParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT, 0.0F);
RelativeLayout.LayoutParams buttonParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
root = new RelativeLayout(this);
root.setLayoutParams(containerParams);
mSurfaceView = new SurfaceView(this);
mSurfaceView.setLayoutParams(surfaceParams);
root.addView(mSurfaceView);
Button stopButton = new Button(this);
stopButton.setText("click");
buttonParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
buttonParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
buttonParams.rightMargin = 5;
buttonParams.topMargin = 5;
stopButton.setLayoutParams(buttonParams);
root.addView(stopButton);
setContentView(root);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mIntent = this.getIntent();
quality = mIntent.getIntExtra("quality", 100);
stopButton.setOnClickListener(mSnapListener);
}
private OnClickListener mSnapListener = new OnClickListener() {
public void onClick(View v) {
mCamera.takePicture(null, null, mPictureCallback);
}
};
public boolean onCreateOptionsMenu(android.view.Menu menu) {
MenuItem item = menu.add(0, 0, 0, "goto gallery");
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
Uri target = Uri.parse("content://media/external/images/media");
Intent intent = new Intent(Intent.ACTION_VIEW, target);
startActivity(intent);
return true;
}
});
return true;
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
}
/*
* We got the data, send it back to PhoneGap to be handled and processed.
*
*/
Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera c) {
Log.e(TAG, "PICTURE CALLBACK: data.length = " + data.length);
storeAndExit(data);
}
};
/*
* We can't just store and exit, because Android freezes up when we try to cram a picture across a process in a Bundle.
* We HAVE to compress this data and send back the compressed data
*/
public void storeAndExit(byte[] data)
{
ByteArrayOutputStream jpeg_data = new ByteArrayOutputStream();
Bitmap myMap = BitmapFactory.decodeByteArray(data, 0, data.length);
try {
if (myMap.compress(CompressFormat.JPEG, quality, jpeg_data))
{
byte[] code = jpeg_data.toByteArray();
byte[] output = Base64.encodeBase64(code);
String js_out = new String(output);
mIntent.putExtra("picture", js_out);
setResult(RESULT_OK, mIntent);
}
}
catch(Exception e)
{
//Do shit here
}
finish();
}
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_BACK) {
return super.onKeyDown(keyCode, event);
}
if (keyCode == KeyEvent.KEYCODE_CAMERA || keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_SEARCH) {
mCamera.takePicture(null, null, mPictureCallback);
return true;
}
return false;
}
protected void onResume()
{
Log.e(TAG, "onResume");
super.onResume();
}
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
}
protected void onStop()
{
Log.e(TAG, "onStop");
super.onStop();
}
public void surfaceCreated(SurfaceHolder holder)
{
Log.e(TAG, "surfaceCreated");
mCamera = Camera.open();
//mCamera.startPreview();
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
{
Log.e(TAG, "surfaceChanged");
// XXX stopPreview() will crash if preview is not running
if (mPreviewRunning) {
mCamera.stopPreview();
}
Camera.Parameters p = mCamera.getParameters();
p.setPreviewSize(w, h);
mCamera.setParameters(p);
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mCamera.startPreview();
mPreviewRunning = true;
}
public void surfaceDestroyed(SurfaceHolder holder)
{
Log.e(TAG, "surfaceDestroyed");
mCamera.stopPreview();
mPreviewRunning = false;
mCamera.release();
}
}

View File

@ -0,0 +1,54 @@
package com.phonegap;
import java.util.List;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.content.Context;
import android.webkit.WebView;
public class CompassListener implements SensorEventListener{
WebView mAppView;
Context mCtx;
Sensor mSensor;
private SensorManager sensorManager;
CompassListener(Context ctx, WebView appView)
{
mCtx = ctx;
mAppView = appView;
sensorManager = (SensorManager) mCtx.getSystemService(Context.SENSOR_SERVICE);
}
public void start()
{
List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_ORIENTATION);
if (list.size() > 0)
{
this.mSensor = list.get(0);
this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}
public void stop()
{
this.sensorManager.unregisterListener(this);
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
public void onSensorChanged(SensorEvent event) {
// We only care about the orientation as far as it refers to Magnetic North
float heading = event.values[0];
mAppView.loadUrl("javascript:navigator.compass.setHeading(" + heading + ")");
}
}

View File

@ -0,0 +1,291 @@
package com.phonegap;
import android.provider.Contacts.ContactMethods;
import android.provider.Contacts.People;
import android.util.Log;
import android.webkit.WebView;
import android.app.Activity;
import android.content.ContentResolver;
import android.net.Uri;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
@SuppressWarnings("deprecation")
public class ContactManager {
public class ContactTriplet
{
public String name = "";
public String email = "";
public String phone = "";
}
private static final String LOG_TAG = "Contact Query";
Activity mApp;
WebView mView;
Uri mPeople = android.provider.Contacts.People.CONTENT_URI;
Uri mPhone = android.provider.Contacts.Phones.CONTENT_URI;
Uri mEmail = android.provider.Contacts.ContactMethods.CONTENT_URI;
ContactManager(Activity app, WebView view)
{
mApp = app;
mView = view;
}
// This is to add backwards compatibility to the OLD Contacts API\
public void getContactsAndSendBack()
{
String[] projection = new String[] {
People._ID,
People.NAME,
People.NUMBER,
People.PRIMARY_EMAIL_ID
};
try{
Cursor myCursor = mApp.managedQuery(mPeople, projection,
null, null , People.NAME + " ASC");
processResults(myCursor, true);
}
catch (SQLiteException ex)
{
Log.d(LOG_TAG, ex.getMessage());
}
}
public void search(String name, String npa, String email)
{
if (email.length() > 0)
searchByEmail(email);
else
searchPeople(name, npa);
}
private void searchByEmail(String email)
{
String[] projection = new String[] {
ContactMethods._ID,
ContactMethods.DATA,
ContactMethods.KIND,
ContactMethods.PERSON_ID
};
String[] variables = new String[] {
email
};
try{
Cursor myCursor = mApp.managedQuery(mEmail, projection,
"contact_methods." + ContactMethods.DATA + " = ?" + "AND contact_methods.kind = 1", variables , ContactMethods.DATA + " ASC");
getMethodData(myCursor);
}
catch (SQLiteException ex)
{
Log.d(LOG_TAG, ex.getMessage());
}
}
private void searchPeople(String name, String number)
{
String conditions = "";
if (name.length() == 0)
{
name = "%";
conditions += People.NAME + " LIKE ? AND ";
}
else
{
conditions += People.NAME + " = ? AND ";
}
if (number.length() == 0)
number = "%";
else
{
number = number.replace('+', '%');
number = number.replace('.', '%');
number = number.replace('-', '%');
}
conditions += People.NUMBER + " LIKE ? ";
String[] projection = new String[] {
People._ID,
People.NAME,
People.NUMBER,
People.PRIMARY_EMAIL_ID
};
String[] variables = new String[] {
name, number
};
try{
Cursor myCursor = mApp.managedQuery(mPeople, projection,
conditions, variables , People.NAME + " ASC");
processResults(myCursor, false);
}
catch (SQLiteException ex)
{
Log.d(LOG_TAG, ex.getMessage());
}
}
private void processResults(Cursor cur, boolean all){
if (cur.moveToFirst()) {
String name;
String phoneNumber;
String email_id;
String email;
int nameColumn = cur.getColumnIndex(People.NAME);
int phoneColumn = cur.getColumnIndex(People.NUMBER);
int emailIdColumn = cur.getColumnIndex(People.PRIMARY_EMAIL_ID);
do {
// Get the field values
name = cur.getString(nameColumn);
phoneNumber = cur.getString(phoneColumn);
email_id = cur.getString(emailIdColumn);
if (email_id != null && email_id.length() > 0)
email = getEmail(email_id);
else
email = "";
// Code for backwards compatibility with the OLD Contacts API
if (all)
mView.loadUrl("javascript:navigator.ContactManager.droidAddContact('" + name + "','" + phoneNumber + "','" + email +"')");
else
mView.loadUrl("javascript:navigator.contacts.droidFoundContact('" + name + "','" + phoneNumber + "','" + email +"')");
} while (cur.moveToNext());
if (all)
mView.loadUrl("javascript:navigator.ContactManager.droidDone()");
else
mView.loadUrl("javascript:navigator.contacts.droidDone();");
}
else
{
if(all)
mView.loadUrl("javascript:navigator.ContactManager.fail()");
else
mView.loadUrl("javascript:navigator.contacts.fail('None found!')");
}
}
private void getMethodData(Cursor cur)
{
ContactTriplet data = new ContactTriplet();
String id;
String email;
if (cur.moveToFirst()) {
int idColumn = cur.getColumnIndex(ContactMethods._ID);
int emailColumn = cur.getColumnIndex(ContactMethods.DATA);
do {
// Get the field values
id = cur.getString(idColumn);
email = cur.getString(emailColumn);
data = getContactData(id);
if(data != null)
{
data.email = email;
mView.loadUrl("javascript:navigator.Contacts.droidFoundContact('" + data.name + "','" + data.phone + "','" + data.email +"')");
}
} while (cur.moveToNext());
mView.loadUrl("javascript:navigator.contacts.droidDoneContacts();");
}
}
private ContactTriplet getContactData(String id) {
ContactTriplet data = null;
String[] projection = new String[] {
People._ID,
People.NAME,
People.NUMBER,
People.PRIMARY_EMAIL_ID
};
String[] variables = new String[] {
id
};
try{
Cursor myCursor = mApp.managedQuery(mPeople, projection,
People.PRIMARY_EMAIL_ID + " = ?", variables , People.NAME + " ASC");
data = getTriplet(myCursor);
}
catch (SQLiteException ex)
{
Log.d(LOG_TAG, ex.getMessage());
}
return data;
}
private ContactTriplet getTriplet(Cursor cur) {
ContactTriplet data = new ContactTriplet();
if (cur.moveToFirst()) {
int nameColumn = cur.getColumnIndex(People.NAME);
int numberColumn = cur.getColumnIndex(People.NUMBER);
do {
data.name = cur.getString(nameColumn);
data.phone = cur.getString(numberColumn);
} while (cur.moveToNext());
}
return data;
}
private String getEmailColumnData(Cursor cur)
{
String email = "";
if (cur != null && cur.moveToFirst()) {
int emailColumn = cur.getColumnIndex(ContactMethods.DATA);
do {
// Get the field values
email = cur.getString(emailColumn);
} while (cur.moveToNext());
}
return email;
}
private String getEmail(String id)
{
String email = "";
String[] projection = new String[] {
ContactMethods._ID,
ContactMethods.DATA,
ContactMethods.KIND
};
String[] variables = new String[] {
id
};
try
{
Cursor myCursor = mApp.managedQuery(mEmail, projection,
"contact_methods." + ContactMethods._ID + " = ?" + " AND contact_methods.kind = 1", variables , ContactMethods.DATA + " ASC");
email = getEmailColumnData(myCursor);
}
catch (SQLiteException ex)
{
Log.d(LOG_TAG, ex.getMessage());
}
return email;
}
}

View File

@ -0,0 +1,37 @@
package com.phonegap;
import android.webkit.WebView;
public class CryptoHandler {
WebView mView;
CryptoHandler(WebView view)
{
mView = view;
}
public void encrypt(String pass, String text)
{
try {
String encrypted = SimpleCrypto.encrypt(pass,text);
mView.loadUrl("javascript:Crypto.gotCryptedString('" + text + "')");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void decrypt(String pass, String text)
{
try {
String decrypted = SimpleCrypto.decrypt(pass,text);
mView.loadUrl("javascript:Crypto.gotPlainString('" + text + "')");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,126 @@
package com.phonegap;
import java.io.File;
import android.os.Environment;
import android.os.StatFs;
import android.util.Log;
public class DirectoryManager {
protected boolean testFileExists (String name){
boolean status;
if ((testSaveLocationExists())&&(!name.equals(""))){
File path = Environment.getExternalStorageDirectory();
File newPath = constructFilePaths(path.toString(), name);
status = newPath.exists();
}else{
status = false;
}
return status;
}
protected long getFreeDiskSpace(){
/*
* gets the available SD card free space or returns -1 if the SD card is not mounted.
*/
String status = Environment.getExternalStorageState();
long freeSpace = 0;
if (status.equals(Environment.MEDIA_MOUNTED)) {
try {
File path = Environment.getExternalStorageDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSize();
long availableBlocks = stat.getAvailableBlocks();
freeSpace = availableBlocks*blockSize/1024;
} catch (Exception e) {e.printStackTrace(); }
} else { return -1; }
return (freeSpace);
}
protected boolean createDirectory(String directoryName){
boolean status;
if ((testSaveLocationExists())&&(!directoryName.equals(""))){
File path = Environment.getExternalStorageDirectory();
File newPath = constructFilePaths(path.toString(), directoryName);
status = newPath.mkdir();
status = true;
}else
status = false;
return status;
}
protected boolean testSaveLocationExists(){
String sDCardStatus = Environment.getExternalStorageState();
boolean status;
if (sDCardStatus.equals(Environment.MEDIA_MOUNTED)){
status = true;
}else
status = false;
return status;
}
protected boolean deleteDirectory(String fileName){
boolean status;
SecurityManager checker = new SecurityManager();
if ((testSaveLocationExists())&&(!fileName.equals(""))){
File path = Environment.getExternalStorageDirectory();
File newPath = constructFilePaths(path.toString(), fileName);
checker.checkDelete(newPath.toString());
if(newPath.isDirectory()){
String[] listfile = newPath.list();
// delete all files within the specified directory and then delete the directory
try{
for (int i=0; i < listfile.length; i++){
File deletedFile = new File (newPath.toString()+"/"+listfile[i].toString());
deletedFile.delete();
}
newPath.delete();
Log.i("DirectoryManager deleteDirectory", fileName);
status = true;
}catch (Exception e){
e.printStackTrace();
status = false;
}
}else
status = false;
}else
status = false;
return status;
}
protected boolean deleteFile(String fileName){
boolean status;
SecurityManager checker = new SecurityManager();
if ((testSaveLocationExists())&&(!fileName.equals(""))){
File path = Environment.getExternalStorageDirectory();
File newPath = constructFilePaths(path.toString(), fileName);
checker.checkDelete(newPath.toString());
if (newPath.isFile()){
try {
Log.i("DirectoryManager deleteFile", fileName);
newPath.delete();
status = true;
}catch (SecurityException se){
se.printStackTrace();
status = false;
}
}else
status = false;
}else
status = false;
return status;
}
private File constructFilePaths (String file1, String file2){
File newPath;
newPath = new File(file1+"/"+file2);
return newPath;
}
}

View File

@ -0,0 +1,374 @@
package com.phonegap;
/* License (MIT)
* Copyright (c) 2008 Nitobi
* website: http://phonegap.com
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* Software), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebStorage;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.webkit.WebSettings.LayoutAlgorithm;
import android.widget.LinearLayout;
import android.os.Build.*;
public class DroidGap extends Activity {
private static final String LOG_TAG = "DroidGap";
protected WebView appView;
private LinearLayout root;
private PhoneGap gap;
private GeoBroker geo;
private AccelBroker accel;
private CameraLauncher launcher;
private ContactManager mContacts;
private FileUtils fs;
private NetworkManager netMan;
private CompassListener mCompass;
private Storage cupcakeStorage;
private CryptoHandler crypto;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
// This builds the view. We could probably get away with NOT having a LinearLayout, but I like having a bucket!
LinearLayout.LayoutParams containerParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT, 0.0F);
LinearLayout.LayoutParams webviewParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT, 1.0F);
root = new LinearLayout(this);
root.setOrientation(LinearLayout.VERTICAL);
root.setBackgroundColor(Color.BLACK);
root.setLayoutParams(containerParams);
appView = new WebView(this);
appView.setLayoutParams(webviewParams);
WebViewReflect.checkCompatibility();
if (android.os.Build.VERSION.RELEASE.startsWith("2."))
appView.setWebChromeClient(new EclairClient(this));
else
{
appView.setWebChromeClient(new GapClient(this));
}
appView.setWebViewClient(new GapViewClient(this));
appView.setInitialScale(100);
appView.setVerticalScrollBarEnabled(false);
WebSettings settings = appView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setJavaScriptCanOpenWindowsAutomatically(true);
settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
Package pack = this.getClass().getPackage();
String appPackage = pack.getName();
WebViewReflect.setStorage(settings, true, "/data/data/" + appPackage + "/app_database/");
// Turn on DOM storage!
WebViewReflect.setDomStorage(settings);
// Turn off native geolocation object in browser - we use our own :)
WebViewReflect.setGeolocationEnabled(settings, false);
/* Bind the appView object to the gap class methods */
bindBrowser(appView);
if(cupcakeStorage != null)
cupcakeStorage.setStorage(appPackage);
root.addView(appView);
setContentView(root);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
//don't reload the current page when the orientation is changed
super.onConfigurationChanged(newConfig);
}
private void bindBrowser(WebView appView)
{
gap = new PhoneGap(this, appView);
geo = new GeoBroker(appView, this);
accel = new AccelBroker(appView, this);
launcher = new CameraLauncher(appView, this);
mContacts = new ContactManager(this, appView);
fs = new FileUtils(appView);
netMan = new NetworkManager(this, appView);
mCompass = new CompassListener(this, appView);
crypto = new CryptoHandler(appView);
// This creates the new javascript interfaces for PhoneGap
appView.addJavascriptInterface(gap, "DroidGap");
appView.addJavascriptInterface(geo, "Geo");
appView.addJavascriptInterface(accel, "Accel");
appView.addJavascriptInterface(launcher, "GapCam");
appView.addJavascriptInterface(mContacts, "ContactHook");
appView.addJavascriptInterface(fs, "FileUtil");
appView.addJavascriptInterface(netMan, "NetworkManager");
appView.addJavascriptInterface(mCompass, "CompassHook");
appView.addJavascriptInterface(crypto, "GapCrypto");
if (android.os.Build.VERSION.RELEASE.startsWith("1."))
{
cupcakeStorage = new Storage(appView);
appView.addJavascriptInterface(cupcakeStorage, "droidStorage");
}
}
public void loadUrl(String url)
{
appView.loadUrl(url);
}
public class GapViewClient extends WebViewClient {
Context mCtx;
public GapViewClient(Context ctx)
{
mCtx = ctx;
}
/*
* (non-Javadoc)
* @see android.webkit.WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String)
*
* Note: Since we override it to make sure that we are using PhoneGap and not some other bullshit
* viewer that may or may not exist, we need to make sure that http:// and tel:// still work.
*
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// TODO: See about using a switch statement
if (url.startsWith("http://"))
{
Intent browse = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(browse);
return true;
}
else if(url.startsWith("tel://"))
{
Intent dial = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
startActivity(dial);
return true;
}
else if(url.startsWith("sms:"))
{
Uri smsUri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, smsUri);
intent.setType("vnd.android-dir/mms-sms");
startActivity(intent);
return true;
}
else
{
view.loadUrl(url);
return false;
}
}
}
/**
* Provides a hook for calling "alert" from javascript. Useful for
* debugging your javascript.
*/
public class GapClient extends WebChromeClient {
Context mCtx;
public GapClient(Context ctx)
{
mCtx = ctx;
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d(LOG_TAG, message);
// This shows the dialog box. This can be commented out for dev
AlertDialog.Builder alertBldr = new AlertDialog.Builder(mCtx);
GapOKDialog okHook = new GapOKDialog();
GapCancelDialog cancelHook = new GapCancelDialog();
alertBldr.setMessage(message);
alertBldr.setTitle("Alert");
alertBldr.setCancelable(true);
alertBldr.setPositiveButton("OK", okHook);
alertBldr.setNegativeButton("Cancel", cancelHook);
alertBldr.show();
result.confirm();
return true;
}
/*
* This is the Code for the OK Button
*/
public class GapOKDialog implements DialogInterface.OnClickListener {
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
dialog.dismiss();
}
}
public class GapCancelDialog implements DialogInterface.OnClickListener {
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
dialog.dismiss();
}
}
}
public final class EclairClient extends GapClient
{
private String TAG = "PhoneGapLog";
private long MAX_QUOTA = 100 * 1024 * 1024;
public EclairClient(Context ctx) {
super(ctx);
// TODO Auto-generated constructor stub
}
public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize,
long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater)
{
Log.d(TAG, "event raised onExceededDatabaseQuota estimatedSize: " + Long.toString(estimatedSize) + " currentQuota: " + Long.toString(currentQuota) + " totalUsedQuota: " + Long.toString(totalUsedQuota));
if( estimatedSize < MAX_QUOTA)
{
//increase for 1Mb
long newQuota = currentQuota + 1024*1024;
Log.d(TAG, "calling quotaUpdater.updateQuota newQuota: " + Long.toString(newQuota) );
quotaUpdater.updateQuota(newQuota);
}
else
{
// Set the quota to whatever it is and force an error
// TODO: get docs on how to handle this properly
quotaUpdater.updateQuota(currentQuota);
}
}
// This is a test of console.log, because we don't have this in Android 2.01
public void addMessageToConsole(String message, int lineNumber, String sourceID)
{
Log.d(TAG, sourceID + ": Line " + Integer.toString(lineNumber) + " : " + message);
}
// console.log in api level 7: http://developer.android.com/guide/developing/debug-tasks.html
public void onConsoleMessage(String message, int lineNumber, String sourceID)
{
Log.d(TAG, sourceID + ": Line " + Integer.toString(lineNumber) + " : " + message);
}
}
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_BACK) {
String testUrl = appView.getUrl();
appView.goBack();
if(appView.getUrl() == testUrl)
{
return super.onKeyDown(keyCode, event);
}
}
if (keyCode == KeyEvent.KEYCODE_MENU)
{
appView.loadUrl("javascript:keyEvent.menuTrigger()");
}
if (keyCode == KeyEvent.KEYCODE_SEARCH)
{
appView.loadUrl("javascript:keyEvent.searchTrigger()");
}
return false;
}
// This is required to start the camera activity! It has to come from the previous activity
public void startCamera(int quality)
{
Intent i = new Intent(this, CameraPreview.class);
i.setAction("android.intent.action.PICK");
i.putExtra("quality", quality);
startActivityForResult(i, 0);
}
protected void onActivityResult(int requestCode, int resultCode, Intent intent)
{
String data;
super.onActivityResult(requestCode, resultCode, intent);
if (resultCode == RESULT_OK)
{
data = intent.getStringExtra("picture");
// Send the graphic back to the class that needs it
launcher.processPicture(data);
}
else
{
launcher.failPicture("Did not complete!");
}
}
public WebView getView()
{
return this.appView;
}
}

View File

@ -0,0 +1,126 @@
package com.phonegap;
import java.io.*;
import android.webkit.WebView;
public class FileUtils {
WebView mView;
DirectoryManager fileManager;
FileReader f_in;
FileWriter f_out;
FileUtils(WebView view)
{
mView = view;
}
public int testSaveLocationExists(){
if (fileManager.testSaveLocationExists())
return 0;
else
return 1;
}
public long getFreeDiskSpace(){
long freeDiskSpace=fileManager.getFreeDiskSpace();
return freeDiskSpace;
}
public int testFileExists(String file){
if (fileManager.testFileExists(file))
return 0;
else
return 1;
}
public int testDirectoryExists(String file){
if (fileManager.testFileExists(file))
return 0;
else
return 1;
}
/**
* Delete a specific directory.
* Everyting in side the directory would be gone.
* TODO: JavaScript Call backs for success and error handling
*/
public int deleteDirectory (String dir){
if (fileManager.deleteDirectory(dir))
return 0;
else
return 1;
}
/**
* Delete a specific file.
* TODO: JavaScript Call backs for success and error handling
*/
public int deleteFile (String file){
if (fileManager.deleteFile(file))
return 0;
else
return 1;
}
/**
* Create a new directory.
* TODO: JavaScript Call backs for success and error handling
*/
public int createDirectory(String dir){
if (fileManager.createDirectory(dir))
return 0;
else
return 1;
}
public String read(String filename)
{
String data = "";
String output = "";
try {
FileInputStream fstream = new FileInputStream(filename);
DataInputStream in = new DataInputStream(fstream);
while (in.available() !=0)
{
data += in.readLine();
}
} catch (FileNotFoundException e) {
data = "FAIL: File not found";
} catch (IOException e) {
data = "FAIL: IO ERROR";
}
//mView.loadUrl("javascript:navigator.FileReader.hasRead('" + data + "')");
return data;
}
public int write(String filename, String data, boolean append)
{
String FilePath= filename;
try {
byte [] rawData = data.getBytes();
ByteArrayInputStream in = new ByteArrayInputStream(rawData);
FileOutputStream out= new FileOutputStream(FilePath, append);
byte buff[] = new byte[rawData.length];
in.read(buff, 0, buff.length);
out.write(buff, 0, rawData.length);
out.flush();
out.close();
//mView.loadUrl("javascript:navigator.FileReader.onsuccess('File written')");
} catch (Exception e) {
//mView.loadUrl("javascript:navigator.FileReader.onerror('Fail')");
// So, do we just return -1 at this point!
return -1;
}
return 0;
}
}

View File

@ -0,0 +1,48 @@
package com.phonegap;
import java.util.HashMap;
import android.content.Context;
import android.location.Location;
import android.webkit.WebView;
/*
* This class is the interface to the Geolocation. It's bound to the geo object.
*
* This class only starts and stops various GeoListeners, which consist of a GPS and a Network Listener
*/
public class GeoBroker {
private WebView mAppView;
private Context mCtx;
private HashMap<String, GeoListener> geoListeners;
public GeoBroker(WebView view, Context ctx)
{
mCtx = ctx;
mAppView = view;
geoListeners = new HashMap<String, GeoListener>();
}
public void getCurrentLocation()
{
GeoListener listener = new GeoListener("global", mCtx, 10000, mAppView);
Location loc = listener.getCurrentLocation();
String params = loc.getLatitude() + "," + loc.getLongitude() + ", " + loc.getAltitude() + "," + loc.getAccuracy() + "," + loc.getBearing();
params += "," + loc.getSpeed() + "," + loc.getTime();
mAppView.loadUrl("javascript:navigator.geolocation.gotCurrentPosition(" + params + ")");
listener.stop();
}
public String start(int freq, String key)
{
GeoListener listener = new GeoListener(key, mCtx, freq, mAppView);
geoListeners.put(key, listener);
return key;
}
public void stop(String key)
{
GeoListener geo = geoListeners.get(key);
}
}

View File

@ -0,0 +1,85 @@
package com.phonegap;
import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.webkit.WebView;
public class GeoListener {
String id;
String successCallback;
String failCallback;
GpsListener mGps;
NetworkListener mNetwork;
LocationManager mLocMan;
Context mCtx;
private WebView mAppView;
int interval;
GeoListener(String i, Context ctx, int time, WebView appView)
{
id = i;
interval = time;
mCtx = ctx;
mGps = null;
mNetwork = null;
mLocMan = (LocationManager) mCtx.getSystemService(Context.LOCATION_SERVICE);
if (mLocMan.getProvider(LocationManager.GPS_PROVIDER) != null)
mGps = new GpsListener(mCtx, interval, this);
if (mLocMan.getProvider(LocationManager.NETWORK_PROVIDER) != null)
mNetwork = new NetworkListener(mCtx, interval, this);
mAppView = appView;
}
void success(Location loc)
{
/*
* We only need to figure out what we do when we succeed!
*/
String params;
/*
* Build the giant string to send back to Javascript!
*/
params = loc.getLatitude() + "," + loc.getLongitude() + ", " + loc.getAltitude() + "," + loc.getAccuracy() + "," + loc.getBearing();
params += "," + loc.getSpeed() + "," + loc.getTime();
if(id != "global")
{
mAppView.loadUrl("javascript:navigator._geo.success(" + id + "," + params + ")");
}
}
void fail()
{
// Do we need to know why? How would we handle this?
if (id != "global") {
mAppView.loadUrl("javascript:navigator._geo.fail(" + id + ")");
}
else
{
mAppView.loadUrl("javascript:navigator._geo.fail()");
}
}
// This stops the listener
void stop()
{
if(mGps != null)
mGps.stop();
if(mNetwork != null)
mNetwork.stop();
}
public Location getCurrentLocation() {
Location loc = null;
if (mGps != null)
loc = mGps.getLocation();
if (loc == null && mNetwork != null)
loc = mNetwork.getLocation();
if(loc == null)
loc = new Location(LocationManager.NETWORK_PROVIDER);
return loc;
}
}

View File

@ -0,0 +1,101 @@
package com.phonegap;
/* License (MIT)
* Copyright (c) 2008 Nitobi
* website: http://phonegap.com
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* Software), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationListener;
import android.os.Bundle;
import android.util.Log;
public class GpsListener implements LocationListener {
private Context mCtx;
private Location cLoc;
private LocationManager mLocMan;
private static final String LOG_TAG = "PhoneGap";
private GeoListener owner;
private boolean hasData = false;
public GpsListener(Context ctx, int interval, GeoListener m)
{
owner = m;
mCtx = ctx;
mLocMan = (LocationManager) mCtx.getSystemService(Context.LOCATION_SERVICE);
mLocMan.requestLocationUpdates(LocationManager.GPS_PROVIDER, interval, 0, this);
cLoc = mLocMan.getLastKnownLocation(LocationManager.GPS_PROVIDER);
}
public Location getLocation()
{
cLoc = mLocMan.getLastKnownLocation(LocationManager.GPS_PROVIDER);
hasData = true;
return cLoc;
}
public void onProviderDisabled(String provider) {
// TODO Auto-generated method stub
Log.d(LOG_TAG, "The provider " + provider + " is disabled");
owner.fail();
}
public void onProviderEnabled(String provider) {
// TODO Auto-generated method stub
Log.d(LOG_TAG, "The provider "+ provider + " is enabled");
}
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
Log.d(LOG_TAG, "The status of the provider " + provider + " has changed");
if(status == 0)
{
Log.d(LOG_TAG, provider + " is OUT OF SERVICE");
owner.fail();
}
else if(status == 1)
{
Log.d(LOG_TAG, provider + " is TEMPORARILY_UNAVAILABLE");
}
else
{
Log.d(LOG_TAG, provider + " is Available");
}
}
public void onLocationChanged(Location location) {
Log.d(LOG_TAG, "The location has been updated!");
owner.success(location);
}
public boolean hasLocation() {
return hasData;
}
public void stop()
{
mLocMan.removeUpdates(this);
}
}

View File

@ -0,0 +1,65 @@
package com.phonegap;
import java.io.EOFException;
import java.io.FileOutputStream;
import java.io.InputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
public class HttpHandler {
protected Boolean get(String url, String file)
{
HttpEntity entity = getHttpEntity(url);
try {
writeToDisk(entity, file);
} catch (Exception e) { e.printStackTrace(); return false; }
try {
entity.consumeContent();
} catch (Exception e) { e.printStackTrace(); return false; }
return true;
}
private HttpEntity getHttpEntity(String url)
/**
* get the http entity at a given url
*/
{
HttpEntity entity=null;
try {
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet(url);
HttpResponse response = httpclient.execute(httpget);
entity = response.getEntity();
} catch (Exception e) { e.printStackTrace(); return null; }
return entity;
}
private void writeToDisk(HttpEntity entity, String file) throws EOFException
/**
* writes a HTTP entity to the specified filename and location on disk
*/
{
int i=0;
String FilePath="/sdcard/" + file;
try {
InputStream in = entity.getContent();
byte buff[] = new byte[1024];
FileOutputStream out=
new FileOutputStream(FilePath);
do {
int numread = in.read(buff);
if (numread <= 0)
break;
out.write(buff, 0, numread);
System.out.println("numread" + numread);
i++;
} while (true);
out.flush();
out.close();
} catch (Exception e) { e.printStackTrace(); }
}
}

View File

@ -0,0 +1,102 @@
package com.phonegap;
/* License (MIT)
* Copyright (c) 2008 Nitobi
* website: http://phonegap.com
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* Software), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationListener;
import android.os.Bundle;
import android.util.Log;
public class NetworkListener implements LocationListener {
private Context mCtx;
private Location cLoc;
private LocationManager mLocMan;
private static final String LOG_TAG = "PhoneGap";
GeoListener owner;
public NetworkListener(Context ctx, int interval, GeoListener m)
{
owner = m;
mCtx = ctx;
mLocMan = (LocationManager) mCtx.getSystemService(Context.LOCATION_SERVICE);
mLocMan.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, interval, 0, this);
cLoc = mLocMan.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
}
public Location getLocation()
{
cLoc = mLocMan.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
return cLoc;
}
public void onProviderDisabled(String provider) {
// TODO Auto-generated method stub
Log.d(LOG_TAG, "The provider " + provider + " is disabled");
}
public void onProviderEnabled(String provider) {
// TODO Auto-generated method stub
Log.d(LOG_TAG, "The provider "+ provider + " is enabled");
}
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
Log.d(LOG_TAG, "The status of the provider " + provider + " has changed");
if(status == 0)
{
Log.d(LOG_TAG, provider + " is OUT OF SERVICE");
}
else if(status == 1)
{
Log.d(LOG_TAG, provider + " is TEMPORARILY_UNAVAILABLE");
}
else
{
Log.d(LOG_TAG, provider + " is Available");
}
}
/*
* The GPS is the primary form of Geolocation in PhoneGap. Only fire the success variables if the GPS is down
* for some reason
*/
public void onLocationChanged(Location location) {
Log.d(LOG_TAG, "The location has been updated!");
if (!owner.mGps.hasLocation())
{
owner.success(location);
}
cLoc = location;
}
public void stop()
{
mLocMan.removeUpdates(this);
}
}

View File

@ -0,0 +1,57 @@
package com.phonegap;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.content.Context;
import android.net.*;
import android.webkit.WebView;
public class NetworkManager {
Context mCtx;
WebView mView;
ConnectivityManager sockMan;
NetworkManager(Context ctx, WebView view)
{
mCtx = ctx;
mView = view;
sockMan = (ConnectivityManager) mCtx.getSystemService(Context.CONNECTIVITY_SERVICE);
}
public boolean isAvailable()
{
NetworkInfo info = sockMan.getActiveNetworkInfo();
boolean conn = false;
if(info != null)
conn = info.isConnected();
return conn;
}
public boolean isWifiActive()
{
NetworkInfo info = sockMan.getActiveNetworkInfo();
if(info != null)
{
String type = info.getTypeName();
return type.equals("WIFI");
}
return false;
}
public boolean isReachable(String uri)
{
if (uri.indexOf("http://") == -1)
uri = "http://" + uri;
boolean reached = isAvailable();
try {
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet(uri);
httpclient.execute(httpget);
} catch (Exception e) {
reached = false;
}
return reached;
}
}

View File

@ -0,0 +1,199 @@
package com.phonegap;
/* License (MIT)
* Copyright (c) 2008 Nitobi
* website: http://phonegap.com
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* Software), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import java.util.TimeZone;
import android.content.Context;
import android.content.IntentFilter;
import android.hardware.SensorManager;
import android.net.Uri;
import android.os.Vibrator;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.webkit.WebView;
import android.media.Ringtone;
import android.media.RingtoneManager;
public class PhoneGap{
private static final String LOG_TAG = "PhoneGap";
/*
* UUID, version and availability
*/
public boolean droid = true;
public static String version = "0.9.99999";
public static String platform = "Android";
public static String uuid;
private Context mCtx;
private WebView mAppView;
AudioHandler audio;
public PhoneGap(Context ctx, WebView appView) {
this.mCtx = ctx;
this.mAppView = appView;
audio = new AudioHandler("/sdcard/tmprecording.mp3", ctx);
uuid = getUuid();
}
public void beep(long pattern)
{
Uri ringtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Ringtone notification = RingtoneManager.getRingtone(mCtx, ringtone);
for (long i = 0; i < pattern; ++i)
{
notification.play();
}
}
public void vibrate(long pattern){
// Start the vibration, 0 defaults to half a second.
if (pattern == 0)
pattern = 500;
Vibrator vibrator = (Vibrator) mCtx.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(pattern);
}
public String getPlatform()
{
return this.platform;
}
public String getUuid()
{
//TelephonyManager operator = (TelephonyManager) mCtx.getSystemService(Context.TELEPHONY_SERVICE);
//String uuid = operator.getDeviceId();
String uuid = Settings.Secure.getString(mCtx.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
return uuid;
}
public String getLine1Number(){
TelephonyManager operator = (TelephonyManager)mCtx.getSystemService(Context.TELEPHONY_SERVICE);
return operator.getLine1Number();
}
public String getDeviceId(){
TelephonyManager operator = (TelephonyManager)mCtx.getSystemService(Context.TELEPHONY_SERVICE);
return operator.getDeviceId();
}
public String getSimSerialNumber(){
TelephonyManager operator = (TelephonyManager)mCtx.getSystemService(Context.TELEPHONY_SERVICE);
return operator.getSimSerialNumber();
}
public String getSubscriberId(){
TelephonyManager operator = (TelephonyManager)mCtx.getSystemService(Context.TELEPHONY_SERVICE);
return operator.getSubscriberId();
}
public String getModel()
{
String model = android.os.Build.MODEL;
return model;
}
public String getProductName()
{
String productname = android.os.Build.PRODUCT;
return productname;
}
public String getOSVersion()
{
String osversion = android.os.Build.VERSION.RELEASE;
return osversion;
}
public String getSDKVersion()
{
String sdkversion = android.os.Build.VERSION.SDK;
return sdkversion;
}
public String getVersion()
{
return version;
}
public void httpGet(String url, String file)
/**
* grabs a file from specified url and saves it to a name and location
* the base directory /sdcard is abstracted so that paths may be the same from one mobile OS to another
* TODO: JavaScript call backs and error handling
*/
{
HttpHandler http = new HttpHandler();
http.get(url, file);
}
/**
* AUDIO
* TODO: Basic functions done but needs more work on error handling and call backs, remove record hack
*/
public void startRecordingAudio(String file)
{
/* for this to work the recording needs to be specified in the constructor,
* a hack to get around this, I'm moving the recording after it's complete
*/
audio.startRecording(file);
}
public void stopRecordingAudio()
{
audio.stopRecording();
}
public void startPlayingAudio(String file)
{
audio.startPlaying(file);
}
public void stopPlayingAudio()
{
audio.stopPlaying();
}
public long getCurrentPositionAudio()
{
System.out.println(audio.getCurrentPosition());
return(audio.getCurrentPosition());
}
public long getDurationAudio(String file)
{
System.out.println(audio.getDuration(file));
return(audio.getDuration(file));
}
public void setAudioOutputDevice(int output){
audio.setAudioOutputDevice(output);
}
public int getAudioOutputDevice(){
return audio.getAudioOutputDevice();
}
public String getTimeZoneID() {
TimeZone tz = TimeZone.getDefault();
return(tz.getID());
}
}

View File

@ -0,0 +1,96 @@
/*
* Code originally found on Android Snippets
* Contributed to snippets Ferenc Hechler
* Copyright (c) 2009 Ferenc Hechler
*/
package com.phonegap;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class SimpleCrypto {
public static String encrypt(String seed, String cleartext) throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext.getBytes());
return toHex(result);
}
public static String decrypt(String seed, String encrypted) throws Exception {
byte [] rawKey = getRawKey(seed.getBytes());
byte [] enc = toByte(encrypted);
byte [] result = decrypt(rawKey, enc);
return new String(result);
}
public static byte[] getRawKey(byte [] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
public static String toHex(String txt)
{
return toHex(txt.getBytes());
}
public static String fromHex(String hex)
{
return new String(toByte(hex));
}
public static byte[] toByte(String hexString) {
int len = hexString.length()/2;
byte[] result = new byte[len];
for (int i = 0; i < len; ++i)
{
result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
}
return result;
}
public static String toHex(byte[] buf) {
if (buf == null)
{
return "";
}
StringBuffer result = new StringBuffer(2*buf.length);
for (int i = 0; i < buf.length; i++)
{
appendHex(result, buf[i]);
}
return result.toString();
}
private final static String HEX = "01234567890ABCDEF";
private static void appendHex(StringBuffer sb, byte b)
{
sb.append(HEX.charAt((b>>4)&0x0f)).append(HEX.charAt(b&0x0f));
}
}

View File

@ -0,0 +1,17 @@
package com.phonegap;
import java.lang.reflect.Field;
import android.app.Activity;
import android.os.Bundle;
public class StandAlone extends DroidGap {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.loadUrl("file:///android_asset/www/index.html");
}
}

View File

@ -0,0 +1,78 @@
package com.phonegap;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.*;
import android.util.Log;
import android.webkit.WebView;
public class Storage {
private static final String LOG_TAG = "SQLite Storage:";
SQLiteDatabase myDb;
String path;
String txid = "";
WebView appView;
Context mCtx;
Storage(WebView view)
{
appView = view;
}
public void setStorage(String appPackage)
{
path = "/data/data/" + appPackage + "/databases/";
}
public void openDatabase(String db, String version, String display_name, long size)
{
if (path != null)
{
path += db + ".db";
myDb = SQLiteDatabase.openOrCreateDatabase(path, null);
}
}
public void executeSql(String query, String[] params, String tx_id)
{
try{
txid = tx_id;
Cursor myCursor = myDb.rawQuery(query, params);
processResults(myCursor);
}
catch (SQLiteException ex)
{
Log.d(LOG_TAG, ex.getMessage());
txid = "";
appView.loadUrl("droiddb.fail(" + ex.getMessage() + "," + txid + ")");
}
}
public void processResults(Cursor cur)
{
String key = "";
String value = "";
String resultString = "";
if (cur.moveToFirst()) {
int colCount = cur.getColumnCount();
do {
resultString = "{";
for(int i = 0; i < colCount; ++i)
{
key = cur.getColumnName(i);
value = cur.getString(i);
resultString += " \"" + key + "\" : \"" + value + "\"";
if (i != (colCount - 1))
resultString += ",";
}
resultString += "}";
appView.loadUrl("javascript:droiddb.addResult('" + resultString + "', " + txid + ")");
} while (cur.moveToNext());
appView.loadUrl("javascript:droiddb.completeQuery(" + txid + ")");
txid = "";
myDb.close();
}
}
}

View File

@ -0,0 +1,53 @@
package com.phonegap;
import java.util.List;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.content.Context;
import android.webkit.WebView;
public class TempListener implements SensorEventListener {
WebView mAppView;
Context mCtx;
Sensor mSensor;
private SensorManager sensorManager;
TempListener(Context ctx, WebView appView)
{
mCtx = ctx;
mAppView = appView;
sensorManager = (SensorManager) mCtx.getSystemService(Context.SENSOR_SERVICE);
}
public void start()
{
List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_TEMPERATURE);
if (list.size() > 0)
{
this.mSensor = list.get(0);
this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}
public void stop()
{
this.sensorManager.unregisterListener(this);
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
public void onSensorChanged(SensorEvent event) {
// We want to know what temp this is.
float temp = event.values[0];
mAppView.loadUrl("javascript:gotTemp(" + temp + ")");
}
}

View File

@ -0,0 +1,122 @@
package com.phonegap;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import android.webkit.WebSettings;
public class WebViewReflect {
private static Method mWebSettings_setDatabaseEnabled;
private static Method mWebSettings_setDatabasePath;
private static Method mWebSettings_setDomStorageEnabled;
private static Method mWebSettings_setGeolocationEnabled;
static
{
checkCompatibility();
}
private static void setDatabaseEnabled(boolean e) throws IOException {
try
{
mWebSettings_setDatabaseEnabled.invoke(e);
}
catch (InvocationTargetException ite) {
/* unpack original exception when possible */
Throwable cause = ite.getCause();
if (cause instanceof IOException) {
throw (IOException) cause;
} else if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
} else {
/* unexpected checked exception; wrap and re-throw */
throw new RuntimeException(ite);
}
} catch (IllegalAccessException ie) {
System.err.println("unexpected " + ie);
}
}
public static void checkCompatibility() {
try {
mWebSettings_setDatabaseEnabled = WebSettings.class.getMethod(
"setDatabaseEnabled", new Class[] { boolean.class } );
mWebSettings_setDatabasePath = WebSettings.class.getMethod(
"setDatabasePath", new Class[] { String.class });
mWebSettings_setDomStorageEnabled = WebSettings.class.getMethod(
"setDomStorageEnabled", new Class[] { boolean.class });
mWebSettings_setGeolocationEnabled = WebSettings.class.getMethod(
"setGeolocationEnabled", new Class[] { boolean.class });
/* success, this is a newer device */
} catch (NoSuchMethodException nsme) {
/* failure, must be older device */
}
}
public static void setStorage(WebSettings setting, boolean enable, String path) {
if (mWebSettings_setDatabaseEnabled != null) {
/* feature is supported */
try {
mWebSettings_setDatabaseEnabled.invoke(setting, enable);
mWebSettings_setDatabasePath.invoke(setting, path);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
/* feature not supported, do something else */
}
}
public static void setGeolocationEnabled(WebSettings setting, boolean enable) {
if (mWebSettings_setGeolocationEnabled != null) {
/* feature is supported */
try {
mWebSettings_setGeolocationEnabled.invoke(setting, enable);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
/* feature not supported, do something else */
System.out.println("Native Geolocation not supported - we're ok");
}
}
public static void setDomStorage(WebSettings setting)
{
if(mWebSettings_setDomStorageEnabled != null)
{
/* feature is supported */
try {
mWebSettings_setDomStorageEnabled.invoke(setting, true);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
/* feature not supported, do something else */
}
}
}

View File

@ -1,170 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.cordova.LOG;
import android.net.Uri;
public class AllowList {
private static class URLPattern {
public Pattern scheme;
public Pattern host;
public Integer port;
public Pattern path;
private String regexFromPattern(String pattern, boolean allowWildcards) {
final String toReplace = "\\.[]{}()^$?+|";
StringBuilder regex = new StringBuilder();
for (int i=0; i < pattern.length(); i++) {
char c = pattern.charAt(i);
if (c == '*' && allowWildcards) {
regex.append(".");
} else if (toReplace.indexOf(c) > -1) {
regex.append('\\');
}
regex.append(c);
}
return regex.toString();
}
public URLPattern(String scheme, String host, String port, String path) throws MalformedURLException {
try {
if (scheme == null || "*".equals(scheme)) {
this.scheme = null;
} else {
this.scheme = Pattern.compile(regexFromPattern(scheme, false), Pattern.CASE_INSENSITIVE);
}
if ("*".equals(host)) {
this.host = null;
} else if (host.startsWith("*.")) {
this.host = Pattern.compile("([a-z0-9.-]*\\.)?" + regexFromPattern(host.substring(2), false), Pattern.CASE_INSENSITIVE);
} else {
this.host = Pattern.compile(regexFromPattern(host, false), Pattern.CASE_INSENSITIVE);
}
if (port == null || "*".equals(port)) {
this.port = null;
} else {
this.port = Integer.parseInt(port,10);
}
if (path == null || "/*".equals(path)) {
this.path = null;
} else {
this.path = Pattern.compile(regexFromPattern(path, true));
}
} catch (NumberFormatException e) {
throw new MalformedURLException("Port must be a number");
}
}
public boolean matches(Uri uri) {
try {
return ((scheme == null || scheme.matcher(uri.getScheme()).matches()) &&
(host == null || host.matcher(uri.getHost()).matches()) &&
(port == null || port.equals(uri.getPort())) &&
(path == null || path.matcher(uri.getPath()).matches()));
} catch (Exception e) {
LOG.d(TAG, e.toString());
return false;
}
}
}
private ArrayList<URLPattern> allowList;
public static final String TAG = "CordovaAllowList";
public AllowList() {
this.allowList = new ArrayList<URLPattern>();
}
/* Match patterns (from http://developer.chrome.com/extensions/match_patterns.html)
*
* <url-pattern> := <scheme>://<host><path>
* <scheme> := '*' | 'http' | 'https' | 'file' | 'ftp' | 'chrome-extension'
* <host> := '*' | '*.' <any char except '/' and '*'>+
* <path> := '/' <any chars>
*
* We extend this to explicitly allow a port attached to the host, and we allow
* the scheme to be omitted for backwards compatibility. (Also host is not required
* to begin with a "*" or "*.".)
*/
public void addAllowListEntry(String origin, boolean subdomains) {
if (allowList != null) {
try {
// Unlimited access to network resources
if (origin.compareTo("*") == 0) {
LOG.d(TAG, "Unlimited access to network resources");
allowList = null;
}
else { // specific access
Pattern parts = Pattern.compile("^((\\*|[A-Za-z-]+):(//)?)?(\\*|((\\*\\.)?[^*/:]+))?(:(\\d+))?(/.*)?");
Matcher m = parts.matcher(origin);
if (m.matches()) {
String scheme = m.group(2);
String host = m.group(4);
// Special case for two urls which are allowed to have empty hosts
if (("file".equals(scheme) || "content".equals(scheme)) && host == null) host = "*";
String port = m.group(8);
String path = m.group(9);
if (scheme == null) {
// XXX making it stupid friendly for people who forget to include protocol/SSL
allowList.add(new URLPattern("http", host, port, path));
allowList.add(new URLPattern("https", host, port, path));
} else {
allowList.add(new URLPattern(scheme, host, port, path));
}
}
}
} catch (Exception e) {
LOG.d(TAG, "Failed to add origin %s", origin);
}
}
}
/**
* Determine if URL is in approved list of URLs to load.
*
* @param uri
* @return true if wide open or allow listed
*/
public boolean isUrlAllowListed(String uri) {
// If there is no allowList, then it's wide open
if (allowList == null) return true;
Uri parsedUri = Uri.parse(uri);
// Look for match in allow list
Iterator<URLPattern> pit = allowList.iterator();
while (pit.hasNext()) {
URLPattern p = pit.next();
if (p.matches(parsedUri)) {
return true;
}
}
return false;
}
}

View File

@ -1,160 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.ConfigXmlParser;
import org.apache.cordova.LOG;
import org.apache.cordova.AllowList;
import org.apache.cordova.CordovaPreferences;
import org.xmlpull.v1.XmlPullParser;
import android.content.Context;
public class AllowListPlugin extends CordovaPlugin {
public static final String PLUGIN_NAME = "CordovaAllowListPlugin";
protected static final String LOG_TAG = "CordovaAllowListPlugin";
private AllowList allowedNavigations;
private AllowList allowedIntents;
private AllowList allowedRequests;
// Used when instantiated via reflection by PluginManager
public AllowListPlugin() { }
// These can be used by embedders to allow Java-configuration of an allow list.
public AllowListPlugin(Context context) {
this(new AllowList(), new AllowList(), null);
new CustomConfigXmlParser().parse(context);
}
public AllowListPlugin(XmlPullParser xmlParser) {
this(new AllowList(), new AllowList(), null);
new CustomConfigXmlParser().parse(xmlParser);
}
public AllowListPlugin(AllowList allowedNavigations, AllowList allowedIntents, AllowList allowedRequests) {
if (allowedRequests == null) {
allowedRequests = new AllowList();
allowedRequests.addAllowListEntry("file:///*", false);
allowedRequests.addAllowListEntry("data:*", false);
}
this.allowedNavigations = allowedNavigations;
this.allowedIntents = allowedIntents;
this.allowedRequests = allowedRequests;
}
@Override
public void pluginInitialize() {
if (this.allowedNavigations == null) {
this.allowedNavigations = new AllowList();
this.allowedIntents = new AllowList();
this.allowedRequests = new AllowList();
new CustomConfigXmlParser().parse(webView.getContext());
}
}
private class CustomConfigXmlParser extends ConfigXmlParser {
private CordovaPreferences prefs = new CordovaPreferences();
@Override
public void handleStartTag(XmlPullParser xml) {
String strNode = xml.getName();
if (strNode.equals("content")) {
String startPage = xml.getAttributeValue(null, "src");
allowedNavigations.addAllowListEntry(startPage, false);
} else if (strNode.equals("allow-navigation")) {
String origin = xml.getAttributeValue(null, "href");
if ("*".equals(origin)) {
allowedNavigations.addAllowListEntry("http://*/*", false);
allowedNavigations.addAllowListEntry("https://*/*", false);
allowedNavigations.addAllowListEntry("data:*", false);
} else {
allowedNavigations.addAllowListEntry(origin, false);
}
} else if (strNode.equals("allow-intent")) {
String origin = xml.getAttributeValue(null, "href");
allowedIntents.addAllowListEntry(origin, false);
} else if (strNode.equals("access")) {
String origin = xml.getAttributeValue(null, "origin");
if (origin != null) {
if ("*".equals(origin)) {
allowedRequests.addAllowListEntry("http://*/*", false);
allowedRequests.addAllowListEntry("https://*/*", false);
} else {
String subdomains = xml.getAttributeValue(null, "subdomains");
allowedRequests.addAllowListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
}
}
}
}
@Override
public void handleEndTag(XmlPullParser xml) { }
}
@Override
public Boolean shouldAllowNavigation(String url) {
return this.allowedNavigations.isUrlAllowListed(url)
? true
: null; // default policy
}
@Override
public Boolean shouldAllowRequest(String url) {
return (Boolean.TRUE.equals(this.shouldAllowNavigation(url)) || this.allowedRequests.isUrlAllowListed(url))
? true
: null; // default policy
}
@Override
public Boolean shouldOpenExternalUrl(String url) {
return (this.allowedIntents.isUrlAllowListed(url))
? true
: null; // default policy
}
public AllowList getAllowedNavigations() {
return this.allowedNavigations;
}
public void setAllowedNavigations(AllowList allowedNavigations) {
this.allowedNavigations = allowedNavigations;
}
public AllowList getAllowedIntents() {
return this.allowedIntents;
}
public void setAllowedIntents(AllowList allowedIntents) {
this.allowedIntents = allowedIntents;
}
public AllowList getAllowedRequests() {
return this.allowedRequests;
}
public void setAllowedRequests(AllowList allowedRequests) {
this.allowedRequests = allowedRequests;
}
}

View File

@ -1,69 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
/**
* The Class AuthenticationToken defines the userName and password to be used for authenticating a web resource
*/
public class AuthenticationToken {
private String userName;
private String password;
/**
* Gets the user name.
*
* @return the user name
*/
public String getUserName() {
return userName;
}
/**
* Sets the user name.
*
* @param userName
* the new user name
*/
public void setUserName(String userName) {
this.userName = userName;
}
/**
* Gets the password.
*
* @return the password
*/
public String getPassword() {
return password;
}
/**
* Sets the password.
*
* @param password
* the new password
*/
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -1,74 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
/*
* This is a utility class that allows us to get the BuildConfig variable, which is required
* for the use of different providers. This is not guaranteed to work, and it's better for this
* to be set in the build step in config.xml
*
*/
import android.app.Activity;
import android.content.Context;
import java.lang.reflect.Field;
public class BuildHelper {
private static String TAG="BuildHelper";
/*
* This needs to be implemented if you wish to use the Camera Plugin or other plugins
* that read the Build Configuration.
*
* Thanks to Phil@Medtronic and Graham Borland for finding the answer and posting it to
* StackOverflow. This is annoying as hell! However, this method does not work with
* ProGuard, and you should use the config.xml to define the application_id
*
*/
public static Object getBuildConfigValue(Context ctx, String key)
{
try
{
String packageName = ctx.getApplicationInfo().packageName;
Class<?> clazz = Class.forName(packageName + ".BuildConfig");
Field field = clazz.getField(key);
return field.get(null);
} catch (ClassNotFoundException e) {
LOG.d(TAG, "Unable to get the BuildConfig, is this built with ANT?");
e.printStackTrace();
} catch (NoSuchFieldException e) {
LOG.d(TAG, key + " is not a valid field. Check your build.gradle");
} catch (IllegalAccessException e) {
LOG.d(TAG, "Illegal Access Exception: Let's print a stack trace.");
e.printStackTrace();
} catch (NullPointerException e) {
LOG.d(TAG, "Null Pointer Exception: Let's print a stack trace.");
e.printStackTrace();
}
return null;
}
}

View File

@ -1,142 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import org.json.JSONArray;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.PluginResult;
import org.json.JSONObject;
public class CallbackContext {
private static final String LOG_TAG = "CordovaPlugin";
private String callbackId;
private CordovaWebView webView;
protected boolean finished;
private int changingThreads;
public CallbackContext(String callbackId, CordovaWebView webView) {
this.callbackId = callbackId;
this.webView = webView;
}
public boolean isFinished() {
return finished;
}
public boolean isChangingThreads() {
return changingThreads > 0;
}
public String getCallbackId() {
return callbackId;
}
public void sendPluginResult(PluginResult pluginResult) {
synchronized (this) {
if (finished) {
LOG.w(LOG_TAG, "Attempted to send a second callback for ID: " + callbackId + "\nResult was: " + pluginResult.getMessage());
return;
} else {
finished = !pluginResult.getKeepCallback();
}
}
webView.sendPluginResult(pluginResult, callbackId);
}
/**
* Helper for success callbacks that just returns the Status.OK by default
*
* @param message The message to add to the success result.
*/
public void success(JSONObject message) {
sendPluginResult(new PluginResult(PluginResult.Status.OK, message));
}
/**
* Helper for success callbacks that just returns the Status.OK by default
*
* @param message The message to add to the success result.
*/
public void success(String message) {
sendPluginResult(new PluginResult(PluginResult.Status.OK, message));
}
/**
* Helper for success callbacks that just returns the Status.OK by default
*
* @param message The message to add to the success result.
*/
public void success(JSONArray message) {
sendPluginResult(new PluginResult(PluginResult.Status.OK, message));
}
/**
* Helper for success callbacks that just returns the Status.OK by default
*
* @param message The message to add to the success result.
*/
public void success(byte[] message) {
sendPluginResult(new PluginResult(PluginResult.Status.OK, message));
}
/**
* Helper for success callbacks that just returns the Status.OK by default
*
* @param message The message to add to the success result.
*/
public void success(int message) {
sendPluginResult(new PluginResult(PluginResult.Status.OK, message));
}
/**
* Helper for success callbacks that just returns the Status.OK by default
*/
public void success() {
sendPluginResult(new PluginResult(PluginResult.Status.OK));
}
/**
* Helper for error callbacks that just returns the Status.ERROR by default
*
* @param message The message to add to the error result.
*/
public void error(JSONObject message) {
sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message));
}
/**
* Helper for error callbacks that just returns the Status.ERROR by default
*
* @param message The message to add to the error result.
*/
public void error(String message) {
sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message));
}
/**
* Helper for error callbacks that just returns the Status.ERROR by default
*
* @param message The message to add to the error result.
*/
public void error(int message) {
sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message));
}
}

View File

@ -1,65 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import android.util.Pair;
import android.util.SparseArray;
/**
* Provides a collection that maps unique request codes to CordovaPlugins and Integers.
* Used to ensure that when plugins make requests for runtime permissions, those requests do not
* collide with requests from other plugins that use the same request code value.
*/
public class CallbackMap {
private int currentCallbackId = 0;
private SparseArray<Pair<CordovaPlugin, Integer>> callbacks;
public CallbackMap() {
this.callbacks = new SparseArray<Pair<CordovaPlugin, Integer>>();
}
/**
* Stores a CordovaPlugin and request code and returns a new unique request code to use
* in a permission request.
*
* @param receiver The plugin that is making the request
* @param requestCode The original request code used by the plugin
* @return A unique request code that can be used to retrieve this callback
* with getAndRemoveCallback()
*/
public synchronized int registerCallback(CordovaPlugin receiver, int requestCode) {
int mappedId = this.currentCallbackId++;
callbacks.put(mappedId, new Pair<CordovaPlugin, Integer>(receiver, requestCode));
return mappedId;
}
/**
* Retrieves and removes a callback stored in the map using the mapped request code
* obtained from registerCallback()
*
* @param mappedId The request code obtained from registerCallback()
* @return The CordovaPlugin and orignal request code that correspond to the
* given mappedCode
*/
public synchronized Pair<CordovaPlugin, Integer> getAndRemoveCallback(int mappedId) {
Pair<CordovaPlugin, Integer> callback = callbacks.get(mappedId);
callbacks.remove(mappedId);
return callback;
}
}

View File

@ -1,71 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import java.util.List;
import android.app.Activity;
@Deprecated // Use AllowList, CordovaPrefences, etc. directly.
public class Config {
private static final String TAG = "Config";
static ConfigXmlParser parser;
private Config() {
}
public static void init(Activity action) {
parser = new ConfigXmlParser();
parser.parse(action);
//TODO: Add feature to bring this back. Some preferences should be overridden by intents, but not all
parser.getPreferences().setPreferencesBundle(action.getIntent().getExtras());
}
// Intended to be used for testing only; creates an empty configuration.
public static void init() {
if (parser == null) {
parser = new ConfigXmlParser();
}
}
public static String getStartUrl() {
if (parser == null) {
return "file:///android_asset/www/index.html";
}
return parser.getLaunchUrl();
}
public static String getErrorUrl() {
return parser.getPreferences().getString("errorurl", null);
}
public static List<PluginEntry> getPluginEntries() {
return parser.getPluginEntries();
}
public static CordovaPreferences getPreferences() {
return parser.getPreferences();
}
public static boolean isInitialized() {
return parser != null;
}
}

View File

@ -1,209 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
public class ConfigXmlParser {
private static String TAG = "ConfigXmlParser";
private static String SCHEME_HTTP = "http";
private static String SCHEME_HTTPS = "https";
private static String DEFAULT_HOSTNAME = "localhost";
private static final String DEFAULT_CONTENT_SRC = "index.html";
private String launchUrl;
private String contentSrc;
private CordovaPreferences prefs = new CordovaPreferences();
private ArrayList<PluginEntry> pluginEntries = new ArrayList<PluginEntry>(20);
public CordovaPreferences getPreferences() {
return prefs;
}
public ArrayList<PluginEntry> getPluginEntries() {
return pluginEntries;
}
public String getLaunchUrl() {
if (launchUrl == null) {
setStartUrl(contentSrc);
}
return launchUrl;
}
public void parse(Context action) {
// First checking the class namespace for config.xml
int id = action.getResources().getIdentifier("config", "xml", action.getClass().getPackage().getName());
if (id == 0) {
// If we couldn't find config.xml there, we'll look in the namespace from AndroidManifest.xml
id = action.getResources().getIdentifier("config", "xml", action.getPackageName());
if (id == 0) {
LOG.e(TAG, "res/xml/config.xml is missing!");
return;
}
}
pluginEntries.add(
new PluginEntry(
AllowListPlugin.PLUGIN_NAME,
"org.apache.cordova.AllowListPlugin",
true
)
);
pluginEntries.add(
new PluginEntry(
SplashScreenPlugin.PLUGIN_NAME,
"org.apache.cordova.SplashScreenPlugin",
true
)
);
parse(action.getResources().getXml(id));
}
boolean insideFeature = false;
String service = "", pluginClass = "", paramType = "";
boolean onload = false;
public void parse(XmlPullParser xml) {
int eventType = -1;
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
handleStartTag(xml);
}
else if (eventType == XmlPullParser.END_TAG)
{
handleEndTag(xml);
}
try {
eventType = xml.next();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
onPostParse();
}
private void onPostParse() {
// After parsing, if contentSrc is still null, it signals
// that <content> tag was completely missing. In this case,
// default it.
// https://github.com/apache/cordova-android/issues/1432
if (contentSrc == null) {
contentSrc = DEFAULT_CONTENT_SRC;
}
}
public void handleStartTag(XmlPullParser xml) {
String strNode = xml.getName();
if (strNode.equals("feature")) {
//Check for supported feature sets aka. plugins (Accelerometer, Geolocation, etc)
//Set the bit for reading params
insideFeature = true;
service = xml.getAttributeValue(null, "name");
}
else if (insideFeature && strNode.equals("param")) {
paramType = xml.getAttributeValue(null, "name");
if (paramType.equals("service")) // check if it is using the older service param
service = xml.getAttributeValue(null, "value");
else if (paramType.equals("package") || paramType.equals("android-package"))
pluginClass = xml.getAttributeValue(null,"value");
else if (paramType.equals("onload"))
onload = "true".equals(xml.getAttributeValue(null, "value"));
}
else if (strNode.equals("preference")) {
String name = xml.getAttributeValue(null, "name").toLowerCase(Locale.ENGLISH);
String value = xml.getAttributeValue(null, "value");
prefs.set(name, value);
}
else if (strNode.equals("content")) {
String src = xml.getAttributeValue(null, "src");
if (src != null) {
contentSrc = src;
} else {
// Default
contentSrc = DEFAULT_CONTENT_SRC;
}
}
}
public void handleEndTag(XmlPullParser xml) {
String strNode = xml.getName();
if (strNode.equals("feature")) {
pluginEntries.add(new PluginEntry(service, pluginClass, onload));
service = "";
pluginClass = "";
insideFeature = false;
onload = false;
}
}
private String getLaunchUrlPrefix() {
if (prefs.getBoolean("AndroidInsecureFileModeEnabled", false)) {
return "file:///android_asset/www/";
} else {
String scheme = prefs.getString("scheme", SCHEME_HTTPS).toLowerCase();
String hostname = prefs.getString("hostname", DEFAULT_HOSTNAME).toLowerCase();
if (!scheme.contentEquals(SCHEME_HTTP) && !scheme.contentEquals(SCHEME_HTTPS)) {
LOG.d(TAG, "The provided scheme \"" + scheme + "\" is not valid. " +
"Defaulting to \"" + SCHEME_HTTPS + "\". " +
"(Valid Options=" + SCHEME_HTTP + "," + SCHEME_HTTPS + ")");
scheme = SCHEME_HTTPS;
}
return scheme + "://" + hostname + '/';
}
}
private void setStartUrl(String src) {
Pattern schemeRegex = Pattern.compile("^[a-z-]+://");
Matcher matcher = schemeRegex.matcher(src);
if (matcher.find()) {
launchUrl = src;
} else {
String launchUrlPrefix = getLaunchUrlPrefix();
// remove leading slash, "/", from content src if existing,
if (src.charAt(0) == '/') {
src = src.substring(1);
}
launchUrl = launchUrlPrefix + src;
}
}
}

View File

@ -1,536 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import java.util.ArrayList;
import java.util.Locale;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.AlertDialog;
import android.annotation.SuppressLint;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Color;
import android.media.AudioManager;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.splashscreen.SplashScreen;
/**
* This class is the main Android activity that represents the Cordova
* application. It should be extended by the user to load the specific
* html file that contains the application.
*
* As an example:
*
* <pre>
* package org.apache.cordova.examples;
*
* import android.os.Bundle;
* import org.apache.cordova.*;
*
* public class Example extends CordovaActivity {
* &#64;Override
* public void onCreate(Bundle savedInstanceState) {
* super.onCreate(savedInstanceState);
* super.init();
* // Load your application
* loadUrl(launchUrl);
* }
* }
* </pre>
*
* Cordova xml configuration: Cordova uses a configuration file at
* res/xml/config.xml to specify its settings. See "The config.xml File"
* guide in cordova-docs at http://cordova.apache.org/docs for the documentation
* for the configuration. The use of the set*Property() methods is
* deprecated in favor of the config.xml file.
*
*/
public class CordovaActivity extends AppCompatActivity {
public static String TAG = "CordovaActivity";
// The webview for our app
protected CordovaWebView appView;
private static int ACTIVITY_STARTING = 0;
private static int ACTIVITY_RUNNING = 1;
private static int ACTIVITY_EXITING = 2;
// Keep app running when pause is received. (default = true)
// If true, then the JavaScript and native code continue to run in the background
// when another application (activity) is started.
protected boolean keepRunning = true;
// Flag to keep immersive mode if set to fullscreen
protected boolean immersiveMode;
// Read from config.xml:
protected CordovaPreferences preferences;
protected String launchUrl;
protected ArrayList<PluginEntry> pluginEntries;
protected CordovaInterfaceImpl cordovaInterface;
private SplashScreen splashScreen;
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
// Handle the splash screen transition.
splashScreen = SplashScreen.installSplashScreen(this);
// need to activate preferences before super.onCreate to avoid "requestFeature() must be called before adding content" exception
loadConfig();
String logLevel = preferences.getString("loglevel", "ERROR");
LOG.setLogLevel(logLevel);
LOG.i(TAG, "Apache Cordova native platform version " + CordovaWebView.CORDOVA_VERSION + " is starting");
LOG.d(TAG, "CordovaActivity.onCreate()");
if (!preferences.getBoolean("ShowTitle", false)) {
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
}
if (preferences.getBoolean("SetFullscreen", false)) {
LOG.d(TAG, "The SetFullscreen configuration is deprecated in favor of Fullscreen, and will be removed in a future version.");
preferences.set("Fullscreen", true);
}
if (preferences.getBoolean("Fullscreen", false)) {
// NOTE: use the FullscreenNotImmersive configuration key to set the activity in a REAL full screen
// (as was the case in previous cordova versions)
if (!preferences.getBoolean("FullscreenNotImmersive", false)) {
immersiveMode = true;
setImmersiveUiVisibility();
} else {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
} else {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
}
super.onCreate(savedInstanceState);
cordovaInterface = makeCordovaInterface();
if (savedInstanceState != null) {
cordovaInterface.restoreInstanceState(savedInstanceState);
}
}
protected void init() {
appView = makeWebView();
createViews();
if (!appView.isInitialized()) {
appView.init(cordovaInterface, pluginEntries, preferences);
}
cordovaInterface.onCordovaInit(appView.getPluginManager());
// Setup the splash screen based on preference settings
cordovaInterface.pluginManager.postMessage("setupSplashScreen", splashScreen);
// Wire the hardware volume controls to control media if desired.
String volumePref = preferences.getString("DefaultVolumeStream", "");
if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) {
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
}
@SuppressWarnings("deprecation")
protected void loadConfig() {
ConfigXmlParser parser = new ConfigXmlParser();
parser.parse(this);
preferences = parser.getPreferences();
preferences.setPreferencesBundle(getIntent().getExtras());
launchUrl = parser.getLaunchUrl();
pluginEntries = parser.getPluginEntries();
Config.parser = parser;
}
//Suppressing warnings in AndroidStudio
@SuppressWarnings({"deprecation", "ResourceType"})
protected void createViews() {
//Why are we setting a constant as the ID? This should be investigated
appView.getView().setId(100);
appView.getView().setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
setContentView(appView.getView());
if (preferences.contains("BackgroundColor")) {
try {
int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK);
// Background of activity:
appView.getView().setBackgroundColor(backgroundColor);
}
catch (NumberFormatException e){
e.printStackTrace();
}
}
appView.getView().requestFocusFromTouch();
}
/**
* Construct the default web view object.
* <p/>
* Override this to customize the webview that is used.
*/
protected CordovaWebView makeWebView() {
return new CordovaWebViewImpl(makeWebViewEngine());
}
protected CordovaWebViewEngine makeWebViewEngine() {
return CordovaWebViewImpl.createEngine(this, preferences);
}
protected CordovaInterfaceImpl makeCordovaInterface() {
return new CordovaInterfaceImpl(this) {
@Override
public Object onMessage(String id, Object data) {
// Plumb this to CordovaActivity.onMessage for backwards compatibility
return CordovaActivity.this.onMessage(id, data);
}
};
}
/**
* Load the url into the webview.
*/
public void loadUrl(String url) {
if (appView == null) {
init();
}
// If keepRunning
this.keepRunning = preferences.getBoolean("KeepRunning", true);
appView.loadUrlIntoView(url, true);
}
/**
* Called when the system is about to start resuming a previous activity.
*/
@Override
protected void onPause() {
super.onPause();
LOG.d(TAG, "Paused the activity.");
if (this.appView != null) {
// CB-9382 If there is an activity that started for result and main activity is waiting for callback
// result, we shoudn't stop WebView Javascript timers, as activity for result might be using them
boolean keepRunning = this.keepRunning || this.cordovaInterface.activityResultCallback != null;
this.appView.handlePause(keepRunning);
}
}
/**
* Called when the activity receives a new intent
*/
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//Forward to plugins
if (this.appView != null)
this.appView.onNewIntent(intent);
}
/**
* Called when the activity will start interacting with the user.
*/
@Override
protected void onResume() {
super.onResume();
LOG.d(TAG, "Resumed the activity.");
if (this.appView == null) {
return;
}
if (! this.getWindow().getDecorView().hasFocus()) {
// Force window to have focus, so application always
// receive user input. Workaround for some devices (Samsung Galaxy Note 3 at least)
this.getWindow().getDecorView().requestFocus();
}
this.appView.handleResume(this.keepRunning);
}
/**
* Called when the activity is no longer visible to the user.
*/
@Override
protected void onStop() {
super.onStop();
LOG.d(TAG, "Stopped the activity.");
if (this.appView == null) {
return;
}
this.appView.handleStop();
}
/**
* Called when the activity is becoming visible to the user.
*/
@Override
protected void onStart() {
super.onStart();
LOG.d(TAG, "Started the activity.");
if (this.appView == null) {
return;
}
this.appView.handleStart();
}
/**
* The final call you receive before your activity is destroyed.
*/
@Override
public void onDestroy() {
LOG.d(TAG, "CordovaActivity.onDestroy()");
super.onDestroy();
if (this.appView != null) {
appView.handleDestroy();
}
}
/**
* Called when view focus is changed
*/
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus && immersiveMode) {
setImmersiveUiVisibility();
}
}
@SuppressLint("InlinedApi")
protected void setImmersiveUiVisibility() {
final int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
getWindow().getDecorView().setSystemUiVisibility(uiOptions);
}
@SuppressLint("NewApi")
@Override
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
// Capture requestCode here so that it is captured in the setActivityResultCallback() case.
cordovaInterface.setActivityResultRequestCode(requestCode);
super.startActivityForResult(intent, requestCode, options);
}
/**
* Called when an activity you launched exits, giving you the requestCode you started it with,
* the resultCode it returned, and any additional data from it.
*
* @param requestCode The request code originally supplied to startActivityForResult(),
* allowing you to identify who this result came from.
* @param resultCode The integer result code returned by the child activity through its setResult().
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
LOG.d(TAG, "Incoming Result. Request code = " + requestCode);
super.onActivityResult(requestCode, resultCode, intent);
cordovaInterface.onActivityResult(requestCode, resultCode, intent);
}
/**
* Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable).
* The errorCode parameter corresponds to one of the ERROR_* constants.
*
* @param errorCode The error code corresponding to an ERROR_* value.
* @param description A String describing the error.
* @param failingUrl The url that failed to load.
*/
public void onReceivedError(final int errorCode, final String description, final String failingUrl) {
final CordovaActivity me = this;
// If errorUrl specified, then load it
final String errorUrl = preferences.getString("errorUrl", null);
if ((errorUrl != null) && (!failingUrl.equals(errorUrl)) && (appView != null)) {
// Load URL on UI thread
me.runOnUiThread(new Runnable() {
public void run() {
me.appView.showWebPage(errorUrl, false, true, null);
}
});
}
// If not, then display error dialog
else {
final boolean exit = !(errorCode == WebViewClient.ERROR_HOST_LOOKUP);
me.runOnUiThread(new Runnable() {
public void run() {
if (exit) {
me.appView.getView().setVisibility(View.GONE);
me.displayError("Application Error", description + " (" + failingUrl + ")", "OK", exit);
}
}
});
}
}
/**
* Display an error dialog and optionally exit application.
*/
public void displayError(final String title, final String message, final String button, final boolean exit) {
final CordovaActivity me = this;
me.runOnUiThread(new Runnable() {
public void run() {
try {
AlertDialog.Builder dlg = new AlertDialog.Builder(me);
dlg.setMessage(message);
dlg.setTitle(title);
dlg.setCancelable(false);
dlg.setPositiveButton(button,
new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
if (exit) {
finish();
}
}
});
dlg.create();
dlg.show();
} catch (Exception e) {
finish();
}
}
});
}
/*
* Hook in Cordova for menu plugins
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (appView != null) {
appView.getPluginManager().postMessage("onCreateOptionsMenu", menu);
}
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (appView != null) {
appView.getPluginManager().postMessage("onPrepareOptionsMenu", menu);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (appView != null) {
appView.getPluginManager().postMessage("onOptionsItemSelected", item);
}
return true;
}
/**
* Called when a message is sent to plugin.
*
* @param id The message id
* @param data The message data
* @return Object or null
*/
public Object onMessage(String id, Object data) {
if ("onReceivedError".equals(id)) {
JSONObject d = (JSONObject) data;
try {
this.onReceivedError(d.getInt("errorCode"), d.getString("description"), d.getString("url"));
} catch (JSONException e) {
e.printStackTrace();
}
} else if ("exit".equals(id)) {
finish();
}
return null;
}
protected void onSaveInstanceState(Bundle outState) {
cordovaInterface.onSaveInstanceState(outState);
super.onSaveInstanceState(outState);
}
/**
* Called by the system when the device configuration changes while your activity is running.
*
* @param newConfig The new device configuration
*/
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (this.appView == null) {
return;
}
PluginManager pm = this.appView.getPluginManager();
if (pm != null) {
pm.onConfigurationChanged(newConfig);
}
}
/**
* Called by the system when the user grants permissions
*
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[],
int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
try
{
cordovaInterface.onRequestPermissionResult(requestCode, permissions, grantResults);
}
catch (JSONException e)
{
LOG.d(TAG, "JSONException: Parameters fed into the method are not valid");
e.printStackTrace();
}
}
}

View File

@ -1,113 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Base64;
public class CordovaArgs {
private JSONArray baseArgs;
public CordovaArgs(JSONArray args) {
this.baseArgs = args;
}
// Pass through the basics to the base args.
public Object get(int index) throws JSONException {
return baseArgs.get(index);
}
public boolean getBoolean(int index) throws JSONException {
return baseArgs.getBoolean(index);
}
public double getDouble(int index) throws JSONException {
return baseArgs.getDouble(index);
}
public int getInt(int index) throws JSONException {
return baseArgs.getInt(index);
}
public JSONArray getJSONArray(int index) throws JSONException {
return baseArgs.getJSONArray(index);
}
public JSONObject getJSONObject(int index) throws JSONException {
return baseArgs.getJSONObject(index);
}
public long getLong(int index) throws JSONException {
return baseArgs.getLong(index);
}
public String getString(int index) throws JSONException {
return baseArgs.getString(index);
}
public Object opt(int index) {
return baseArgs.opt(index);
}
public boolean optBoolean(int index) {
return baseArgs.optBoolean(index);
}
public double optDouble(int index) {
return baseArgs.optDouble(index);
}
public int optInt(int index) {
return baseArgs.optInt(index);
}
public JSONArray optJSONArray(int index) {
return baseArgs.optJSONArray(index);
}
public JSONObject optJSONObject(int index) {
return baseArgs.optJSONObject(index);
}
public long optLong(int index) {
return baseArgs.optLong(index);
}
public String optString(int index) {
return baseArgs.optString(index);
}
public boolean isNull(int index) {
return baseArgs.isNull(index);
}
// The interesting custom helpers.
public byte[] getArrayBuffer(int index) throws JSONException {
String encoded = baseArgs.getString(index);
return Base64.decode(encoded, Base64.DEFAULT);
}
}

View File

@ -1,186 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import android.annotation.SuppressLint;
import java.security.SecureRandom;
import org.json.JSONArray;
import org.json.JSONException;
/**
* Contains APIs that the JS can call. All functions in here should also have
* an equivalent entry in CordovaChromeClient.java, and be added to
* cordova-js/lib/android/plugin/android/promptbasednativeapi.js
*/
public class CordovaBridge {
private static final String LOG_TAG = "CordovaBridge";
private PluginManager pluginManager;
private NativeToJsMessageQueue jsMessageQueue;
private volatile int expectedBridgeSecret = -1; // written by UI thread, read by JS thread.
public CordovaBridge(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue) {
this.pluginManager = pluginManager;
this.jsMessageQueue = jsMessageQueue;
}
public String jsExec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException {
if (!verifySecret("exec()", bridgeSecret)) {
return null;
}
// If the arguments weren't received, send a message back to JS. It will switch bridge modes and try again. See CB-2666.
// We send a message meant specifically for this case. It starts with "@" so no other message can be encoded into the same string.
if (arguments == null) {
return "@Null arguments.";
}
jsMessageQueue.setPaused(true);
try {
// Tell the resourceApi what thread the JS is running on.
CordovaResourceApi.jsThread = Thread.currentThread();
pluginManager.exec(service, action, callbackId, arguments);
String ret = null;
if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING) {
ret = jsMessageQueue.popAndEncode(false);
}
return ret;
} catch (Throwable e) {
e.printStackTrace();
return "";
} finally {
jsMessageQueue.setPaused(false);
}
}
public void jsSetNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException {
if (!verifySecret("setNativeToJsBridgeMode()", bridgeSecret)) {
return;
}
jsMessageQueue.setBridgeMode(value);
}
public String jsRetrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException {
if (!verifySecret("retrieveJsMessages()", bridgeSecret)) {
return null;
}
return jsMessageQueue.popAndEncode(fromOnlineEvent);
}
private boolean verifySecret(String action, int bridgeSecret) throws IllegalAccessException {
if (!jsMessageQueue.isBridgeEnabled()) {
if (bridgeSecret == -1) {
LOG.d(LOG_TAG, action + " call made before bridge was enabled.");
} else {
LOG.d(LOG_TAG, "Ignoring " + action + " from previous page load.");
}
return false;
}
// Bridge secret wrong and bridge not due to it being from the previous page.
if (expectedBridgeSecret < 0 || bridgeSecret != expectedBridgeSecret) {
LOG.e(LOG_TAG, "Bridge access attempt with wrong secret token, possibly from malicious code. Disabling exec() bridge!");
clearBridgeSecret();
throw new IllegalAccessException();
}
return true;
}
/** Called on page transitions */
void clearBridgeSecret() {
expectedBridgeSecret = -1;
}
public boolean isSecretEstablished() {
return expectedBridgeSecret != -1;
}
/** Called by cordova.js to initialize the bridge. */
//On old Androids SecureRandom isn't really secure, this is the least of your problems if
//you're running Android 4.3 and below in 2017
int generateBridgeSecret() {
SecureRandom randGen = new SecureRandom();
expectedBridgeSecret = randGen.nextInt(Integer.MAX_VALUE);
return expectedBridgeSecret;
}
public void reset() {
jsMessageQueue.reset();
clearBridgeSecret();
}
public String promptOnJsPrompt(String origin, String message, String defaultValue) {
if (defaultValue != null && defaultValue.startsWith("gap:")) {
JSONArray array;
try {
array = new JSONArray(defaultValue.substring(4));
int bridgeSecret = array.getInt(0);
String service = array.getString(1);
String action = array.getString(2);
String callbackId = array.getString(3);
String r = jsExec(bridgeSecret, service, action, callbackId, message);
return r == null ? "" : r;
} catch (JSONException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return "";
}
// Sets the native->JS bridge mode.
else if (defaultValue != null && defaultValue.startsWith("gap_bridge_mode:")) {
try {
int bridgeSecret = Integer.parseInt(defaultValue.substring(16));
jsSetNativeToJsBridgeMode(bridgeSecret, Integer.parseInt(message));
} catch (NumberFormatException e){
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return "";
}
// Polling for JavaScript messages
else if (defaultValue != null && defaultValue.startsWith("gap_poll:")) {
int bridgeSecret = Integer.parseInt(defaultValue.substring(9));
try {
String r = jsRetrieveJsMessages(bridgeSecret, "1".equals(message));
return r == null ? "" : r;
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return "";
}
else if (defaultValue != null && defaultValue.startsWith("gap_init:")) {
// Protect against random iframes being able to talk through the bridge.
// Trust only pages which the app would have been allowed to navigate to anyway.
if (pluginManager.shouldAllowBridgeAccess(origin)) {
// Enable the bridge
int bridgeMode = Integer.parseInt(defaultValue.substring(9));
jsMessageQueue.setBridgeMode(bridgeMode);
// Tell JS the bridge secret.
int secret = generateBridgeSecret();
return ""+secret;
} else {
LOG.e(LOG_TAG, "gap_init called from restricted origin: " + origin);
}
return "";
}
return null;
}
}

Some files were not shown because too many files have changed in this diff Show More