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