I developed App. In this I integrate cordova-Paypal. I added paypal mobile sdk. but it shows PayPalMobile is not defined occurred in console.I copied helper.js and sdk.js in js folder. Please help me.
var app=angular.module('starter.payPalService', [])
app.factory('PaypalService', ['$q', '$ionicPlatform', 'shopSettings', '$filter', '$timeout', function ($q, $ionicPlatform, shopSettings, $filter, $timeout) {
var init_defer;
/**
* Service object
* #type object
*/
var service = {
initPaymentUI: initPaymentUI,
createPayment: createPayment,
configuration: configuration,
onPayPalMobileInit: onPayPalMobileInit,
makePayment: makePayment
};
/**
* #ngdoc method
* #name initPaymentUI
* #methodOf app.PaypalService
* #description
* Inits the payapl ui with certain envs.
*
*
* #returns {object} Promise paypal ui init done
*/
function initPaymentUI() {
init_defer = $q.defer();
$ionicPlatform.ready().then(function () {
var clientIDs = {
"PayPalEnvironmentProduction": shopSettings.payPalProductionId,
"PayPalEnvironmentSandbox": shopSettings.payPalSandboxId
};
PayPalMobile.init(clientIDs, onPayPalMobileInit);
});
return init_defer.promise;
}
/**
* #ngdoc method
* #name createPayment
* #methodOf app.PaypalService
* #param {string|number} total total sum. Pattern 12.23
* #param {string} name name of the item in paypal
* #description
* Creates a paypal payment object
*
*
* #returns {object} PayPalPaymentObject
*/
function createPayment(total, name) {
// "Sale == > immediate payment
// "Auth" for payment authorization only, to be captured separately at a later time.
// "Order" for taking an order, with authorization and capture to be done separately at a later time.
var payment = new PayPalPayment("" + total, "EUR", "" + name, "Sale");
return payment;
}
/**
* #ngdoc method
* #name configuration
* #methodOf app.PaypalService
* #description
* Helper to create a paypal configuration object
*
*
* #returns {object} PayPal configuration
*/
function configuration() {
// for more options see `paypal-mobile-js-helper.js`
var config = new PayPalConfiguration({merchantName: shopSettings.payPalShopName, merchantPrivacyPolicyURL: shopSettings.payPalMerchantPrivacyPolicyURL, merchantUserAgreementURL: shopSettings.payPalMerchantUserAgreementURL});
return config;
}
function onPayPalMobileInit() {
$ionicPlatform.ready().then(function () {
// must be called
// use PayPalEnvironmentNoNetwork mode to get look and feel of the flow
PayPalMobile.prepareToRender(shopSettings.payPalEnv, configuration(), function () {
$timeout(function () {
init_defer.resolve();
});
});
});
}
/**
* #ngdoc method
* #name makePayment
* #methodOf app.PaypalService
* #param {string|number} total total sum. Pattern 12.23
* #param {string} name name of the item in paypal
* #description
* Performs a paypal single payment
*
*
* #returns {object} Promise gets resolved on successful payment, rejected on error
*/
function makePayment(total, name) {
var defer = $q.defer();
total = $filter('number')(total, 2);
$ionicPlatform.ready().then(function () {
PayPalMobile.renderSinglePaymentUI(createPayment(total, name), function (result) {
$timeout(function () {
defer.resolve(result);
});
}, function (error) {
$timeout(function () {
defer.reject(error);
});
});
});
return defer.promise;
}
return service;
}]);
Controller.js:
controller('MyCtrl' , function($scope,PaypalService){
$scope.click=function()
{
console.log("i am call");
// console.log(error);
PaypalService.initPaymentUI().then(function () {
console.log("i am call 2");
PaypalService.makePayment(1, "Total Amount").then(function (response) {
alert("success"+JSON.stringify(response));
}, function (error) {
alert('Transaction Canceled');
});
});
};
})
Error in console:
ReferenceError: PayPalMobile is not defined
at payPalService.js:35
at processQueue (ionic.bundle.js:29132)
at ionic.bundle.js:29148
at Scope.$eval (ionic.bundle.js:30400)
at Scope.$digest (ionic.bundle.js:30216)
at ChildScope.$apply (ionic.bundle.js:30508)
at HTMLButtonElement.<anonymous> (ionic.bundle.js:65428)
at defaultHandlerWrapper (ionic.bundle.js:16792)
at HTMLButtonElement.eventHandler (ionic.bundle.js:16780)
at triggerMouseEvent (ionic.bundle.js:2953)
I copied helper.js and sdk.js in js folder
You should not need to add the helper JS manually. The plugin will create those files automatically under /js folder. You simply need to put the following statement after cordova.js is imported.
Your index.html should look something like these:
<!DOCTYPE html>
<html>
<head>
...
<script src="cordova.js"></script>
<script src="js/paypal-mobile-js-helper.js"></script>
...
</head>
<body>
...
</body>
</html>
Also ensure that you test your app on either Android emulator or iOS simulator ... or better yet, a real Android / iOS device.
Reference: https://github.com/paypal/PayPal-Cordova-Plugin#your-app-integration
Related
I write a auto testcase to test Tiktok
In the testcase, before I switch to next video, I will check the current video type, and do something.
Log.i(TAG, "check if it's a vr video")
val byRule = By.clazz("android.view.View").descContains("点击体验VR直播,按钮")
if(device.hasObject(byRule)){
Log.i(TAG, "vr video")
device.findObject(byRule)?.click()
SystemClock.sleep(time*1000L)
device.pressBack()
} else {
Log.d(TAG, "no")
}
the UiDevice.hasObject method will return immediately at first time when this code run, however the second time to run becomes very slow, it's takes about more than 10 secs.
Anyone can tell me why?
full code is here
package com.dvdface.qq.uitestdemo
import android.content.Context
import android.content.Intent
import android.os.SystemClock
import android.util.Log
import android.view.Surface
import androidx.test.core.app.ApplicationProvider
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.uiautomator.*
import org.junit.After
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
import org.junit.Before
private const val TIMEOUT = 5000L
private const val PKG_NAME = "com.ss.android.ugc.aweme"
private const val TAG = "TiktokTest"
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
#RunWith(AndroidJUnit4::class)
class DouYinTest {
private lateinit var device:UiDevice
private lateinit var context:Context
#Before
fun setUp() {
// init device
device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
// init context
context = ApplicationProvider.getApplicationContext<Context>()
assertNotNull(context)
}
#After
fun tearDown() {
}
#Test
#LargeTest
fun fastFlingLiveVideos() {
// launch
launch(PKG_NAME)
// click suggestion menu
gotoSuggestionMenu()
// fling video
flingVideo(true, 5, 2*60*60)
}
/**
* watch video by fling gesture
* videos have many categories:
* short video
* fullscreen video
* live video
* VR video
* picture video
* ai video
* reminder video
* Params:
* enter - whether to enter play page by click full screen watch / landscape watch / VR watch
* time - how long to play in single video, in seconds
* duration - how long to test, in seconds
* Returns:
* None
*/
private fun flingVideo(enter:Boolean=false, time:Int=3, duration:Long=14400) {
val actionsForVideos = listOf<()->Unit>(
{
// fullscreen video
Log.i(TAG, "check if it's a fullscreen video")
val byRule = By.clazz("android.widget.LinearLayout").descContains("全屏观看,按钮")
if(device.hasObject(byRule)) {
Log.i(TAG, "fullscreen video")
device.findObject(byRule)?.click()
SystemClock.sleep(time*1000L)
device.pressBack()
} else {
Log.d(TAG, "no")
}
}, {
// live video
Log.i(TAG, "check if it's a live video")
val byRule = By.clazz("android.widget.TextView").text("点击进入直播间")
if(device.hasObject(byRule)){
Log.i(TAG, "live video")
device.findObject(byRule)?.click()
SystemClock.sleep(time*1000L)
device.pressBack()
} else {
Log.d(TAG, "no")
}
}, {
// vr video
Log.i(TAG, "check if it's a vr video")
val byRule = By.clazz("android.view.View").descContains("点击体验VR直播,按钮")
if(device.hasObject(byRule)){
Log.i(TAG, "vr video")
device.findObject(byRule)?.click()
SystemClock.sleep(time*1000L)
device.pressBack()
} else {
Log.d(TAG, "no")
}
}, {
// picture video
Log.i(TAG, "check if it's a picture video")
val byRule = By.clazz("android.widget.LinearLayout").hasChild(By.clazz("android.widget.TextView").text("图文"))
if(device.hasObject(byRule)) {
Log.i(TAG, "picture video")
SystemClock.sleep(time*1000L)
} else {
Log.d(TAG, "no")
}
}, {
// ai video
Log.i(TAG, "check if it's a ai video")
val byRule = By.clazz("android.widget.TextView").textContains("特效")
if(device.hasObject(byRule)){
Log.i(TAG, "ai video")
SystemClock.sleep(time*1000L)
} else {
Log.d(TAG, "no")
}
}
)
val startTime = SystemClock.elapsedRealtime()
while((SystemClock.elapsedRealtime() - startTime) < duration * 1000L) {
// according to video type , do something
if(enter) {
actionsForVideos.forEach{
it()
}
}
// next
fling()
Log.i(TAG, "elapse ${(SystemClock.elapsedRealtime() - startTime)/1000}s")
}
}
/**
* fling gesture
* Params:
* step - steps to fling, more steps more slower, default 6
* Returns:
* none
*/
private fun fling(step:Int = 6) {
Log.i(TAG, "fling")
when(device.displayRotation) {
Surface.ROTATION_0, Surface.ROTATION_180 -> { Log.d(TAG, "fling in portrait"); device.swipe(500, 1400, 500, 800, step) }
Surface.ROTATION_90, Surface.ROTATION_270 -> { Log.d(TAG, "fling in landscape"); device.swipe(1200, 800, 1200, 300, step) }
else -> Log.e(TAG, "unknown direction")
}
}
/**
* goto suggestion menu
* Params:
* None
* Returns:
* None
*/
private fun gotoSuggestionMenu() {
// click suggestion menu
Log.i(TAG, "enter suggestion")
Log.d(TAG, "find first page button")
device.findObject(By.clazz("android.widget.TextView").textStartsWith("首页").descContains("首页,按钮"))?.let {
Log.d(TAG, "click first page button")
it.click()
}
Log.d(TAG, "find suggestion")
device.findObject(By.clazz("android.widget.TextView").textStartsWith("推荐").descContains("推荐,按钮"))?.let {
Log.d(TAG, "click suggestion button")
it.click()
it.wait(Until.descContains("已选中"), TIMEOUT)
}
}
/**
* launch app by clear Intent.FLAG_ACTIVITY_CLEAR_TASK
* Params:
* package - package to launch
* timeout - launching timeout, default 5000 ms
* Returns:
* None
*/
private fun launch(packageName:String, timeout:Long=5000) {
Log.i(TAG, "launch app")
// get launch intent
Log.d(TAG, "get launch intent")
var intent = context.packageManager.getLaunchIntentForPackage(packageName)
// intent can't be null
assertNotNull(intent)
intent?.apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
}
// start app
Log.d(TAG, "launch app by intent")
context.startActivity(intent)
// wait app
Log.d(TAG, "wait app to launch")
device.wait(Until.hasObject(By.pkg(packageName).depth(0)), timeout)
}
}
by looking into the logs, I found it's caused by this code:
My Testcases use androidx.test.uiautomator
androidx.test.uiautomator is a test library, which source code is https://androidx.tech/artifacts/test.uiautomator/uiautomator/2.2.0
there is a Configurator class in the androidx.test.uiautomator , can be used to configure running parameters in this library: this Configurator is singleton, we can get its instance by
Configurator.getInstance()
we can change wait time by call setWaitForXXXXTimeout method to change waiting time. What's idle status ? the idle status is that the uiautomator doesn't recevie accessibility events from accessibility service for a while. For some reason, some accessibility events were kept sending to the uiautomator client, it can't enter the idle status, and causes too much time to wait for idle. So we need cut off the waiting time, to speed up auto testcase running speed.
/**
* Sets the timeout for waiting for the user interface to go into an idle
* state before starting a uiautomator action.
*
* By default, all core uiautomator objects except {#link UiDevice} will perform
* this wait before starting to search for the widget specified by the
* object's {#link UiSelector}. Once the idle state is detected or the
* timeout elapses (whichever occurs first), the object will start to wait
* for the selector to find a match.
* See {#link #setWaitForSelectorTimeout(long)}
*
* #param timeout Timeout value in milliseconds
* #return self
* #since API Level 18
*/
public Configurator setWaitForIdleTimeout(long timeout) {
mWaitForIdleTimeout = timeout;
return this;
}
/**
* Sets the timeout for waiting for a widget to become visible in the user
* interface so that it can be matched by a selector.
*
* Because user interface content is dynamic, sometimes a widget may not
* be visible immediately and won't be detected by a selector. This timeout
* allows the uiautomator framework to wait for a match to be found, up until
* the timeout elapses.
*
* #param timeout Timeout value in milliseconds.
* #return self
* #since API Level 18
*/
public Configurator setWaitForSelectorTimeout(long timeout) {
mWaitForSelector = timeout;
return this;
}
/**
* Sets the timeout for waiting for an acknowledgement of an
* uiautomtor scroll swipe action.
*
* The acknowledgment is an AccessibilityEvent,
* corresponding to the scroll action, that lets the framework determine if
* the scroll action was successful. Generally, this timeout should not be modified.
* See {#link UiScrollable}
*
* #param timeout Timeout value in milliseconds
* #return self
* #since API Level 18
*/
public Configurator setScrollAcknowledgmentTimeout(long timeout) {
mScrollEventWaitTimeout = timeout;
return this;
}
/**
* Sets the timeout for waiting for an acknowledgment of generic uiautomator
* actions, such as clicks, text setting, and menu presses.
*
* The acknowledgment is an AccessibilityEvent,
* corresponding to an action, that lets the framework determine if the
* action was successful. Generally, this timeout should not be modified.
* See {#link UiObject}
*
* #param timeout Timeout value in milliseconds
* #return self
* #since API Level 18
*/
public Configurator setActionAcknowledgmentTimeout(long timeout)
I'm wanting to implement admob reward videos for my online android game.
Is there a way to confirm on my server that the admob video was watched and that someone hasn't just sent false "Video watched" flags to obtain rewards..
Thanks.
As of November 27th, server-side rewarded video verification is in a closed beta.
See https://groups.google.com/d/msg/google-admob-ads-sdk/weXTAGZfYQ8/zITHZhGlCAAJ.
You can use this Node.js piece of code
const queryString = require('query-string');
const crypto = require('crypto');
const axios = require('axios');
const GOOGLE_AD_KEY_URL = 'https://gstatic.com/admob/reward/verifier-keys.json';
/**
* Fetches the google public keys for the admob providers.
* These keys changes time to time.
*/
const getGoogleKeysMap = async () => {
let googleKeyRes = await axios.get(GOOGLE_AD_KEY_URL);
let {keys} = googleKeyRes.data;
if (!keys) {
throw new Error('No keys found from google keys');
}
/** For each of the keys array save it base 64 in decoded form in the key map */
let keyMap = {};
keys.forEach(k => {
keyMap[`${k.keyId}`] = crypto.createPublicKey(k.pem);
console.log(keyMap[`${k.keyId}`]);
});
return keyMap;
};
/**
* Verifies the callback url query params string,
* Resolves the promise if verification was successful, else fails.
* Wanna 'debug' then pass the second parameter as true.
* #param {String} queryUrl
* #param {Boolean} debug
*/
async function verify(queryUrl, debug) {
try {
if (typeof queryUrl !== "string") throw new TypeError("URL needs to be string!");
/**
* Request coming as callback from admob must contain the 'signature' and the 'user_id'.
* For more info https://developers.google.com/admob/android/rewarded-video-ssv
*/
const {signature, key_id} = queryString.parse(queryUrl);
if (!signature) {
throw new Error('No signature value exist in the URL param');
}
if(debug) {
console.debug('Signature and KeyId ---');
console.debug(signature, key_id);
// console.log('Signature and KeyId ---');
// console.log(signature, key_id);
}
let queryParamsString = queryUrl;
if (queryParamsString.indexOf('?') > -1) {
queryParamsString = queryUrl.split('?')[1];
}
if(debug) {
console.debug('Query param string ---');
// console.log('Query param string ---');
console.debug(queryParamsString);
// console.log(queryParamsString);
}
/**
* As per admob,
* The last two query parameters of rewarded video SSV callbacks are always signature and key_id, in that order.
* The remaining query parameters specify the content to be verified.
*/
let contentToVerify = queryParamsString.substring(0, queryParamsString.indexOf('signature') -1);
if(debug) {
console.debug('Content to verify ---');
// console.log(contentToVerify);
// console.log('Content to verify ---');
console.debug(contentToVerify);
}
let keyMap = await getGoogleKeysMap();
if(keyMap[`${key_id}`]) {
let publicKey = keyMap[`${key_id}`];
const verifier = crypto.createVerify('RSA-SHA256');
verifier.update(contentToVerify);
let result = verifier.verify(publicKey, signature, 'base64');
if (result) {
console.debug('Result ---');
console.debug(result);
return true;
} else {
console.debug('Failure ---');
console.debug(result);
throw new Error('Invalid Signature Supplied');
}
} else {
console.debug('Key id provided doesn\'t exist ---');
throw new Error('Key id provided doesn\'t exist in the google public keys');
}
} catch (error) {
}
}
module.exports.verify = verify;
Not so familiar with Android studio as I keep running into problems. My app was running fine until something broke in relation to the cloud backend, which I never touched and this error keeps me from continuing:
warning: [options] bootstrap class path not set in conjunction with -source 1.7
C:\Users\JC\Projects\ca.usimage.resto.RestonetActivity\backend\build\generated-source\endpoints\java\com\example\jean\myapplication\backend\myApi\MyApi.java:279: error: method does not override or implement a method from a supertype
#Override
^
C:\Users\JC\Projects\ca.usimage.resto.RestonetActivity\backend\build\generated-source\endpoints\java\com\example\jean\myapplication\backend\myApi\MyApi.java:281: error: cannot find symbol
return (Builder) super.setBatchPath(batchPath);
^
symbol: method setBatchPath(String)
2 errors
1 warning
I upgraded to Android Studio 3, updated everything I could, even backtracked to a new project from a year old github backup and I still get the error.
What went wrong?
About to reinstall Android Studio, or switch back to Eclipse (yuck)
/*
* 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.
*/
/*
* This code was generated by https://github.com/google/apis-client-generator/
* (build: 2017-11-07 19:12:12 UTC)
* on 2017-11-21 at 02:13:58 UTC
* Modify at your own risk.
*/
package com.example.jean.myapplication.backend.myApi;
/**
* Service definition for MyApi (v1).
*
* <p>
* This is an API
* </p>
*
* <p>
* For more information about this service, see the
* API Documentation
* </p>
*
* <p>
* This service uses {#link MyApiRequestInitializer} to initialize global parameters via its
* {#link Builder}.
* </p>
*
* #since 1.3
* #author Google, Inc.
*/
#SuppressWarnings("javadoc")
public class MyApi extends com.google.api.client.googleapis.services.json.AbstractGoogleJsonClient {
// Note: Leave this static initializer at the top of the file.
static {
com.google.api.client.util.Preconditions.checkState(
com.google.api.client.googleapis.GoogleUtils.MAJOR_VERSION == 1 &&
com.google.api.client.googleapis.GoogleUtils.MINOR_VERSION >= 15,
"You are currently running with version %s of google-api-client. " +
"You need at least version 1.15 of google-api-client to run version " +
"1.23.0 of the myApi library.", com.google.api.client.googleapis.GoogleUtils.VERSION);
}
/**
* The default encoded root URL of the service. This is determined when the library is generated
* and normally should not be changed.
*
* #since 1.7
*/
public static final String DEFAULT_ROOT_URL = "https://myApplicationId.appspot.com/_ah/api/";
/**
* The default encoded service path of the service. This is determined when the library is
* generated and normally should not be changed.
*
* #since 1.7
*/
public static final String DEFAULT_SERVICE_PATH = "myApi/v1/sayHi/";
/**
* The default encoded batch path of the service. This is determined when the library is
* generated and normally should not be changed.
*
* #since 1.23
*/
public static final String DEFAULT_BATCH_PATH = "batch";
/**
* The default encoded base URL of the service. This is determined when the library is generated
* and normally should not be changed.
*/
public static final String DEFAULT_BASE_URL = DEFAULT_ROOT_URL + DEFAULT_SERVICE_PATH;
/**
* Constructor.
*
* <p>
* Use {#link Builder} if you need to specify any of the optional parameters.
* </p>
*
* #param transport HTTP transport, which should normally be:
* <ul>
* <li>Google App Engine:
* {#code com.google.api.client.extensions.appengine.http.UrlFetchTransport}</li>
* <li>Android: {#code newCompatibleTransport} from
* {#code com.google.api.client.extensions.android.http.AndroidHttp}</li>
* <li>Java: {#link com.google.api.client.googleapis.javanet.GoogleNetHttpTransport#newTrustedTransport()}
* </li>
* </ul>
* #param jsonFactory JSON factory, which may be:
* <ul>
* <li>Jackson: {#code com.google.api.client.json.jackson2.JacksonFactory}</li>
* <li>Google GSON: {#code com.google.api.client.json.gson.GsonFactory}</li>
* <li>Android Honeycomb or higher:
* {#code com.google.api.client.extensions.android.json.AndroidJsonFactory}</li>
* </ul>
* #param httpRequestInitializer HTTP request initializer or {#code null} for none
* #since 1.7
*/
public MyApi(com.google.api.client.http.HttpTransport transport, com.google.api.client.json.JsonFactory jsonFactory,
com.google.api.client.http.HttpRequestInitializer httpRequestInitializer) {
this(new Builder(transport, jsonFactory, httpRequestInitializer));
}
/**
* #param builder builder
*/
MyApi(Builder builder) {
super(builder);
}
#Override
protected void initialize(com.google.api.client.googleapis.services.AbstractGoogleClientRequest<?> httpClientRequest) throws java.io.IOException {
super.initialize(httpClientRequest);
}
/**
* Create a request for the method "sayHi".
*
* This request holds the parameters needed by the myApi server. After setting any optional
* parameters, call the {#link SayHi#execute()} method to invoke the remote operation.
*
* #param name
* #return the request
*/
public SayHi sayHi(java.lang.String name) throws java.io.IOException {
SayHi result = new SayHi(name);
initialize(result);
return result;
}
public class SayHi extends MyApiRequest<com.example.jean.myapplication.backend.myApi.model.MyBean> {
private static final String REST_PATH = "{name}";
/**
* Create a request for the method "sayHi".
*
* This request holds the parameters needed by the the myApi server. After setting any optional
* parameters, call the {#link SayHi#execute()} method to invoke the remote operation. <p> {#link
* SayHi#initialize(com.google.api.client.googleapis.services.AbstractGoogleClientRequest)} must
* be called to initialize this instance immediately after invoking the constructor. </p>
*
* #param name
* #since 1.13
*/
protected SayHi(java.lang.String name) {
super(MyApi.this, "POST", REST_PATH, null, com.example.jean.myapplication.backend.myApi.model.MyBean.class);
this.name = com.google.api.client.util.Preconditions.checkNotNull(name, "Required parameter name must be specified.");
}
#Override
public SayHi setAlt(java.lang.String alt) {
return (SayHi) super.setAlt(alt);
}
#Override
public SayHi setFields(java.lang.String fields) {
return (SayHi) super.setFields(fields);
}
#Override
public SayHi setKey(java.lang.String key) {
return (SayHi) super.setKey(key);
}
#Override
public SayHi setOauthToken(java.lang.String oauthToken) {
return (SayHi) super.setOauthToken(oauthToken);
}
#Override
public SayHi setPrettyPrint(java.lang.Boolean prettyPrint) {
return (SayHi) super.setPrettyPrint(prettyPrint);
}
#Override
public SayHi setQuotaUser(java.lang.String quotaUser) {
return (SayHi) super.setQuotaUser(quotaUser);
}
#Override
public SayHi setUserIp(java.lang.String userIp) {
return (SayHi) super.setUserIp(userIp);
}
#com.google.api.client.util.Key
private java.lang.String name;
/**
*/
public java.lang.String getName() {
return name;
}
public SayHi setName(java.lang.String name) {
this.name = name;
return this;
}
#Override
public SayHi set(String parameterName, Object value) {
return (SayHi) super.set(parameterName, value);
}
}
/**
* Builder for {#link MyApi}.
*
* <p>
* Implementation is not thread-safe.
* </p>
*
* #since 1.3.0
*/
public static final class Builder extends com.google.api.client.googleapis.services.json.AbstractGoogleJsonClient.Builder {
/**
* Returns an instance of a new builder.
*
* #param transport HTTP transport, which should normally be:
* <ul>
* <li>Google App Engine:
* {#code com.google.api.client.extensions.appengine.http.UrlFetchTransport}</li>
* <li>Android: {#code newCompatibleTransport} from
* {#code com.google.api.client.extensions.android.http.AndroidHttp}</li>
* <li>Java: {#link com.google.api.client.googleapis.javanet.GoogleNetHttpTransport#newTrustedTransport()}
* </li>
* </ul>
* #param jsonFactory JSON factory, which may be:
* <ul>
* <li>Jackson: {#code com.google.api.client.json.jackson2.JacksonFactory}</li>
* <li>Google GSON: {#code com.google.api.client.json.gson.GsonFactory}</li>
* <li>Android Honeycomb or higher:
* {#code com.google.api.client.extensions.android.json.AndroidJsonFactory}</li>
* </ul>
* #param httpRequestInitializer HTTP request initializer or {#code null} for none
* #since 1.7
*/
public Builder(com.google.api.client.http.HttpTransport transport, com.google.api.client.json.JsonFactory jsonFactory,
com.google.api.client.http.HttpRequestInitializer httpRequestInitializer) {
super(
transport,
jsonFactory,
DEFAULT_ROOT_URL,
DEFAULT_SERVICE_PATH,
httpRequestInitializer,
false);
setBatchPath(DEFAULT_BATCH_PATH);
}
/** Builds a new instance of {#link MyApi}. */
#Override
public MyApi build() {
return new MyApi(this);
}
#Override
public Builder setRootUrl(String rootUrl) {
return (Builder) super.setRootUrl(rootUrl);
}
#Override
public Builder setServicePath(String servicePath) {
return (Builder) super.setServicePath(servicePath);
}
#Override
public Builder setBatchPath(String batchPath) {
return (Builder) super.setBatchPath(batchPath);
}
#Override
public Builder setHttpRequestInitializer(com.google.api.client.http.HttpRequestInitializer httpRequestInitializer) {
return (Builder) super.setHttpRequestInitializer(httpRequestInitializer);
}
#Override
public Builder setApplicationName(String applicationName) {
return (Builder) super.setApplicationName(applicationName);
}
#Override
public Builder setSuppressPatternChecks(boolean suppressPatternChecks) {
return (Builder) super.setSuppressPatternChecks(suppressPatternChecks);
}
#Override
public Builder setSuppressRequiredParameterChecks(boolean suppressRequiredParameterChecks) {
return (Builder) super.setSuppressRequiredParameterChecks(suppressRequiredParameterChecks);
}
#Override
public Builder setSuppressAllChecks(boolean suppressAllChecks) {
return (Builder) super.setSuppressAllChecks(suppressAllChecks);
}
/**
* Set the {#link MyApiRequestInitializer}.
*
* #since 1.12
*/
public Builder setMyApiRequestInitializer(
MyApiRequestInitializer myapiRequestInitializer) {
return (Builder) super.setGoogleClientRequestInitializer(myapiRequestInitializer);
}
#Override
public Builder setGoogleClientRequestInitializer(
com.google.api.client.googleapis.services.GoogleClientRequestInitializer googleClientRequestInitializer) {
return (Builder) super.setGoogleClientRequestInitializer(googleClientRequestInitializer);
}
}
}
I got several UIDs like this:
10022, 10011, 1000
Actually I know the user names of them are u0_a22, u0_a11, system.
But the question is, how can I get the user name using UIDs? There is no /etc/passwd file at all.
I wrote a utility class to get the UID/GID name by hardcoding values from android_filesystem_config.h.
Usage:
String uid = AndroidFilesystemConfig.getNameForId(1000);
AndroidFilesystemConfig.java
import android.os.Build;
import android.util.SparseArray;
import java.util.Locale;
/**
* <p>System Users and Groups for the Android platform as specified in
* android_filesystem_config.h.</p>
*
* <p>Last updated: April 20th, 2016</p>
*
* <p><b>Note:</b> Some OEMs may have specific UIDs for other system users not in this class.</p>
*/
public class AndroidFilesystemConfig {
/* first app user */
private static final int AID_APP = 10000;
/* offset for uid ranges for each user */
private static final int AID_USER = 100000;
/* start of gids for apps in each user to share */
private static final int AID_SHARED_GID_START = 50000;
/* end of gids for apps in each user to share */
private static final int AID_SHARED_GID_END = 59999;
private static final SparseArray<String> SYSTEM_IDS = new SparseArray<>();
static {
putSystemId(0, "root"); /* traditional unix root user */
putSystemId(1000, "system"); /* system server */
putSystemId(1001, "radio"); /* telephony subsystem, RIL */
putSystemId(1002, "bluetooth"); /* bluetooth subsystem */
putSystemId(1003, "graphics"); /* graphics devices */
putSystemId(1004, "input"); /* input devices */
putSystemId(1005, "audio"); /* audio devices */
putSystemId(1006, "camera"); /* camera devices */
putSystemId(1007, "log"); /* log devices */
putSystemId(1008, "compass"); /* compass device */
putSystemId(1009, "mount"); /* mountd socket */
putSystemId(1010, "wifi"); /* wifi subsystem */
putSystemId(1011, "adb"); /* android debug bridge (adbd) */
putSystemId(1012, "install"); /* group for installing packages */
putSystemId(1013, "media"); /* mediaserver process */
putSystemId(1014, "dhcp"); /* dhcp client */
putSystemId(1015, "sdcard_rw"); /* external storage write access */
putSystemId(1016, "vpn"); /* vpn system */
putSystemId(1017, "keystore"); /* keystore subsystem */
putSystemId(1018, "usb"); /* USB devices */
putSystemId(1019, "drm"); /* DRM server */
putSystemId(1020, "mdnsr"); /* MulticastDNSResponder (service discovery) */
putSystemId(1021, "gps"); /* GPS daemon */
// 1022 is deprecated and not used.
putSystemId(1023, "media_rw"); /* internal media storage write access */
putSystemId(1024, "mtp"); /* MTP USB driver access */
// 1025 is deprecated and not used.
putSystemId(1026, "drmrpc"); /* group for drm rpc */
putSystemId(1027, "nfc"); /* nfc subsystem */
putSystemId(1028, "sdcard_r"); /* external storage read access */
putSystemId(1029, "clat"); /* clat part of nat464 */
putSystemId(1030, "loop_radio"); /* loop radio devices */
putSystemId(1031, "mediadrm"); /* MediaDrm plugins */
putSystemId(1032, "package_info"); /* access to installed package details */
putSystemId(1033, "sdcard_pics"); /* external storage photos access */
putSystemId(1034, "sdcard_av"); /* external storage audio/video access */
putSystemId(1035, "sdcard_all"); /* access all users external storage */
putSystemId(1036, "logd"); /* log daemon */
putSystemId(1037, "shared_relro"); /* creator of shared GNU RELRO files */
putSystemId(1038, "dbus"); /* dbus-daemon IPC broker process */
putSystemId(1039, "tlsdate"); /* tlsdate unprivileged user */
putSystemId(1040, "mediaex"); /* mediaextractor process */
putSystemId(1041, "audioserver"); /* audioserver process */
putSystemId(1042, "metrics_coll"); /* metrics_collector process */
putSystemId(1043, "metricsd"); /* metricsd process */
putSystemId(1044, "webserv"); /* webservd process */
putSystemId(1045, "debuggerd"); /* debuggerd unprivileged user */
putSystemId(1046, "mediacodec"); /* mediacodec process */
putSystemId(1047, "cameraserver"); /* cameraserver process */
putSystemId(1048, "firewall"); /* firewalld process */
putSystemId(1049, "trunks"); /* trunksd process (TPM daemon) */
putSystemId(1050, "nvram"); /* Access-controlled NVRAM */
putSystemId(1051, "dns"); /* DNS resolution daemon (system: netd) */
putSystemId(1052, "dns_tether"); /* DNS resolution daemon (tether: dnsmasq) */
putSystemId(1053, "webview_zygote"); /* WebView zygote process */
putSystemId(1054, "vehicle_network"); /* Vehicle network service */
putSystemId(1055, "media_audio"); /* GID for audio files on internal media storage */
putSystemId(1056, "media_video"); /* GID for video files on internal media storage */
putSystemId(1057, "media_image"); /* GID for image files on internal media storage */
putSystemId(2000, "shell"); /* adb and debug shell user */
putSystemId(2001, "cache"); /* cache access */
putSystemId(2002, "diag"); /* access to diagnostic resources */
/* The range 2900-2999 is reserved for OEMs */
// The 3000 series are intended for use as supplemental group id's only. They indicate
// special Android capabilities that the kernel is aware of.
putSystemId(3001, "net_bt_admin"); /* bluetooth: get any socket */
putSystemId(3002, "net_bt"); /* bluetooth: get sco, rfcomm or l2cap sockets */
putSystemId(3003, "inet"); /* can get AF_INET and AF_INET6 sockets */
putSystemId(3004, "net_raw"); /* can get raw INET sockets */
putSystemId(3005, "net_admin"); /* can configure interfaces and routing tables. */
putSystemId(3006, "net_bw_stats"); /* read bandwidth statistics */
putSystemId(3007, "net_bw_acct"); /* change bandwidth statistics accounting */
putSystemId(3008, "net_bt_stack"); /* bluetooth: access config files */
putSystemId(3009, "readproc"); /* Allow /proc read access */
putSystemId(3010, "wakelock"); /* Allow system wakelock read/write access */
/* The range 5000-5999 is also reserved for OEMs. */
putSystemId(9997, "everybody"); /* shared between all apps in the same profile */
putSystemId(9998, "misc"); /* access to misc storage */
putSystemId(9999, "nobody");
}
private static void putSystemId(int id, String name) {
// Check if the uid exists before adding it so we don't add unsupported ids.
if (android.os.Process.getUidForName(name) != id) {
// Not valid on this system. Most likely due to a lower API.
return;
}
SYSTEM_IDS.put(id, name);
}
/**
* #return An array of system UIDs
*/
public static SparseArray<String> getSystemIds() {
return SYSTEM_IDS;
}
/**
* Returns the UID/GID name assigned to a particular id, or {#code null} if there is none.
*
* #param id
* The UID/GID of a process or file
* #return the name of the UID/GID or {#code null} if the id is unrecognized.
*/
public static String getNameForId(int id) {
String name = SYSTEM_IDS.get(id);
if (name == null) {
if (id >= AID_SHARED_GID_START && id <= AID_SHARED_GID_END) {
name = String.format(Locale.ENGLISH, "all_a%d", id - AID_SHARED_GID_START);
} else {
int appId = id - AID_APP;
int userId = 0;
// loop until we get the correct user id.
// 100000 is the offset for each user.
while (appId > AID_USER) {
appId -= AID_USER;
userId++;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
// u{user_id}_a{app_id} is used on API 17+ for multiple user account support.
name = String.format(Locale.ENGLISH, "u%d_a%d", userId, appId);
} else {
// app_{app_id} is used below API 17.
name = String.format(Locale.ENGLISH, "app_%d", appId);
}
}
}
return name;
}
private AndroidFilesystemConfig() {
throw new AssertionError("no instances");
}
}
Using Reflection:
Inspired by odexide's answer, here is some really ugly reflection I wouldn't advise using that will get the UID/GID name from getpwuid(uid_t):
public static String getNameForUid(int id) {
try {
Class<?> clazz = Class.forName("libcore.io.Libcore");
Field field = clazz.getDeclaredField("os");
if (!field.isAccessible()) {
field.setAccessible(true);
}
Object os = field.get(null);
if (os != null) {
Method getpwuid = os.getClass().getMethod("getpwuid", int.class);
if (getpwuid != null) {
if (!getpwuid.isAccessible()) {
getpwuid.setAccessible(true);
}
Object passwd = getpwuid.invoke(os, id);
if (passwd != null) {
Field pw_name = passwd.getClass().getDeclaredField("pw_name");
if (!pw_name.isAccessible()) {
pw_name.setAccessible(true);
}
return (String) pw_name.get(passwd);
}
}
}
} catch (Exception ignored) {
}
return null;
}
The UIDs are hardcoded for the android implemention of libc (bionic) and also are provided in ranges for apps. android_filesystem_config.h explains the mapping.
You should be able to use the bionic stubs (c++) for getpwuid(uid_t).
For more info on the stubs, see this AOSP documentation.
I have to develop an Android application using phongap that retrieves the sensors data from the device.
One of the sensors I have to listen to is the ambient light sensor. This sensor has no implementation in phoneGap, so I have to add it as a plugin to PhoneGap.
I know how to add plugin and I know how to access the ALS data from Java - but in order to be sure that I am implementing it well I want to implement it as PhoneGap implements other sensors like Accelerometer. Therefor I wrote a ALSManager class in java that I implemented as I found the Accelerometer was implemented here:
https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/AccelListener.java
and added lightSensor and lightValues modules like the acceleromter and acceleration modules.
But when I run this application I got following error message:
TypeError: Object # has no method 'getCurrentLight'
(and in the lightSensor module I have getCurrentLight method).
does any one can please suggest me what I am missing? or what do I have to do?
Thanks in advance,
The code I added in the cordova-2.5.0.js. Let me know if it's not enough:
// file: lib/common/plugin/LightValues.js
define("cordova/plugin/LightValues", function(require, exports, module) {
var Acceleration = function(lux, timestamp) {
this.lux = lux;
this.timestamp = timestamp || (new Date()).getTime();
};
module.exports = LightValues;
});
// file: lib/common/plugin/lightSensor.js
define("cordova/plugin/lightSensor", function(require, exports, module) {
/**
* This class provides access to device accelerometer data.
* #constructor
*/
var argscheck = require('cordova/argscheck'),
utils = require("cordova/utils"),
exec = require("cordova/exec"),
LightValues = require('cordova/plugin/LightValues');
// Is the accel sensor running?
var running = false;
// Keeps reference to watchAcceleration calls.
var timers = {};
// Array of listeners; used to keep track of when we should call start and stop.
var listeners = [];
// Last returned acceleration object from native
var light = null;
// Tells native to start.
function start() {
exec(function(a) {
var tempListeners = listeners.slice(0);
light = new LightValues(a.lux, a.timestamp);
for (var i = 0, l = tempListeners.length; i < l; i++) {
tempListeners[i].win(light);
}
}, function(e) {
var tempListeners = listeners.slice(0);
for (var i = 0, l = tempListeners.length; i < l; i++) {
tempListeners[i].fail(e);
}
}, "Light", "start", []);
running = true;
}
// Tells native to stop.
function stop() {
exec(null, null, "Light", "stop", []);
running = false;
}
// Adds a callback pair to the listeners array
function createCallbackPair(win, fail) {
return {win:win, fail:fail};
}
// Removes a win/fail listener pair from the listeners array
function removeListeners(l) {
var idx = listeners.indexOf(l);
if (idx > -1) {
listeners.splice(idx, 1);
if (listeners.length === 0) {
stop();
}
}
}
var lightSensor = {
/**
* Asynchronously acquires 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. (OPTIONAL)
* #param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL)
*/
getCurrentLight: function(successCallback, errorCallback, options) {
//argscheck.checkArgs('fFO', 'lightSensor.getCurrentLight', arguments);
var p;
var win = function(a) {
removeListeners(p);
successCallback(a);
};
var fail = function(e) {
removeListeners(p);
errorCallback && errorCallback(e);
};
p = createCallbackPair(win, fail);
listeners.push(p);
if (!running) {
start();
}
},
/**
* Asynchronously acquires 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. (OPTIONAL)
* #param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL)
* #return String The watch id that must be passed to #clearWatch to stop watching.
*/
watchLight: function(successCallback, errorCallback, options) {
//argscheck.checkArgs('fFO', 'lightSensor.watchLight', arguments);
// Default interval (10 sec)
var frequency = (options && options.frequency && typeof options.frequency == 'number') ? options.frequency : 10000;
// Keep reference to watch id, and report accel readings as often as defined in frequency
var id = utils.createUUID();
var p = createCallbackPair(function(){}, function(e) {
removeListeners(p);
errorCallback && errorCallback(e);
});
listeners.push(p);
timers[id] = {
timer:window.setInterval(function() {
if (light) {
successCallback(light);
}
}, frequency),
listeners:p
};
if (running) {
// If we're already running then immediately invoke the success callback
// but only if we have retrieved a value, sample code does not check for null ...
if (light) {
successCallback(light);
}
} else {
start();
}
return id;
},
/**
* Clears the specified accelerometer watch.
*
* #param {String} id The id of the watch returned from #watchAcceleration.
*/
clearWatch: function(id) {
// Stop javascript timer & remove from timer list
if (id && timers[id]) {
window.clearInterval(timers[id].timer);
removeListeners(timers[id].listeners);
delete timers[id];
}
}
};
module.exports = lightSensor;
});
I think maybe the problem is that you are adding your plugin code to cordova-2.5.0.js file. Instead what you should do is create a standalone JS file for each of your JavaScript files and then cordova.require() those files in the HTML page where you want to use that functionality.
So, create LightValues.js and LightSensor.js as separate files somewhere in your www folder. Then in your HTML file, make sure to include the JS file: <script type="text/javascript" src="path-to-lightSensor.JS-file"> (You'd only need to include this one file since it require()s the second one.)
Next, in deviceReady() function, you can call the light sensor with var lightSensor = cordova.require("cordova/plugin/lightSensor"). Notice that the cordova/plugin/lightSensor is not the path to the JS file but the name of it module that you declared in the define() section when you wrote the plugin.
After this you should be able to call lightSensor.getCurrentLight(). If you console.log(lightSensor) you would expect to see all of the available methods that you wrote.
Note that I'm not positive that cordova.require and cordova.define work in cordova-2.5. I'd hope that they do but this page sort of suggests it may not be supported until 2.6. If you are having problems after splitting the files out, maybe it is because of this.
I have just developed a light sensor plugin and fortunately succeed. I have just read the code above and found some small mistakes about identifier e.g "var Acceleration = function(lux, timestamp)" the variable should be LightValues instead of Acceleration. So please check the code first to void some essential mistakes. Then, I first use "lux" as the name of the variable but I got an undefined value of light when debugging the program. So I change "lux" into "x" and it did work!
There are 6 files should be paid attention to: the "LightListener.java", "LightValues.js", "LightSensor.js","cordova_plugin.js", "index.html" and "config.xml". If you configure all the 6 files, the program should work.