Simulate taking picture in default system camera a

2019-07-24 03:39发布

问题:

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.

回答1:

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.



回答2:

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