Simulate taking picture in default system camera app - android

im making android voice assistant app...that run service in background for recognizing the voice command .
i want to take picture in default system camera app when the user say's the word "selfie".i already know how to work with voice command but the problem is i cant make the camera app take picture ...
i tried some way but wont helped
1st i tried to simulate android camera key event
Intent intent1 = new Intent("android.intent.action.CAMERA_BUTTON");
intent1.putExtra("android.intent.extra.KEY_EVENT", new KeyEvent(0,
KeyEvent.KEYCODE_CAMERA));
sendOrderedBroadcast(intent1, null);
intent1 = new Intent("android.intent.action.CAMERA_BUTTON");
intent1.putExtra("android.intent.extra.KEY_EVENT", new KeyEvent(1,
KeyEvent.KEYCODE_CAMERA));
sendOrderedBroadcast(intent1, null);
this one open camera but wont take picture in phone's without physical camera key
2nd i tried to inject key event "enter" ... like bluetooth remote shutter ...
KeyEvent eventDown = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER);
KeyEvent eventUp = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER);
dispatchKeyEvent(eventDown);
dispatchKeyEvent(eventUp);
but in this one i faced 2 problem 1st this code cant be use in service 2nd its impossible to inject event to other app since that only system app could do this
now the question is how can i fix this problem?
is it possible or not?
i read some thing on web that appium could do this but its online & i want my app working off line
note that : adding camera permission & inject event permission wont help and i don't want to use camera api because i want to take pic in default system camera app.

Yes, Its possible After 2 days investigation I find the solution.
Requirement : Open system camera app and click pic.
Step 1:
Add Camera permission in manifest file:
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.front"
android:required="false" />
Step 2: Create one service which extends AccessibilityService
<service
android:name=".AccessTest"
android:enabled="true"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="#xml/accessibility_service_config"/>
</service>
Step 3: Start service when you required
Intent mailAccessabilityIntent = new Intent(getApplicationContext(), AccessTest.class);
startService(mailAccessabilityIntent);
Step 4: Add accessibility file.
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackAllMask"
android:accessibilityFlags="flagEnableAccessibilityVolume"
android:canRetrieveWindowContent="true"
android:notificationTimeout="100"
android:packageNames="com.google.android.GoogleCamera"
android:settingsActivity="com.mobiliya.cameraautoclick.MainActivity" />
Step 5: Write service class where you want to handle camera related listener.
public class AccessTest extends AccessibilityService {
private final static String TAG = "Yogesh";
#Override
public void onCreate() {
super.onCreate();
Log.d("Yogesh","I am started");
}
#Override
protected void onServiceConnected() {
super.onServiceConnected();
Log.d(TAG, "onServiceConnected");
}
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
Log.d(TAG, "ACC::onAccessibilityEvent: " + event.getEventType());
//TYPE_WINDOW_STATE_CHANGED == 32
if (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event
.getEventType()) {
AccessibilityNodeInfo nodeInfo = event.getSource();
if (nodeInfo == null) {
return;
}
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
String x = takePictureIntent.resolveActivity(getPackageManager()).getPackageName();
Log.d("Yogesh","Package name " + x);
List<AccessibilityNodeInfo> list1 = nodeInfo.findAccessibilityNodeInfosByText("Switch to front camera");
for (AccessibilityNodeInfo node : list1) {
Log.i(TAG, "ACC::onAccessibilityEvent: click " + node);
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
final List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("Take photo");
final android.os.Handler handler = new android.os.Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
for (AccessibilityNodeInfo node : list) {
Log.i(TAG, "ACC::onAccessibilityEvent: click " + node);
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
handler.postDelayed(this,5000);
}
},10000);
for (AccessibilityNodeInfo node : list) {
Log.i(TAG, "ACC::onAccessibilityEvent: click " + node);
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
Log.d(TAG,"Access " + getAllChildNodeText(nodeInfo).toString());
}
}
private List<CharSequence> getAllChildNodeText(AccessibilityNodeInfo infoCompat) {
List<CharSequence> contents = new ArrayList<>();
if (infoCompat == null)
return contents;
if (infoCompat.getContentDescription() != null) {
contents.add(infoCompat.getContentDescription().toString().isEmpty() ? "unlabelled" : infoCompat.getContentDescription());
} else if (infoCompat.getText() != null) {
contents.add(infoCompat.getText().toString().isEmpty() ? "unlabelled" : infoCompat.getText());
} else {
getTextInChildren(infoCompat, contents);
}
if (infoCompat.isClickable()) {
if (infoCompat.getClassName().toString().contains(Button.class.getSimpleName())) {
if (contents.size() == 0) {
contents.add("Unlabelled button");
} else {
contents.add("button");
}
}
contents.add("Double tap to activate");
}
return contents;
}
private void getTextInChildren(AccessibilityNodeInfo nodeInfoCompat, List<CharSequence> contents) {
if (nodeInfoCompat == null)
return;
if (!nodeInfoCompat.isScrollable()) {
if (nodeInfoCompat.getContentDescription() != null) {
contents.add(nodeInfoCompat.getContentDescription());
} else if (nodeInfoCompat.getText() != null) {
contents.add(nodeInfoCompat.getText());
}
if (nodeInfoCompat.getChildCount() > 0) {
for (int i = 0; i < nodeInfoCompat.getChildCount(); i++) {
if (nodeInfoCompat.getChild(i) != null) {
getTextInChildren(nodeInfoCompat.getChild(i), contents);
}
}
}
}
}
#Override
public void onInterrupt() {
}
}
Here getAllChildNodeText() return all text button which is clickable, Google default application have Take Photo text so for this view you can perform action.
Added handler for capture pic every 10 seconds for more clarification.
If you want to track multiple camera app then remove below line and use Java code for set package more details See -Accessibility Service
android:packageNames="com.google.android.GoogleCamera"
I uploaded working example -> https://github.com/ycrathi/cameraautoclick
Note: In above GitHub repo have multiple unwanted code, which I tried.
This solution is not global for all app. You can find some famous app like google camera and find text and then perform click action package wise.

you can use 3rd-party "fake camera" apps such as:
Image2Camera
Fake Camera by New Horizon Apps
Fake Camera - donate version by Vaclav Balak
alternatively you can use:
ICS emulator - that supports camera
alternatively you can use:
In your AVD advanced settings, you should be able to set front and back cameras to Webcam() or Emulated

Related

Send message via whatsapp programmatically

I'm trying to send messages via Whatsapp programmatically, the code works except the user needs to click the send button. I need the app to do everything (all user interactions). One way to do to it as follows .
Go to Menu Button > Settings > Chats. and check the "Enter is send option"
Here's the code I'm using:
protected void sendwts(){
String smsNumber = "2126123456789"; // E164 format without '+' sign
Intent sendIntent = new Intent(Intent.ACTION_SEND);
// Intent sendIntent = new Intent(Intent.ACTION_SENDTO);
sendIntent.setType("text/plain");
sendIntent.putExtra(Intent.EXTRA_TEXT, "test \n");
sendIntent.putExtra("jid", smsNumber + "#s.whatsapp.net"); //phone number without "+" prefix
sendIntent.setPackage("com.whatsapp");
startActivity(sendIntent);
}
Thank you
You can do that only using the Accessibility API of Android.
The idea is quite simple, you'll actually make Android perform the click on Whatsapp's send button.
So the flow will be:
Send a regular message (with the intent you're currently using) with a suffix at the end of your message content such as "Sent by MY_APP".
Once the text there, your accessibility service will be notified that the EditText of whatsapp is filled.
If the suffix is present on the EditText of whatsapp, Your accessibility service will click on the send button. (this is to avoid performing actions as the user types in naturally a regular message).
Here's an example (which you'll have tweak if you wanna make it more restrictive):
public class WhatsappAccessibilityService extends AccessibilityService {
#Override
public void onAccessibilityEvent (AccessibilityEvent event) {
if (getRootInActiveWindow () == null) {
return;
}
AccessibilityNodeInfoCompat rootInActiveWindow = AccessibilityNodeInfoCompat.wrap (getRootInActiveWindow ());
// Whatsapp Message EditText id
List<AccessibilityNodeInfoCompat> messageNodeList = rootInActiveWindow.findAccessibilityNodeInfosByViewId ("com.whatsapp:id/entry");
if (messageNodeList == null || messageNodeList.isEmpty ()) {
return;
}
// check if the whatsapp message EditText field is filled with text and ending with your suffix (explanation above)
AccessibilityNodeInfoCompat messageField = messageNodeList.get (0);
if (messageField.getText () == null || messageField.getText ().length () == 0
|| !messageField.getText ().toString ().endsWith (getApplicationContext ().getString (R.string.whatsapp_suffix))) { // So your service doesn't process any message, but the ones ending your apps suffix
return;
}
// Whatsapp send button id
List<AccessibilityNodeInfoCompat> sendMessageNodeInfoList = rootInActiveWindow.findAccessibilityNodeInfosByViewId ("com.whatsapp:id/send");
if (sendMessageNodeInfoList == null || sendMessageNodeInfoList.isEmpty ()) {
return;
}
AccessibilityNodeInfoCompat sendMessageButton = sendMessageNodeInfoList.get (0);
if (!sendMessageButton.isVisibleToUser ()) {
return;
}
// Now fire a click on the send button
sendMessageButton.performAction (AccessibilityNodeInfo.ACTION_CLICK);
// Now go back to your app by clicking on the Android back button twice:
// First one to leave the conversation screen
// Second one to leave whatsapp
try {
Thread.sleep (500); // hack for certain devices in which the immediate back click is too fast to handle
performGlobalAction (GLOBAL_ACTION_BACK);
Thread.sleep (500); // same hack as above
} catch (InterruptedException ignored) {}
performGlobalAction (GLOBAL_ACTION_BACK);
}
}
Then create its definition in res -> xml -> whatsapp_service.xml:
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowContentChanged"
android:packageNames="com.whatsapp"
android:accessibilityFeedbackType="feedbackSpoken"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"/>
Then declare it in your manifest:
<service
android:name=".services.WhatsappAccessibilityService"
android:label="Accessibility Service"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<meta-data
android:name="android.accessibilityservice"
android:resource="#xml/whatsapp_service"/>
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
</service>
And last thing, is to check if the accessibility services are enabled for your app or not, and redirect the user to the settings if not:
private boolean isAccessibilityOn (Context context, Class<? extends AccessibilityService> clazz) {
int accessibilityEnabled = 0;
final String service = context.getPackageName () + "/" + clazz.getCanonicalName ();
try {
accessibilityEnabled = Settings.Secure.getInt (context.getApplicationContext ().getContentResolver (), Settings.Secure.ACCESSIBILITY_ENABLED);
} catch (Settings.SettingNotFoundException ignored) { }
TextUtils.SimpleStringSplitter colonSplitter = new TextUtils.SimpleStringSplitter (":");
if (accessibilityEnabled == 1) {
String settingValue = Settings.Secure.getString (context.getApplicationContext ().getContentResolver (), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (settingValue != null) {
colonSplitter.setString (settingValue);
while (colonSplitter.hasNext ()) {
String accessibilityService = colonSplitter.next ();
if (accessibilityService.equalsIgnoreCase (service)) {
return true;
}
}
}
}
return false;
}
which you'll call with:
if (!isAccessibilityOn (context, WhatsappAccessibilityService.class)) {
Intent intent = new Intent (Settings.ACTION_ACCESSIBILITY_SETTINGS);
context.startActivity (intent);
}
This is purely on the technical aspect of the solution.
Now, the ethical question of "should you do that?", I believe the answer is quite clear:
Except if you are targeting people with disabilities (which is the very purpose of the Accessibility API), you should probably NOT do that.

Android Clickable On screen text

I was curious, I seen this app the other day that allowed it to open other apps and set certain functions up for you automatically. I have came to realize that it must be using an on screen click function of some sort, but I can't seem to find any documentation for something like this. For example if we know the on screen text from the other app is "Ready", is there a way to read that text and maybe do something like:
protected void processText(String text)
{
if (text.contains("Ready"))
// click the ready text
}
I have done this using AccessibilityService. It will only work fine on API level >= 16 though.
You need to extend AccessibilityService. For instance this class will get text of USSD responses and dismiss the dialog.
// ....
public class UssdAccessibilityService extends AccessibilityService {
public UssdAccessibilityService() {
}
#TargetApi(16)
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (!"com.android.phone".equalsIgnoreCase((String)event.getPackageName())){
// In this example we are only interested in events comming
// from "com.android.phone" package
event.recycle();
return;
}
String className = (String)event.getClassName();
if (className == null || (!className.contains("AlertDialog") && !className.contains("AlertDialog"))){
// Class is not an USSD dialog
event.recycle();
return;
}
AccessibilityNodeInfo source = event.getSource();
if (source == null) {
// getSource() is annotated #Nullable, so we do this to be
// safe just in case
event.recycle();
return;
}
AccessibilityNodeInfo acceptButton = null;
String ussdText = null;
int childCount = source.getChildCount();
for (int i = 0; i < childCount; i++){
AccessibilityNodeInfo current = source.getChild(i);
if (current == null)
continue;
String currentText = (String)current.getText();
if (current.isClickable()){
// In the case of USSD dialogs, there is only one clickable.
// May be necessary to do more robust search in other scenarios
acceptButton = current;
continue;
}
ussdText = currentText;
current.recycle();
}
if (ussdText!= null) {
if (acceptButton != null)
acceptButton.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
source.recycle();
event.recycle();
}
// ....
}
You must declare the accessibility service in the manifest under <application>
<service
android:name=".UssdAccessibilityService"
android:enabled="true"
android:label="Read USSD codes and dismiss"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="#xml/accessibility_service_config" />
</service>
Under res/xml create accessibility_service_config.xml
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="#string/accessibility_service_description"
android:packageNames="com.android.phone,com.ats.android.activationcodebot"
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged"
android:accessibilityFlags="flagDefault"
android:accessibilityFeedbackType="feedbackSpoken"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
/>
Of course you have to adapt this code to your own needs.
Finally you will have to enable the accessibility service manually on Settings > Accessibility in Android (or ask the user to do it).
Read more ... Developing an Accessibility Service

How to detect that the call which is done from our android device is answered or rejceted?

I am making an application to call multiple numbers.
In that app
When I call to 1 person and if the call is answered by the user then
the loop should be stopped.
But If the call is rejected then the call should be on next number and
loop should be couninue.
My problem is I cant detect whether the call is rejected or answered. when I had search on net some people says it is not possible to detect the call is answered or rejected.
Is it really not possible to detect the call in android If it is possible then how can I do that?
I think you can check outgoing call time of last call in PhoneStateListener class' onCallStateChanged method. Fetch the data if state is idle that is TelephonyManager.CALL_STATE_IDLE.
Something like this:
Cursor mCallCursor = context.getContentResolver().query(android.provider.CallLog.Calls.CONTENT_URI,null,null,null,null);
int duration = mCallCursor.getColumnIndex( CallLog.Calls.DURATION);
while(mCallCursor.moveToFirst())
{
Toast.makeText(context, mCallCursor.getString(duration), Toast.LENGTH_LONG).show();
}
You can find more about that here. I haven't tested the above code. But something like that should work.
You can check if time's 00:00, then call next number of loop. Else you can stop calling.
Hope this helps you.
below is a code of detecting outgoing call by accessibility events -
Add a class which extends AccessibilityService in your projects -
public class CallDetection extends AccessibilityService {
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
acquireLock(this);
Log.d("myaccess","after lock");
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
Log.d("myaccess","in window changed");
AccessibilityNodeInfo info = event.getSource();
if (info != null && info.getText() != null) {
String duration = info.getText().toString();
String zeroSeconds = String.format("%02d:%02d", new Object[]{Integer.valueOf(0), Integer.valueOf(0)});
String firstSecond = String.format("%02d:%02d", new Object[]{Integer.valueOf(0), Integer.valueOf(1)});
Log.d("myaccess","after calculation - "+ zeroSeconds + " --- "+ firstSecond + " --- " + duration);
if (zeroSeconds.equals(duration) || firstSecond.equals(duration)) {
Toast.makeText(getApplicationContext(),"Call answered",Toast.LENGTH_SHORT).show();
// Your Code goes here
}
info.recycle();
}
}
}
#Override
protected void onServiceConnected() {
super.onServiceConnected();
Toast.makeText(this,"Service connected",Toast.LENGTH_SHORT).show();
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.eventTypes = AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
info.notificationTimeout = 0;
info.packageNames = null;
setServiceInfo(info);
}
#Override
public void onInterrupt() {
}
}
But to get the function event.getSource() working you have to specify some of your service configuration through xml, so create a xml folder in your project and add a xml file called serviceconfig.xml (you can give any name you want.
The content of serviceconfig is below -
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="#string/callDetection"
android:accessibilityEventTypes="typeWindowContentChanged"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
/>
You can find more about serviceconfig in Here
Now add your service in you Manifest file like this -
<service android:name=".CallDetection"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:label="#string/callDetection">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="#xml/serviceconfig" />
</service>
And youre done, just run the app and go to Accessibility settings in your phone, you will find an option named as detection (or whatever name you have given as your service description), switch that on to give accesibility permissions for you app.
Now you will see a toast when call is answered.
you can Code any code you want in there, also you can call a callback function in your activity
Most important - Dont call your call window(android dialer window) untill the call is answered, otherwise this will not work.
Note - As android doesn't provide any solution to detect if the call is answered or not, this is the best alternative i have made, hope it works for you.

Catch toast event (from any app) and get toast message

As I understood this is possible, from here
Detecting toast messages
But I am unable to catch any event with code snippet from the link.
MyAccessibilityService.java
package com.test.toasts2;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Notification;
import android.os.Parcelable;
import android.view.accessibility.AccessibilityEvent;
import android.widget.Toast;
public class MyAccessibilityService extends AccessibilityService {
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
System.out.println("event catched");
Toast.makeText(this, "catched " + "!", Toast.LENGTH_SHORT).show();
if(event.getEventType() != AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)
return; // event is not a notification
String sourcePackageName = (String)event.getPackageName();
Parcelable parcelable = event.getParcelableData();
if(parcelable instanceof Notification){
// Statusbar Notification
}
else{
// something else, e.g. a Toast message
String log = "Message: "+event.getText().get(0)+" [Source: "+sourcePackageName+"]";
System.out.println(log);
// write `log` to file...
}
}
#Override
public void onInterrupt() {
// TODO Auto-generated method stub
}
#Override
protected void onServiceConnected() {
// TODO Auto-generated method stub
super.onServiceConnected();
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.feedbackType = AccessibilityServiceInfo.DEFAULT;
setServiceInfo(info);
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.toasts2"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="15" />
<application>
<service android:name=".MyAccessibilityService"
android:label="label">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
</service>
</application>
</manifest>
Seems like this service is simply not started. What I am doing wrong?
Why I am doing this:
I am installing many shortcuts on the stock launcher from my app. I am having the problem that theese shortcuts are placed one over another in one cell (even Sleep 500 did not help). So I am finding a way to install them one by another. But how to know when shortcut was successfully installed? I have found only a message that ics launcher shows to user.
TYPE_NOTIFICATION_STATE_CHANGED generally refers to NotificationManager and icons placed in the status bar. Nonetheless, the below code should help shed some light as to the origin of a Toast message. On Android 4.0.4 ICS a Toast has a class of android.widget.Toast so getClassName should do the trick.
For what its worth, the change seems to have been made in Android 4.0.3 to add and use the following method in Toast.TN
private void trySendAccessibilityEvent() {
AccessibilityManager accessibilityManager =
AccessibilityManager.getInstance(mView.getContext());
if (!accessibilityManager.isEnabled()) {
return;
}
// treat toasts as notifications since they are used to
// announce a transient piece of information to the user
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
event.setClassName(getClass().getName());
event.setPackageName(mView.getContext().getPackageName());
mView.dispatchPopulateAccessibilityEvent(event);
accessibilityManager.sendAccessibilityEvent(event);
}
You can see the Toast class in all versions of Android here.
private final String getEventType(AccessibilityEvent event) {
switch (event.getEventType()) {
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
return "TYPE_NOTIFICATION_STATE_CHANGED";
case AccessibilityEvent.TYPE_VIEW_CLICKED:
return "TYPE_VIEW_CLICKED";
case AccessibilityEvent.TYPE_VIEW_FOCUSED:
return "TYPE_VIEW_FOCUSED";
case AccessibilityEvent.TYPE_VIEW_LONG_CLICKED:
return "TYPE_VIEW_LONG_CLICKED";
case AccessibilityEvent.TYPE_VIEW_SELECTED:
return "TYPE_VIEW_SELECTED";
case AccessibilityEvent.TYPE_VIEW_SCROLLED:
return "TYPE_VIEW_SCROLLED";
case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT:
return "TYPE_VIEW_HOVER_EXIT";
case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
return "TYPE_VIEW_HOVER_ENTER";
case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START:
return "TYPE_TOUCH_EXPLORATION_GESTURE_START";
case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END:
return "TYPE_TOUCH_EXPLORATION_GESTURE_END";
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
return "TYPE_WINDOW_STATE_CHANGED";
case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
return "TYPE_WINDOW_CONTENT_CHANGED";
case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED:
return "TYPE_VIEW_TEXT_SELECTION_CHANGED";
case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
return "TYPE_VIEW_TEXT_CHANGED";
}
return "default";
}
private final String getEventText(AccessibilityEvent event) {
StringBuilder sb = new StringBuilder();
for (CharSequence s : event.getText()) {
sb.append(s);
sb.append('\n');
}
return sb.toString();
}
#Override
public void onAccessibilityEvent(AccessibilityEvent event)
{
Log.v(TAG, String.format(
"onAccessibilityEvent: [type] %s [class] %s [package] %s [time]
%s [fullscreen] %s [text] %s", getEventType(event), event.getClassName(),
event.getPackageName(), event.getEventTime(), Boolean.toString(
event.isFullScreen()), getEventText(event)));
if (android.os.Build.VERSION.SDK_INT >= 14)
Log.v(TAG, "Window ID: " + Integer.toString(event.getWindowId()) + ".");
}
private void setServiceInfo(int feedbackType)
{
final AccessibilityServiceInfo info = new AccessibilityServiceInfo();
// We are interested in all types of accessibility events.
info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
// We want to provide specific type of feedback.
info.feedbackType = feedbackType;
// We want to receive events in a certain interval.
// info.notificationTimeout = EVENT_NOTIFICATION_TIMEOUT_MILLIS;
// We want to receive accessibility events only from certain packages.
// info.packageNames = PACKAGE_NAMES;
setServiceInfo(info);
}
private boolean isInfrastructureInitialized = false;
#Override
public void onServiceConnected()
{
if (isInfrastructureInitialized) return;
// Claim the events with which to listen to.
setServiceInfo(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
// We are in an initialized state now.
isInfrastructureInitialized = true;
}
Source: personal experience.
create an Activity that will build an intent and use it to start your service.
something like this will be the code inside the activity.
Intent i = new Intent(YourActivity.this, MyAccessibilityService.class);
startService(i);
In your manifest make the intent filter for the activity have the MAIN and LAUNCHER in it so that it will the Activity that is run when the user (or adb) starts your app. The activity will then start your service for you.
EDIT: I assume you saw this note from the post that you linked. And that you aren't trying this on 2.2?
Note: This didn't work for me on Android 2.2 (it doesn't seem to catch Toasts), but it worked on Android 4.0.
First of all you shouldn't be trying to catch the Toast as this is an Asynchronous call that bring up a Toast on the screen and it will stay on the screen depending on the Timing, it is possible to leave the application and still have the toast appear. You should NOT care when the toast is done as this is irrelevant. All a Toast should be used for is JUST for giving the User info about a particular process that is underway/done or just Information. Its not meant for what you're trying to do.
Why don't you just send an internal broadcast into your application and catch it via an Intent Filter and then upon receiving that broadcast, you should start the service.

How to distinguish an Importand Application from a non Importand one when both are in background

I hope that this question will not have the same luck with this one.
I want to give my users the ability to close some applications which are in the background via a list. By applications in the background I mean applications that the user started and then press the home button, for example Internet Browser. I have managed to find the background applications via the ActivityManager (both getRunningAppProcesses and getRunningTasks will do the job). However there are some applications in the background which are important for the system to work (i.e. Phone, Launcher, Input Methods etc) and I don't want them in my list.
My question is: How can I distinguish them from the non Important ones. I don't want to use some kind of String checking / filtering (like contains(com.android) etc) because I want to exclude some Important 3rd party apps like non stock Launchers, Dialer, Messaging etc.
Everyone who owns a Galaxy S2 and have used the "Program Monitor Widget" will know what i mean.
Thank you very much for your time and efforts...
Well I finally came up with a function that checks if a running task may considered as an "Important" one. First of all we have to use the getRunningTasks function from the ActivityManager to get a list filled with ActivityManager.RunningTaskInfo objects and then we pass each of these objects to the following function to do the check. Hope this helps someone...
public boolean isRunningTaskImportant(ActivityManager.RunningTaskInfo taskinfo) {
//What makes a running task important is somehow "fluid" but there are a few task categories
//on which is safe to assume that they are important. These categories are:
//1. If task has not any actual activities running. This is because these are not actuall running but they are frozen by the
// the system and they will be killed if needed. They are not Important but we do not want them in our running apps list.
//2. Well known namespaces including our own if we want to
//3. Home Launcher Applications
//4. Phone Handling Applications
boolean result = false;
ComponentName bActivity = taskinfo.baseActivity;
if (bActivity == null) return false; //<-- The task has no base activity so we ignore it...
String pName = bActivity.getPackageName();
if (taskinfo.numRunning == 0) {
result = true;
} else {
if (pName.equalsIgnoreCase("com.android.phone")) {
result = true;
} else if (pName.equalsIgnoreCase("com.android.contacts")) {
result = true;
} else if (pName.equalsIgnoreCase("com.chdcomputers.powerpanel")) {
result = true;
} else {
//Here we are checking if out task is a home launcher application.
//This code is based on this question: http://stackoverflow.com/questions/3293253/getting-list-of-installed-apps-easy-but-how-to-launch-one-of-them
Log.d(TAG, "isRunningTaskImportant checking for launchers");
Intent launchersIntent = new Intent(Intent.ACTION_MAIN, null);
launchersIntent.addCategory(Intent.CATEGORY_HOME);
List<ResolveInfo> list = cx.getPackageManager().queryIntentActivities(launchersIntent,0);
boolean found = false;
for (ResolveInfo ri : list){
if (!found){
Log.d(TAG, "isRunningTaskImportant checking launcher app: " + ri.activityInfo.applicationInfo.packageName);
found = pName.equalsIgnoreCase(ri.activityInfo.applicationInfo.packageName);
}
if (found) break;
}
result = found;
if (!found) {
//Finaly we are going to check if out task is a Phone Handling application
//The idea behind that is to check for what kind of permissions the application wants
//In my opinion any "serious" phone handling app should ask for, at least, the following 9 permissions:
//CALL_PHONE, CALL_PRIVILEGED, READ_CONTACTS, WRITE_CONTACTS, SYSTEM_ALERT_WINDOW, READ_PHONE_STATE,
//MODIFY_PHONE_STATE, PROCESS_OUTGOING_CALLS, RECEIVE_BOOT_COMPLETED
//So if the application asks for those permissions we can assume that is a phone handling application...
Log.d(TAG, "isRunningTaskImportant checking possible phone app: " + pName);
try {
PackageInfo pi = cx.getPackageManager().getPackageInfo(pName,PackageManager.GET_PERMISSIONS);
String[] perms = pi.requestedPermissions;
if (perms == null) {
result = false;
} else {
int pCount = 0;
for (String perm : perms) {
if (perm.equalsIgnoreCase("android.permission.CALL_PHONE")) {
pCount++;
} else if (perm.equalsIgnoreCase("android.permission.CALL_PRIVILEGED")) {
pCount++;
} else if (perm.equalsIgnoreCase("android.permission.READ_CONTACTS")) {
pCount++;
} else if (perm.equalsIgnoreCase("android.permission.WRITE_CONTACTS")) {
pCount++;
} else if (perm.equalsIgnoreCase("android.permission.SYSTEM_ALERT_WINDOW")) {
pCount++;
} else if (perm.equalsIgnoreCase("android.permission.READ_PHONE_STATE")) {
pCount++;
} else if (perm.equalsIgnoreCase("android.permission.MODIFY_PHONE_STATE")) {
pCount++;
} else if (perm.equalsIgnoreCase("android.permission.PROCESS_OUTGOING_CALLS")) {
pCount++;
} else if (perm.equalsIgnoreCase("android.permission.RECEIVE_BOOT_COMPLETED")) {
pCount++;
}
}
result = (pCount == 9);
}
} catch (Exception ex) {
Log.e(TAG, "isRunningTaskImportant checking possible phone app ERROR: " + ex.getMessage());
result = false;
}
}
}
}
return result;
}

Categories

Resources