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)
Related
I'm working with a recently purchased DragonBoard 410C running the Android 5.1 operating system and using Android Studio with Kotlin for generating a sample application that explores some of the hardware such as the 40 pin low power connector.
My question is how to access the 40 pin low power connector with the GPIO pins using Kotlin and Android Studio.
From my research thus far, it appears that the mraa library is the path to success however I have been unable to find any documentation on using the library with Kotlin.
How do I get started with the mraa library with Kotlin to access the 40 pin low power connector?
Or is there a different approach?
My first example is a simple blink the LED application however I have no idea as to how to access pins of the low power connector using Kotlin.
Notes and resources
mraa documentation page
Libmraa is a C/C++ library with bindings to Python, Javascript and
Java to interface with the I/O on Galileo, Edison & other platforms,
with a structured and sane API where port names/numbering matches the
board that you are on. Use of libmraa does not tie you to specific
hardware with board detection done at runtime you can create portable
code that will work across the supported platforms.
upm library for mraa GitHub repository
The UPM repository provides software drivers for a wide variety of
commonly used sensors and actuators. These software drivers interact
with the underlying hardware platform (or microcontroller), as well as
with the attached sensors, through calls to MRAA APIs.
Which Android runs which Linux kernel? https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel
Android Version |API Level |Linux Version in AOSP |Header Version
----------------------------------------------------------------------------------------
4.4 Kit Kat |19, 20 |(3.10) |2.6.18
5.x Lollipop |21, 22 |(3.16.1) |3.14.0
6.0 Marshmallow |23 |(3.18.10) |3.18.10
sysfs is dead! long live libgpiod! (libgpiod for linux & CircuitPython)
This is basically code that will replace our Python DHT driver, and
has the benefit of being forward compatible with any other Linux board
that runs a 4.8+ kernel. We’ll slowly be replacing other CircuitPython
code to use libgpiod, so that we can have broad support for
CircuitPython on a Raspberry Pi, BeagleBone or Onion.io.
There’s not a lot of libgpiod code out there, and libgpiod doesn’t
come stock on Linux distros yet which may be why its taking a little
while to catch on. There’s bindings for C and Python. Here’s a script
that can help you get started by compiling it for you
https://github.com/adafruit/Raspberry-Pi-Installer-Scripts/blob/master/libgpiod.sh
A DragonBoard 410C GPIO library on GitHub https://github.com/IOT-410c/DragonBoard410c_GpioLibrary which is written in Java and uses the "/sys/class/gpio" method of Linux to access GPIO pins. This looks like this is a repository in a set of repositories used for Coursera courses on Internet of things, some of which use the DragonBoard 410C.
The Linux Kernel: Legacy GPIO Interfaces
This provides an overview of GPIO access conventions on Linux.
These calls use the gpio_* naming prefix. No other calls should use
that prefix, or the related _gpio* prefix.
Android Studio and adb
Android Studio is the application for developing Android apps. It can be downloaded for installation from https://developer.android.com/studio/releases
In addition there are the Android Platform Tools which are a separate download. The adb shell application is part of these tools. The tools can be downloaded for installation from SDK Platform Tools release notes. Pick the particular version needed for your operating system (Windows, Linux, MacOS).
Android Things
While Android Things looks helpful, it appears only a couple of boards are actually supported and the DragonBoard 410C is not one of those. And I'm not sure that Android Things would work with Android 5.1 anyway.
https://developer.android.com/things/get-started
However there was a Brillo (now Android Things) port for DragonBoard 410C https://discuss.96boards.org/t/android-things-on-the-dragonboard/1128
Android Developers > Docs > Android Things > Guides > GPIO
In order to open a connection to a GPIO port, you need to know the
unique port name. During the initial stages of development, or when
porting an app to new hardware, it's helpful to discover all the
available port names from PeripheralManager using getGpioList():
Android Things GitHub repositories https://github.com/androidthings/
See also the following stackoverflow posts which have something to say on this subject. See tag [android-things] in stackoverflow as well.
How can a GpioCallback register "false" twice in a row?
Android Things Pin naming convention
PeripheralManagerService throws NoClassDefFoundError
After reviewing a number of alternatives, it appears that the easiest approach for accessing the GPIO pins of the DragonBoard 410C running Android 5.1 was to use the legacy sysfs special device files method.
I'm not sure if this is the only workable solution. Using Android Things as well as using libgpiod both seem to require a more recent Linux kernel than Android 5.1 uses.
I have written an article on CodeProject.com providing details about working up this solution. See Using Windows 10 for Development with DragonBoard 410C and Android.
Which Android runs which Linux kernel? https://android.stackexchange.com/questions/51651/which-android-runs-which-linux-kernel
Android Version |API Level |Linux Version in AOSP |Header Version
----------------------------------------------------------------------------------------
4.4 Kit Kat |19, 20 |(3.10) |2.6.18
5.x Lollipop |21, 22 |(3.16.1) |3.14.0
6.0 Marshmallow |23 |(3.18.10) |3.18.10
This method also seems to be the easiest in that the library used is written in Kotlin as well.
Using the legacy sysfs special device GPIO interface
See this StackOverFlow post about Linux pseudo files and special device files and the legacy sysfs interface for GPIO pins, what is the /sys/class/gpio/export and `/sys/class/gpio/unexport mechanism and what is the underlying sysfs functionality? .
I found a simple GPIO library written in Java that provided necessary source code. Android Studio has a tool that converted the Java to Kotlin which I included into my project. The source code is below in files Gpio.kt and GpioProcessor.kt.
However in order for this to work I had to make a change to my DragonBoard startup scripts in order to make sure that the necessary special device files were created and available with the proper permissions allowing a user program to manipulate the GPIO pins.
The following procedure is from the Coursera class Internet of Things: Sensing and Actuation from Devices, Lesson 5a: Access GPIO through programs (Android) video #2, Modify boot script. The procedure was to:
use adb to pull a copy of /etc/init.qcom.post_boot.sh from the DragonBoard to my PC
use Notepad to modify the shell script to create the special device files
use adb to push the modified copy back to the Dragonboard
use adb to reboot the DragonBoard
The additional shell code to add to the bottom of /etc/init.qcom.post_boot.sh is as follows. However these special device files are for Android 5.1 only. Linux uses different GPIO pin names.
set -A pins 938 915 1017 926 937 930 914 971 901 936 935
for i in 0 1 2 3 4 5 6 7 8 9 10
do
echo ${pins[i]} > /sys/class/gpio/export;
chmod 777 /sys/class/gpio/gpio${pins[i]};
chmod 777 /sys/class/gpio/gpio${pins[i]}/value;
chmod 777 /sys/class/gpio/gpio${pins[i]}/direction;
done
A Note on sysfs device attributes
Here is some documentation on the GPIO Sysfs Inferface for Userspace from kernel.org. In addition to the two attributes that I use, direction and value, there are several others such as edge and active_low.
“direction” … reads as either “in” or “out”. This value may normally
be written. Writing as “out” defaults to initializing the value as
low. To ensure glitch free operation, values “low” and “high” may be
written to configure the GPIO as an output with that initial value.
Note that this attribute will not exist if the kernel doesn’t support
changing the direction of a GPIO, or it was exported by kernel code
that didn’t explicitly allow userspace to reconfigure this GPIO’s
direction.
“value” … reads as either 0 (low) or 1 (high). If the GPIO is
configured as an output, this value may be written; any nonzero value
is treated as high.
If the pin can be configured as interrupt-generating interrupt and if
it has been configured to generate interrupts (see the description of
“edge”), you can poll(2) on that file and poll(2) will return whenever
the interrupt was triggered. If you use poll(2), set the events
POLLPRI and POLLERR. If you use select(2), set the file descriptor in
exceptfds. After poll(2) returns, either lseek(2) to the beginning of
the sysfs file and read the new value or close the file and re-open it
to read the value.
“edge” … reads as either “none”, “rising”, “falling”, or “both”. Write
these strings to select the signal edge(s) that will make poll(2) on
the “value” file return.
This file exists only if the pin can be configured as an interrupt
generating input pin.
“active_low” … reads as either 0 (false) or 1 (true). Write any
nonzero value to invert the value attribute both for reading and
writing. Existing and subsequent poll(2) support configuration via the
edge attribute for “rising” and “falling” edges will follow this
setting.
Kotlin source code for using sysfs
The complete testing application I am using to explore this topic of using the DragonBoard 410C with Android is in my GitHub repository, https://github.com/RichardChambers/dragonboard_410c
Source for file Gpio.kt
package com.example.myapplication
import java.io.*
/**
* Created by Ara on 7/21/15.
* From https://www.instructables.com/id/DragonBoard-How-to-Access-GPIOs-Using-Java/
* Java source from the article was converted to Kotlin using Android Studio.
*
* See as well https://github.com/IOT-410c/DragonBoard410c_GpioLibrary
*
*/
class Gpio(pin: Int) {
private val pin: Int
/*
* The GPIO pins are represented by folders in the Linux file system
* within the folder /sys/class/gpio. Each pin is represented by a folder
* whose name is the prefix "gpio" followed by the pin number.
* Within the folder representing the pin are two files, "value" used to
* set or get the value of the pin and "direction" used to set or get
* the direction of the pin.
*
* This function creates the path to the Linux file which represents a particular
* GPIO pin function, "value" or "direction".
*/
private fun MakeFileName(pin: Int, op: String): String {
return "/sys/class/gpio/gpio$pin$op"
}
/*
* Get or set the current direction of a pin.
* A pin may be either an Input pin or an Output pin.
*/
var direction: String
get() {
println("Getting Direction")
var line = ""
try {
val br = BufferedReader(FileReader(MakeFileName(pin, "/direction")))
line = br.readLine()
br.close()
} catch (e: Exception) {
println("Error: " + e.message)
}
return line
}
private set(direction) {
println("Setting Direction")
try {
val out = BufferedWriter(FileWriter(MakeFileName(pin, "/direction"), false))
out.write(direction)
out.close()
} catch (e: IOException) {
println("Error: " + e.message)
}
}
/**
* Get or Set pin value.
* #param value Value of pin.
* 0 -> Low Level.
* 1 -> High Level
*/
var value: Int
get() {
println("Getting Value")
var line = ""
try {
val br = BufferedReader(FileReader(MakeFileName(pin, "/value")))
line = br.readLine()
br.close()
} catch (e: Exception) {
println("Error: " + e.message)
}
return line.toInt()
}
private set(value) {
println("Setting Value")
try {
val out = BufferedWriter(FileWriter(MakeFileName(pin, "/value"), false))
out.write(Integer.toString(value))
out.close()
} catch (e: IOException) {
println("Error: " + e.message)
}
}
/**
* Set pin as high.
*/
fun pinHigh() {
value = HIGH
}
/**
* Set pin as low.
*/
fun pinLow() {
value = LOW
}
/**
* Set pin as output.
*/
fun pinOut() {
direction = "out"
}
/**
* Set pin as input.
* #param pin - Desirable pin.
*/
fun pinIn() {
direction = "in"
}
fun exportPin() {
println("Exporting Ping")
try {
val out = BufferedWriter(FileWriter("$PATH/export", false))
out.write(pin.toString())
out.close()
} catch (e: IOException) {
println("Error: " + e.message)
}
}
/**
* Disable access to GPIO.
* #param pin GPIO pin to disable access.
*/
fun unexportPin() {
println("unExporting Ping")
try {
val out = BufferedWriter(FileWriter("$PATH/unexport", false))
out.write(pin.toString())
out.close()
} catch (e: IOException) {
println("Error: " + e.message)
}
}
companion object {
const val HIGH = 1
const val LOW = 0
private const val PATH = "/sys/class/gpio"
}
/**
* Set desirable pin for the GPIO class.
*/
init {
println("Initializing pin $pin")
this.pin = pin
}
}
Source for GpioProcessor.kt
package com.example.myapplication
import java.io.BufferedWriter
import java.io.FileWriter
import java.io.IOException
import java.util.*
/**
* Created by Ara on 7/21/15.
* From https://www.instructables.com/id/DragonBoard-How-to-Access-GPIOs-Using-Java/
* Java source from the article was converted to Kotlin using Android Studio.
*
* See as well https://github.com/IOT-410c/DragonBoard410c_GpioLibrary
*
* Simple example main()
*
* public class Main {
*
* public static void main(String[] args) {
* int count = 0;
* int buttonValue = 0;
*
* GpioProcessor gpioProcessor = new GpioProcessor();
*
* // Get reference of GPIO27 and GPIO29.
*
* Gpio gpioPin27 = gpioProcessor.getPin27();
* Gpio gpioPin29 = gpioProcessor.getPin29();
*
* // Set GPIO27 as output.Set GPIO29 as input.
* gpioPin27.pinOut();
* gpioPin29.pinIn();
*
* while(count<20){
* count++;
* // Read value of GPIO29.
* buttonValue=gpioPin29.getValue();
*
* if(buttonValue == 0){
* // Set GPIO27 as low level.
* gpioPin27.pinLow();
* } else{
* // Set GPIO27 as high level.
* gpioPin27.pinHigh();
* }
*
* try {
* Thread.sleep(1000);
* } catch(InterruptedException e){
* // TODO Auto-generated catch block
* e.printStackTrace();
* }
* }
*
* // Disable access GPIO27 and GPIO29.
* gpioProcessor.closePins();
* }
* }
*/ /*
This class abstracts the use of the gpio pins. This class can be utilized on any linux operating
system that has gpio pins defined in the /sys/class/gpio directory. It is required that the gpio
pins themselves are available for access by the user of this application, and may require a
change of permissions.
*/
class GpioProcessor {
private val PATH = "/sys/class/gpio"
private val pins: MutableList<Int> = ArrayList()
// mapping of physical pin number to GPIO file number.
// the mapping varies depending on the operating system
private val androidPin23 = 938
private val androidPin24 = 914
private val androidPin25 = 915
private val androidPin26 = 971
private val androidPin27 = 1017
private val androidPin28 = 901 // GPIO pin borrowed from MPP. supports PWM. support analog I/O.
private val androidPin29 = 926 // (input only)
private val androidPin30 = 927
private val androidPin31 = 937
private val androidPin32 = 936
private val androidPin33 = 930
private val androidPin34 = 935
private val linuxPin23 = 36
private val linuxPin24 = 12
private val linuxPin25 = 13
private val linuxPin26 = 69
private val linuxPin27 = 115
private val linuxPin28 = 4 // GPIO pin borrowed from MPP. supports PWM. support analog I/O.
private val linuxPin29 = 24 // (input only)
private val linuxPin30 = 25
private val linuxPin31 = 35
private val linuxPin32 = 34
private val linuxPin33 = 28
private val linuxPin34 = 33
private val physicalPin23 = androidPin23
private val physicalPin24 = androidPin24
private val physicalPin25 = androidPin25
private val physicalPin26 = androidPin26
private val physicalPin27 = androidPin27
private val physicalPin28 = androidPin28 // GPIO pin borrowed from MPP. supports PWM. support analog I/O.
private val physicalPin29 = androidPin29 // (input only)
private val physicalPin30 = androidPin30
private val physicalPin31 = androidPin31
private val physicalPin32 = androidPin32
private val physicalPin33 = androidPin33
private val physicalPin34 = androidPin34
/**
* Get function of specific pin.
* #param pin Desirable pin.
*/
fun getPin(pin: Int): Gpio {
exportPin(pin)
pins.add(pin)
return Gpio(pin)
}
/**
* Get pin 23;
* #returns {Gpio}
*/
val pin23: Gpio
get() = getPin(physicalPin23)
/**
* Get pin 24.
* #returns {Gpio}
*/
val pin24: Gpio
get() = getPin(physicalPin24)
/**
* Get pin 25.
* #returns {Gpio}
*/
val pin25: Gpio
get() = getPin(physicalPin25)
/**
* Get pin 26.
* #returns {Gpio}
*/
val pin26: Gpio
get() = getPin(physicalPin26)
/**
* Get pin 27.
* #returns {Gpio}
*/
val pin27: Gpio
get() = getPin(physicalPin27)
/**
* Get pin 28.
* #returns {Gpio}
*/
val pin28: Gpio
get() = getPin(physicalPin28)
/**
* Get pin 29.
* #returns {Gpio}
*/
val pin29: Gpio
get() = getPin(physicalPin29)
/**
* Get pin 30.
* #returns {Gpio}
*/
val pin30: Gpio
get() = getPin(physicalPin30)
/**
* Get pin 31.
* #returns {Gpio}
*/
val pin31: Gpio
get() = getPin(physicalPin31)
/**
* Get pin 32.
* #returns {Gpio}
*/
val pin32: Gpio
get() = getPin(physicalPin32)
/**
* Get pin 33.
* #returns {Gpio}
*/
val pin33: Gpio
get() = getPin(physicalPin33)
/**
* Get pin 34.
* #returns {Gpio}
*/
val pin34: Gpio
get() = getPin(physicalPin34)
/**
* Get all GPIO's pins.
* #return List of pins.
*/
val allPins: Array<Gpio?>
get() {
val allPins = arrayOfNulls<Gpio>(12) // android linux
allPins[0] = pin23 // GPIO 938 GPIO 36
allPins[1] = pin24 // GPIO 914 GPIO 12
allPins[2] = pin25 // GPIO 915 GPIO 13
allPins[3] = pin26 // GPIO 971 GPIO 69
allPins[4] = pin27 // GPIO 1017 GPIO 115
allPins[5] = pin28 // Reserved
allPins[6] = pin29 // GPIO 926 GPIO 24 (input only)
allPins[7] = pin30 // GPIO 927 GPIO 25
allPins[8] = pin31 // GPIO 937 GPIO 35
allPins[9] = pin32 // GPIO 936 GPIO 34
allPins[10] = pin33 // GPIO 930 GPIO 28
allPins[11] = pin34 // GPIO 935 GPIO 33
return allPins
}
/**
* Enable access to GPIO.
* #param pin GPIO pin to access.
*/
private fun exportPin(pin: Int) {
println("Exporting Ping")
try {
val out = BufferedWriter(FileWriter("$PATH/export", false))
out.write(pin.toString())
out.close()
} catch (e: IOException) {
println("Error: " + e.message)
}
}
/**
* Disable access to GPIO.
* #param pin GPIO pin to disable access.
*/
private fun unexportPin(pin: Int) {
println("unExporting Ping")
try {
val out = BufferedWriter(FileWriter("$PATH/unexport", false))
out.write(pin.toString())
out.close()
} catch (e: IOException) {
println("Error: " + e.message)
}
}
fun closePins() {
for (pin in pins) {
unexportPin(pin)
}
pins.clear()
}
companion object {
const val TAG = "GpioProcessor"
}
}
Example source using the GpioProcessor class
I used the GPIO sysfs interface library in an Android app within a fragment by linking a button press to a listener. I have two buttons, one to turn the LED on by driving a pin high and a second to turn the LED off by driving a pin low.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<Button>(R.id.button_second).setOnClickListener {
findNavController().navigate(R.id.action_SecondFragment_to_FirstFragment)
}
val txtScroll = view.findViewById(R.id.LedStatus) as TextView
// find the button whose id is button_Location and then set an listener for
// any clicks on that button. In the following listener we are going to have
// the "Location" button, defined in the file fragment_first.xml, generate a
// list of the GPS service providers by creatinga LocationManager object to
// generate a list.
val gpioProcessor_x = GpioProcessor()
// Get reference of GPIO23.
val gpioPin23_x = gpioProcessor_x.pin23
gpioPin23_x.exportPin()
view.findViewById<Button>(R.id.button_led_off).setOnClickListener {
val gpioProcessor = GpioProcessor()
// Get reference of GPIO27.
val gpioPin23 = gpioProcessor.pin23
// Set GPIO23 as output.
gpioPin23.pinOut()
gpioPin23.pinLow() // drive pin low to turn off LED.
txtScroll.append("LED Off\n")
}
view.findViewById<Button>(R.id.button_led_on).setOnClickListener {
val gpioProcessor = GpioProcessor()
// Get reference of GPIO27.
val gpioPin23 = gpioProcessor.pin23
// Set GPIO23 as output.
gpioPin23.pinOut()
gpioPin23.pinHigh() // drive pin high to turn on LED
txtScroll.append("LED On\n")
}
}
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
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.
I want to show an animated GIF image in an android application like the image below. I have tried the webview but no success. How to show the animated gif in the application?
You can also use this lib to easily support a gifDrawable.
Just use GifImageView instead of normal ImageView:
<pl.droidsonroids.gif.GifImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/your_anim"/>
and locate your gif-file under the src attr. That's all!
You can use Glide :
ImageView imageView = (ImageView) findViewById(R.id.imageView);
GlideDrawableImageViewTarget imageViewTarget = new GlideDrawableImageViewTarget(imageView);
Glide.with(this).load(R.raw.sample_gif).into(imageViewTarget);
After long Google search, I knew that there is no native support for the GIF images. There are no proper solutions for showing the animated gif in application. You can view This solution
to make the animated gif play in a layout.
I tried so many library to use Animated gif .
But every library is lagging and crushing .
But now , after one or two day research i got a idea to use animated gif and the performance is very good no lagging no crushing.
Solution is that use Glide
Follow the below step .
in xml file.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#62b849"
tools:context=".MainActivity">
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:id="#+id/splace_image_view"
android:layout_centerInParent="true"/>
</RelativeLayout>
and in your java file
ImageView imageView = findViewById(R.id.splace_image_view);
Glide
.with(this)
.load(R.drawable.football)
.into(imageView);
Best and easiest solution to display GIF image in Android
and it will work perfectly:
Open build.gradle (Module: app)
put in dependencies:
compile 'pl.droidsonroids.gif:android-gif-drawable:1.1.+'
Open layout folder and put this code where you want to display GIF image:
e-g activity_main.xml
<pl.droidsonroids.gif.GifImageView
android:layout_width="150dp"
android:layout_height="wrap_content"
android:src="#drawable/your_gif_file_name"/>
android:src="#drawable/your_gif_file_name", Replace 'your_gif_file_name' with your desired gif image file
You don't need any library, simply use this code:
Step 1:
Create a file named GIFView.java
package com.thigale.testproject;
/**
* Created by Thigale Sameer on 11-12-16.
*/
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Movie;
import android.util.AttributeSet;
import android.view.View;
import java.io.InputStream;
public class GifView extends View {
public Movie mMovie;
public long movieStart;
private int gifId;
public GifView(Context context) {
super(context);
}
public GifView(Context context, AttributeSet attrs) {
super(context, attrs);
initializeView(attrs.getAttributeResourceValue("http://schemas.android.com/apk/res-auto", "src", 0));
}
public GifView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initializeView(attrs.getAttributeResourceValue("http://schemas.android.com/apk/res-auto", "src", 0));
}
private void initializeView(final int id) {
InputStream is = getContext().getResources().openRawResource(id);
mMovie = Movie.decodeStream(is);
this.gifId = id;
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
super.onDraw(canvas);
long now = android.os.SystemClock.uptimeMillis();
if (movieStart == 0) {
movieStart = now;
}
if (mMovie != null) {
int relTime = (int) ((now - movieStart) % mMovie.duration());
mMovie.setTime(relTime);
mMovie.draw(canvas, getWidth() - mMovie.width(), getHeight() - mMovie.height());
this.invalidate();
}
}
public void setGIFResource(int resId) {
this.gifId = resId;
initializeView(this.gifId);
}
public int getGIFResource() {
return this.gifId;
}
}
Step 2:
Add following lines in res/attrs.xml
<declare-styleable name="GIFView">
<attr name="src" format="reference" />
</declare-styleable>
Step 3:
Add this line your AndroidManifest.xml in specific activity
android:hardwareAccelerated="false"
Step 4:
Create this view in your XML:
<com.thigale.testproject.GifView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
components:src="#drawable/loading" />
Step 5
In the parent activity where you created the view, add the following lines:
xmlns:components="http://schemas.android.com/apk/res-auto"
I have also tried to do the same but Android doesn't show gif images with Animation. If you want to achieve the same then you have take few frames of your animated Image and then use frame by frame animation.
You Can have reference in the below link.
http://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html
Try this way :
Movie movie,movie1;
InputStream is=null,is1=null;
long moviestart;
long moviestart1;
public GIFView(Context context) {
super(context);
is=context.getResources().openRawResource(R.drawable.hxps);
is1=context.getResources().openRawResource(R.drawable.cartoon);
movie=Movie.decodeStream(is);
movie1=Movie.decodeStream(is1);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFFCCCCCC);
super.onDraw(canvas);
long now=android.os.SystemClock.uptimeMillis();
System.out.println("now="+now);
if (moviestart == 0) { // first time
moviestart = now;
}
if(moviestart1==0)
{
moviestart1=now;
}
System.out.println("\tmoviestart="+moviestart);
int relTime = (int)((now - moviestart) % movie.duration()) ;
int relTime1=(int)((now - moviestart1)% movie1.duration());
System.out.println("time="+relTime+"\treltime="+movie.duration());
movie.setTime(relTime);
movie1.setTime(relTime1);
movie.draw(canvas,0,0);
movie1.draw(canvas,10,300);
this.invalidate();
}
You may have a try on this lib GifImageView. It's very simple and easy to use. The following sample code is from README of this project.
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gifView = new GifImageView(context);
gifView.setBytes(bitmapData);
setContentView(gifView);
}
#Override
protected void onStart() {
super.onStart();
gifView.startAnimation();
}
#Override
protected void onStop() {
super.onStop();
gifView.stopAnimation();
}
You could just add a webview and show a gif image in that, Like:
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient());
webView.loadUrl("https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Rotating_earth_(large).gif/200px-Rotating_earth_(large).gif");
This will show the gif image in your app.
Hope it helps! Good Luck!
update:
There is an up-to-date version on android arsenal and in the GitHub page of GIFView.
This is something small I did when someone asked me to help him with showing gifs.
Most of the things I found online were third-party libraries and solutions which used the UI Thread for processing the gif which didn't go so well on my phone so I decided to do it myself with the help of android's Movie API.
I deliberately made it extend ImageView so we can use attributes like scaleType.
This supports retrieving gif from url or from the assets directory. I documented everything.
How to use it:
Simple example of using it in a xml layout file:
<[package].GIFView xmlns:gif_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/gif_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="center"
gif_view:gif_src="asset:gif1" />
The code:
GIF.java:
/**
* Class that represents a gif instance.
*/
public class GIF {
private static final Bitmap.Config DEF_VAL_CONFIG = Bitmap.Config.RGB_565;
private static final int DEF_VAL_DELAY_IN_MILLIS = 33;
// the gif's frames are stored in a movie instance
private Movie movie;
// the canvas of this gif
private Canvas canvas;
// the bitmap of this gif
private Bitmap bitmap;
// the start time of the gif
private long gifStartTime;
// the executor of the gif's thread
private ScheduledExecutorService executor;
// the main runnable of the gif
private Runnable mainRunnable;
// delay in millis between frames
private int delayInMillis;
private OnFrameReadyListener onFrameReadyListener;
private Handler listenerHandler;
private Runnable listenerRunnable;
/**
* Creates Gif instance based on the passed InputStream.
*
* #param in the InputStream
* #throws InputStreamIsNull if in is null
* #throws InputStreamIsEmptyOrUnavailableException if in is empty or unavailable
*/
public GIF(InputStream in) {
this(in, DEF_VAL_CONFIG);
}
/**
* Creates Gif instance based on the passed InputStream and the config.
*
* #param in the InputStream
* #param config the Config
* #throws NullPointerException if config is null
* #throws InputStreamIsNull if in is null
* #throws InputStreamIsEmptyOrUnavailableException if in is empty or unavailable
*/
public GIF(InputStream in, Bitmap.Config config) {
if (in == null)
throw new InputStreamIsNull("the input stream is null");
this.movie = Movie.decodeStream(in);
if (movie == null)
throw new InputStreamIsEmptyOrUnavailableException("the input steam is empty or unavailable");
this.bitmap = Bitmap.createBitmap(movie.width(), movie.height(), config);
// associates the canvas with the bitmap
this.canvas = new Canvas(bitmap);
this.mainRunnable = new Runnable() {
#Override
public void run() {
draw();
invokeListener();
}
};
setDelayInMillis(DEF_VAL_DELAY_IN_MILLIS);
}
/**
* Register a callback to be invoked when the gif changed a frame.
* Invokes methods from a special thread.
*
* #param onFrameReadyListener the listener to attach
*/
public void setOnFrameReadyListener(OnFrameReadyListener onFrameReadyListener) {
setOnFrameReadyListener(onFrameReadyListener, null);
}
/**
* Register a callback to be invoked when the gif changed a frame.
* Invokes methods from the specified handler.
*
* #param onFrameReadyListener the listener to attach
* #param handler the handler
*/
public void setOnFrameReadyListener(OnFrameReadyListener onFrameReadyListener, Handler handler) {
this.onFrameReadyListener = onFrameReadyListener;
listenerHandler = handler;
if (listenerHandler != null)
listenerRunnable = new Runnable() {
#Override
public void run() {
GIF.this.onFrameReadyListener.onFrameReady(bitmap);
}
};
else
listenerRunnable = null;
}
/**
* Sets the delay in millis between every calculation of the next frame to be set.
*
* #param delayInMillis the delay in millis
* #throws IllegalArgumentException if delayInMillis is non-positive
*/
public void setDelayInMillis(int delayInMillis) {
if (delayInMillis <= 0)
throw new IllegalArgumentException("delayInMillis must be positive");
this.delayInMillis = delayInMillis;
}
/**
* Starts the gif.
* If the gif is already running does nothing.
*/
public void startGif() {
if (executor != null)
return;
executor = Executors.newSingleThreadScheduledExecutor();
final int INITIAL_DELAY = 0;
executor.scheduleWithFixedDelay(mainRunnable, INITIAL_DELAY,
delayInMillis, TimeUnit.MILLISECONDS);
}
/**
* Stops the gif.
* If the gif is not running does nothing.
*/
public void stopGif() {
if (executor == null)
return;
executor.shutdown();
// waits until the thread is finished
while (true) {
try {
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
break;
} catch (InterruptedException ignored) {
}
}
executor = null;
}
// calculates the frame and draws it to the bitmap through the canvas
private void draw() {
// if gifStartTime == 0 inits it for the first time
if (gifStartTime == 0)
gifStartTime = SystemClock.uptimeMillis();
long timeElapsed = SystemClock.uptimeMillis() - gifStartTime;
int timeInGif = (int) (timeElapsed % movie.duration());
movie.setTime(timeInGif);
movie.draw(canvas, 0, 0);
}
// invokes the listener
private void invokeListener() {
if (onFrameReadyListener == null)
return;
// if handler was given invokes from it, otherwise invokes from this thread
if (listenerHandler != null)
listenerHandler.post(listenerRunnable);
else
onFrameReadyListener.onFrameReady(bitmap);
}
/**
* Interface definition for a callback to be invoked when the gif changed a frame.
*/
public interface OnFrameReadyListener {
/**
* Called when the gif changed a frame.
* <p>
* Note: If a handler was given with the listener this method
* invokes from the handler, otherwise this method
* invokes from a special thread.
* <p>
* Note: This bitmap is mutable and used by the gif instance
* thus it is not recommended to mutate it.
*
* #param bitmap the new bitmap of the gif
*/
void onFrameReady(Bitmap bitmap);
}
/**
* Definition of a runtime exception class to throw when the inputStream is null.
*/
public static class InputStreamIsNull extends NullPointerException {
/**
* Creates a new instance.
*/
public InputStreamIsNull() {
super();
}
/**
* * Creates a new instance with a message.
*
* #param message the message
*/
public InputStreamIsNull(String message) {
super(message);
}
}
/**
* Definition of a runtime exception class to throw when the inputStream is empty or unavailable.
*/
public static class InputStreamIsEmptyOrUnavailableException extends RuntimeException {
/**
* Creates a new instance.
*/
public InputStreamIsEmptyOrUnavailableException() {
super();
}
/**
* * Creates a new instance with a message.
*
* #param message the message
*/
public InputStreamIsEmptyOrUnavailableException(String message) {
super(message);
}
}
}
GIFView.java:
/**
* A view that can show gifs.
* <p>
* XML Attributes:
* <p>
* gif_src:
* A string that represents the gif's source.
* <p>
* - If you want to get the gif from a url
* concatenate the string "url:" with the full url.
* <p>
* - if you want to get the gif from the assets directory
* concatenate the string "asset:" with the full path of the gif
* within the assets directory. You can exclude the .gif extension.
* <p>
* for example if you have a gif in the path "assets/ex_dir/ex_gif.gif"
* the string should be: "asset:ex_dir/ex_gif"
* <p>
* delay_in_millis:
* A positive integer that represents how many milliseconds
* should pass between every calculation of the next frame to be set.
*/
public class GIFView extends ImageView {
public static final String RESOURCE_PREFIX_URL = "url:";
public static final String RESOURCE_PREFIX_ASSET = "asset:";
private static final int DEF_VAL_DELAY_IN_MILLIS = 33;
// the gif instance
private GIF gif;
// keeps track if the view is in the middle of setting the gif
private boolean settingGif;
private GIF.OnFrameReadyListener gifOnFrameReadyListener;
private OnSettingGifListener onSettingGifListener;
// delay in millis between frames
private int delayInMillis;
/**
* Creates a new instance in the passed context.
*
* #param context the context
*/
public GIFView(Context context) {
super(context);
init(null);
}
/**
* Creates a new instance in the passed context with the specified set of attributes.
*
* #param context the context
* #param attrs the attributes
*/
public GIFView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
// inits the view
private void init(AttributeSet attrs) {
this.gifOnFrameReadyListener = new GIF.OnFrameReadyListener() {
#Override
public void onFrameReady(Bitmap bitmap) {
setImageBitmap(bitmap);
}
};
setDelayInMillis(DEF_VAL_DELAY_IN_MILLIS);
if (attrs != null)
initAttrs(attrs);
}
// inits the view with the specified attributes
private void initAttrs(AttributeSet attrs) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(
attrs, R.styleable.gif_view,
0, 0);
try {
// gets and sets the delay in millis.
int delayInMillis = typedArray.getInt(R.styleable.gif_view_delay_in_millis,
DEF_VAL_DELAY_IN_MILLIS);
if (delayInMillis != DEF_VAL_DELAY_IN_MILLIS)
setDelayInMillis(delayInMillis);
// gets the source of the gif and sets it
String string = typedArray.getString(R.styleable.gif_view_gif_src);
if (string != null)
setGifResource(typedArray.getString(R.styleable.gif_view_gif_src));
} finally {
typedArray.recycle();
}
}
/**
* Register callbacks to be invoked when the view finished setting a gif.
*
* #param onSettingGifListener the listener to attach
*/
public void setOnSettingGifListener(OnSettingGifListener onSettingGifListener) {
this.onSettingGifListener = onSettingGifListener;
}
/**
* Sets the delay in millis between every calculation of the next frame to be set.
*
* #param delayInMillis the delay in millis
* #throws IllegalArgumentException if delayInMillis is non-positive
*/
public void setDelayInMillis(int delayInMillis) {
if (delayInMillis <= 0)
throw new IllegalArgumentException("delayInMillis must be positive");
this.delayInMillis = delayInMillis;
if (gif != null)
gif.setDelayInMillis(delayInMillis);
}
/**
* Returns true if the view is in the process of setting the gif, false otherwise.
*
* #return true if the view is in the process of setting the gif, false otherwise
*/
public boolean isSettingGif() {
return settingGif;
}
/**
* Sets the gif of this view and starts it.
* <p>
* Note that every exception while setting the gif is only sent to the
* OnSettingGifListener instance attached to this view.
* <p>
* If the view has already begun setting another gif, does nothing.
* You can query this state with isSettingGif().
* <p>
* The string passed must be in the following format:
* <p>
* - If you want to get the gif from a url
* concatenate the string "url:" with the full url.
* <p>
* - if you want to get the gif from the assets directory
* concatenate the string "asset:" with the full path of the gif
* within the assets directory. You can exclude the .gif extension.
* <p>
* You can use the Constants:
* <p>
* GIFView.RESOURCE_PREFIX_URL = "url:"
* GIFView.RESOURCE_PREFIX_ASSET = "asset:"
* <p>
* for example if you have a gif in the path "assets/ex_dir/ex_gif.gif"
* invoke the method like this: setGifResource(GIFView.RESOURCE_PREFIX_ASSET + "ex_dir/ex_gif");
*
* #param string the string
* #throws IllegalArgumentException if the string format is invalid
*/
public void setGifResource(String string) {
if (settingGif)
return;
// stops the gif if it is running
if (gif != null)
gif.stopGif();
// defines some finals for readability
final int URL_START_INDEX = RESOURCE_PREFIX_URL.length();
final int ASSET_START_INDEX = RESOURCE_PREFIX_ASSET.length();
final String GIF_EXTENSION = ".gif";
if (string.startsWith(RESOURCE_PREFIX_URL)) {
// notifies setting gif has started
settingGif = true;
// gets the url
String url = string.substring(URL_START_INDEX);
new AsyncSettingOfGif() {
#Override
protected InputStream getGifInputStream(String url) throws Exception {
// gets the input stream from the url
return (InputStream) new URL(url).getContent();
}
}.execute(url);
} else if (string.startsWith(RESOURCE_PREFIX_ASSET)) {
// notifies setting gif has started
settingGif = true;
// gets the asset path
String assetPath = string.substring(ASSET_START_INDEX)
.replaceAll("[\\\\/]", File.separator); // replacing file separators
if (!assetPath.endsWith(GIF_EXTENSION))
assetPath += GIF_EXTENSION;
new AsyncSettingOfGif() {
#Override
protected InputStream getGifInputStream(String assetPath) throws Exception {
// gets the input stream from the assets directory
return GIFView.this.getResources().getAssets().open(assetPath);
}
}.execute(assetPath);
// if string format is invalid
} else {
throw new IllegalArgumentException("string format is invalid");
}
}
/**
* Called when the view finished to set the gif
* or an exception has occurred.
* If there are no exceptions e is null.
* <p>
* Note that the gif can be initialized properly
* and one or more exceptions can be caught in the way.
*
* #param e the Exception
*/
protected void onFinishSettingGif(Exception e) {
// notifies setting the gif has finished
settingGif = false;
if (gif != null)
onSuccess();
else
onFailure(e);
}
// on finish setting the gif
private void onSuccess() {
gif.setOnFrameReadyListener(gifOnFrameReadyListener, getHandler());
gif.setDelayInMillis(delayInMillis);
startGif();
if (onSettingGifListener != null)
onSettingGifListener.onSuccess(this);
}
// when an exception has occurred while trying to set the gif
private void onFailure(Exception e) {
if (onSettingGifListener != null)
onSettingGifListener.onFailure(this, e);
}
/**
* Starts the gif.
* If the gif is already running does nothing.
*
* #throws IllegalStateException if the gif has not been initialized yet
*/
public void startGif() {
if (gif == null || settingGif)
throw new IllegalStateException("the gif has not been initialized yet");
gif.startGif();
}
/**
* Stops the gif.
* If the gif is not running does nothing.
*
* #throws IllegalStateException if the gif has not been initialized yet
*/
public void stopGif() {
if (gif == null || settingGif)
throw new IllegalStateException("the gif has not been initialized yet");
gif.stopGif();
}
/**
* Interface definition for callbacks to be invoked when setting a gif.
*/
public interface OnSettingGifListener {
/**
* Called when a gif has successfully set.
*
* #param view the GIFView
*/
void onSuccess(GIFView view);
/**
* Called when a gif cannot be set.
*
* #param view the GIFView
* #param e the Exception
*/
void onFailure(GIFView view, Exception e);
}
/**
* Definition of an Exception class to throw when the view cannot initialize the gif.
*/
public static class CannotInitGifException extends Exception {
/**
* Creates a new instance.
*/
public CannotInitGifException() {
super();
}
/**
* * Creates a new instance with a message.
*
* #param message the message
*/
public CannotInitGifException(String message) {
super(message);
}
}
/**
* A sub-class of AsyncTask to easily perform an async task of setting a gif.
* <p>
* The default implementation of AsyncSettingOfGif.doInBackground() is to try and init the gif
* from the input stream returned from AsyncSettingOfGif.getGifInputStream() and notify
* GIFView.onFinishSettingGif() sending to it the exception, if occurred, or null.
* <p>
* Implementations of this class should override AsyncSettingOfGif.getGifInputStream()
* to return the right input stream for the gif based on the string argument.
* The string argument can be, for example, a url to retrieve the input stream from.
*/
protected abstract class AsyncSettingOfGif extends AsyncTask<String, Void, Exception> {
#Override
protected Exception doInBackground(String... string) {
CannotInitGifException exceptionToSend = null;
try (InputStream in = getGifInputStream(string[0])) {
// tries to init the gif
gif = new GIF(in);
} catch (Exception e) {
// prepares the message of the exception
String message = e.getMessage();
if (e instanceof FileNotFoundException)
message = "file not found: " + message;
// prepares the exception to send back
exceptionToSend = new CannotInitGifException(message);
}
return exceptionToSend;
}
/**
* Override this method to return the right input stream for the gif based on the string argument.
* The string argument can be, for example, a url to retrieve the input stream from.
*
* #param string the string
* #return an InputStream of a gif
* #throws Exception if an exception has occurred
*/
protected abstract InputStream getGifInputStream(String string) throws Exception;
#Override
protected void onPostExecute(Exception e) {
onFinishSettingGif(e);
}
}
}
res/values/attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="gif_view">
<attr name="gif_src" format="string" />
<attr name="delay_in_millis" format="integer" />
</declare-styleable>
</resources>