Сделайте фото с помощью сервиса на OnePlus One — используя хак WindowManager

Я пытаюсь сделать снимок, ничего не показывая пользователю (без просмотра) через службу. Этот вопрос задавался несколько раз, и я просмотрел все, что мог найти. Некоторые похожие вопросы:

Большинство вопросов связаны с другими вопросами, не предлагая нового решения.

Я считаю, что это лучший способ решить эту проблему: https://stackoverflow.com/a/10268650/3860594 К сожалению человек не дал полного ответа, и у меня возникли проблемы с воспроизведением его метода.

Я пытаюсь создать SurfaceView внутри SurfaceHolder. Затем я буду использовать WindowManager с SurfaceView для создания своего рода плавающего окна, которое будет полностью прозрачным, чтобы оно было скрыто от пользователя. Пожалуйста, поправьте меня, если я ошибаюсь.

Вот мой код:

SurfaceView mview = new SurfaceView(this);
SurfaceHolder mholder = new SurfaceHolder() {
    @Override
    public void addCallback(Callback callback) {

    }

    @Override
    public void removeCallback(Callback callback) {

    }

    @Override
    public boolean isCreating() {
        return false;
    }

    @Override
    public void setType(int type) {

    }

    @Override
        public void setFixedSize(int width, int height) {
    }

    @Override
    public void setSizeFromLayout() {
    }

    @Override
    public void setFormat(int format) {

    }

    @Override
    public void setKeepScreenOn(boolean screenOn) {
    }

    @Override
    public Canvas lockCanvas() {
        return null;
    }

    @Override
    public Canvas lockCanvas(Rect dirty) {
        return null;
    }

    @Override
        public void unlockCanvasAndPost(Canvas canvas) {

    }

    @Override
    public Rect getSurfaceFrame() {
        return null;
    }

    @Override
    public Surface getSurface() {
        return null;
    }
};


WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
        WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
        PixelFormat.TRANSLUCENT);
wm.addView(mview, params);
mview.setZOrderOnTop(true);
mholder.setFormat(PixelFormat.TRANSPARENT);


try {
    camera.setPreviewDisplay(mview.getHolder());
    camera.startPreview();
    camera.takePicture(null,null,photoCallback);
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

Ничто из этого не работает, так как отображается обычное сообщение RuntimeException: takePicture failed. Любая помощь будет потрясающей, спасибо.


person john    schedule 25.09.2015    source источник
comment
Попробуйте сделать окно не полностью прозрачным и проверьте, действительно ли оно показывает предварительный просмотр на экране.   -  person Alex Cohn    schedule 25.09.2015
comment
@AlexCohn, спасибо за ваше предложение, я пытался установить его непрозрачным, но получаю ту же ошибку.   -  person john    schedule 25.09.2015
comment
Вопрос был в том, видите ли вы там поток с камеры?   -  person Alex Cohn    schedule 25.09.2015
comment
@AlexCohn Нет, приложение запускается, а затем сразу же вылетает без плавающих окон или видоискателей.   -  person john    schedule 25.09.2015
comment
Подождите секунду, вы уже видели takePicture failed раньше. Разве эта авария не отличается?   -  person Alex Cohn    schedule 25.09.2015
comment
Нет, этот сбой выглядел точно так же, как на телефоне, так и на AStudio.   -  person john    schedule 25.09.2015
comment
Кстати, неясно, какова роль поля mholder в вашем фрагменте кода.   -  person Alex Cohn    schedule 25.09.2015
comment
Мой недостаток: я не замечал эту ошибку раньше. Вы никогда не должны вызывать takePicture() сразу после addView(). Вы должны дождаться хотя бы обратного вызова surfaceCreated().   -  person Alex Cohn    schedule 25.09.2015
comment
mholder используется только в этом фрагменте кода в моем приложении. Кажется, mholder на самом деле не делает ничего полезного :/ Кроме того, как я могу дождаться обратного вызова, созданного поверхностью? (поиск в гугле не помог) спасибо   -  person john    schedule 26.09.2015
comment
mview.getHolder().addCallback()   -  person Alex Cohn    schedule 26.09.2015
comment
Я попробовал SystemClock.sleep(20000); прямо перед takePicture, и на этот раз экран телефона на секунду стал черным прямо перед тем, как приложение вылетело из строя. Хотя такая же ошибка.   -  person john    schedule 26.09.2015
comment
Спасибо, добавил обратный звонок и теперь ошибки нет и приложение не вылетает. Однако изображение, которое было сохранено в хранилище, было полностью черным.   -  person john    schedule 26.09.2015
comment
Черный экран НЕ является пустым изображением (размер 200 КБ). Я думаю, это потому, что изображение с камеры не загружается должным образом, захваченное изображение черное. Любые идеи? Спасибо   -  person john    schedule 26.09.2015
comment
превью тоже черное?   -  person Alex Cohn    schedule 26.09.2015
comment
На экране нет предварительного просмотра (я пробовал и непрозрачный, и прозрачный), однако Logcat показывает это предупреждение: I/Choreographer﹕ Skipped 395 frames! The application may be doing too much work on its main thread. W/InputEventReceiver﹕ Attempted to finish an input event but the input event receiver has already been disposed.   -  person john    schedule 27.09.2015
comment
Как открыть камеру?   -  person Alex Cohn    schedule 27.09.2015
comment
Я не открываю интерфейс камеры (если это то, о чем вы спрашиваете), я хочу сделать снимок напрямую, ничего не показывая пользователю. Что касается того, как я открываю объект camera, вы можете увидеть это на этом снимке экрана: i.imgur.com/F5sXvgI .png   -  person john    schedule 27.09.2015
comment
Ожидается сообщение хореографа, но, возможно, вы сможете уменьшить количество задач, использующих поток пользовательского интерфейса. А вот второе странно: не слишком ли рано вы избавляетесь от SurfaceView?   -  person Alex Cohn    schedule 27.09.2015
comment
В моем проекте есть основная активность, из которой я запускаю службу (onCreate), и я пытаюсь запустить этот метод на службе. Я больше ничего не делаю в пользовательском интерфейсе. Я никуда не выбрасывал SurfaceView. Может он не правильно создан? Это возможно?   -  person john    schedule 27.09.2015
comment
Возможно, ваша ошибка в том, что вы создаете SurfaceView в onCreate(). Попробуйте onStart() - у меня работает!   -  person Alex Cohn    schedule 28.09.2015
comment
Извините за радиомолчание. На самом деле мой сервис запускается при onCreate основного действия. И тогда мой метод takePicture запускается из метода onStartCommand сервиса. Может быть, вы можете загрузить свой проект в виде zip-файла, чтобы я мог взглянуть? Спасибо   -  person john    schedule 01.10.2015
comment
github.com/alexcohn/CameraInService   -  person Alex Cohn    schedule 01.10.2015
comment
Я очень, очень ценю ваш код. Но нигде в CameraService.java вы не вызываете метод camera.takePicture для захвата изображения. Превью загружается отлично.   -  person john    schedule 01.10.2015
comment
Добавьте takePicture после того, как предварительный просмотр будет готов, см. второй коммит.   -  person Alex Cohn    schedule 01.10.2015
comment
Я попробовал ваш код, и он работает нормально. Чтобы подтвердить правильность захвата изображения, я попытался сохранить его в хранилище и внес следующие изменения: " rel="nofollow noreferrer">gist.github.com/puckh/ и изображение сохраняется как знакомый черный экран.   -  person john    schedule 01.10.2015
comment
извините, у меня работает ;-) См. следующий коммит. На самом деле нет никакой причины, кроме упражнения, конвертировать данные JPEG в Bitmap и сжимать их обратно. Тем не менее, вы можете сохранить PNG таким образом.   -  person Alex Cohn    schedule 01.10.2015
comment
Спасибо за ваш отзыв. Очень полезно. Я пробовал все, в том числе просто запускал ваш проект, но мой JPG всегда просто черный экран. Я использую OnePlus One (уровень API 21).   -  person john    schedule 01.10.2015
comment
@AlexCohn попробовал код на телефоне друга, и он сработал нормально (не удалось сделать предварительный просмотр невидимым, но изображение вышло не сфокусированным и с разрешением строки, но все еще в порядке). Интересно, почему это не работало на моем телефоне. Спасибо за поддержку. Очень ценю вашу работу.   -  person john    schedule 02.10.2015


Ответы (1)


Бывает так, что последний кирпичик головоломки раскрылся только через неделю после того, как был задан вопрос. Проблема специфична для конкретного устройства, а именно OnePlus One.

Пример кода на https://github.com/alexcohn/CameraInService показывает, как это можно сделать; последняя фиксация добавляет задержку между запуском предварительного просмотра и выполнением takePicture(). Эксперимент на OnePlus One моей жены показывает, что задержка в 100 мс — это нормально, но 1 мс — это слишком мало.

person Alex Cohn    schedule 02.10.2015