Untitled

 avatar
unknown
plain_text
5 months ago
22 kB
4
Indexable
package com.armasight.app.streamUSB;

import android.Manifest;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.SurfaceTexture;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.media.projection.MediaProjectionManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;

import com.armasight.app.R;
import com.armasight.app.base.BaseMainFragment;
import com.armasight.app.streamWiFi.ScreenRecordingService;
import com.serenegiant.usb.CameraDialog;
import com.serenegiant.usb.USBMonitor;
import com.serenegiant.usb.UVCCamera;
import com.serenegiant.usbcameracommon.UVCCameraHandler;
import com.serenegiant.widget.CameraViewInterface;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class StreamUSBFragment extends BaseMainFragment implements CameraDialog.CameraDialogParent {

    public static final String TAG = "StreamUSBFragment";
    private static final boolean DEBUG = true;

    // Request codes and permissions
    private static final int REQUEST_CAMERA_PERMISSION = 200;
    private static final int SCREEN_RECORD_REQUEST_CODE = 1001;
    private static final int PERMISSION_REQUEST_CODE = 1002;

    // Preview configurations
//    private static final int PREVIEW_WIDTH = 512;
//    private static final int PREVIEW_HEIGHT = 384;
    private static final int PREVIEW_WIDTH = 640;
    private static final int PREVIEW_HEIGHT = 480;
//    works
    private static final int PREVIEW_MODE = 1;
    private boolean isPreviewRunning = false;

    // Intent action
    private static final String ACTION_USB_PERMISSION = "com.armasight.app.USB_PERMISSION";

    // UI Elements
    private ImageButton mCaptureButton;
    private ImageButton screenRecordButton;
    private ImageButton screenshotButton;
    private CameraViewInterface mUVCCameraView;

    // USB and Camera Handlers
    private USBMonitor mUSBMonitor;
    private UVCCameraHandler mCameraHandler;

    // State variables
    private boolean isRecording = false;
    private PendingIntent mPermissionIntent;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (DEBUG) Log.v(TAG, "onCreate:");

        // Request all necessary permissions upfront
        requestAllPermissions();

        // Setup USB permission intent and receiver
        setupUsbPermissionReceiver();
    }

    private void setupUsbPermissionReceiver() {
        mPermissionIntent = PendingIntent.getBroadcast(
                getActivity(), 0, new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);

        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);

        // Specify the receiver as exported
        getActivity().registerReceiver(mUsbReceiver, filter, Context.RECEIVER_EXPORTED);
    }


    private void requestAllPermissions() {
        List<String> permissionsToRequest = new ArrayList<>();

        if (!hasPermission(Manifest.permission.CAMERA)) {
            permissionsToRequest.add(Manifest.permission.CAMERA);
        }
        if (!hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            permissionsToRequest.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
        if (!hasPermission(Manifest.permission.RECORD_AUDIO)) {
            permissionsToRequest.add(Manifest.permission.RECORD_AUDIO);
        }

        if (!permissionsToRequest.isEmpty()) {
            ActivityCompat.requestPermissions(
                    getActivity(),
                    permissionsToRequest.toArray(new String[0]),
                    PERMISSION_REQUEST_CODE);
        }
    }

    private boolean hasPermission(String permission) {
        return ContextCompat.checkSelfPermission(
                getContext(), permission) == PackageManager.PERMISSION_GRANTED;
    }

    @Override
    public View onCreateView(
            @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.activity_usb_main, container, false);

        // Initialize UI elements and handlers
        initializeUIElements(view);
        initializeUSBHandlers();

        // Handle orientation
        handleOrientation(getResources().getConfiguration().orientation);

        return view;
    }

    private void initializeUIElements(View view) {
        mCaptureButton = view.findViewById(R.id.capture_button);
        mUVCCameraView = view.findViewById(R.id.camera_view);
        screenRecordButton = view.findViewById(R.id.screen_record_button);
        screenshotButton = view.findViewById(R.id.screenshot_button);

        setupCaptureButton();
        setupCameraView();
        setupScreenRecordButton();
        setupScreenshotButton();
    }

    private void setupCaptureButton() {
        if (mCaptureButton != null) {
            mCaptureButton.setOnClickListener(mOnClickListener);
            mCaptureButton.setVisibility(View.INVISIBLE);
        }
    }

    private void setupCameraView() {
        if (mUVCCameraView != null) {
            mUVCCameraView.setAspectRatio(PREVIEW_WIDTH / (float) PREVIEW_HEIGHT);
            mUVCCameraView.setCallback(new CameraViewInterface.Callback() {
                @Override
                public void onSurfaceCreated(CameraViewInterface view, Surface surface) {
                    // Start the preview when surface is created
                    if (mCameraHandler != null && !mCameraHandler.isPreviewing()) {
                        mCameraHandler.startPreview(surface);
                    }
                    isPreviewRunning = true; // Set flag to indicate preview is running
                }

                @Override
                public void onSurfaceChanged(CameraViewInterface view, Surface surface, int width, int height) {
                    // Handle surface size changes if necessary
                }

                @Override
                public void onSurfaceDestroy(CameraViewInterface view, Surface surface) {
                    // Stop the preview when the surface is destroyed
                    if (mCameraHandler != null) {
                        mCameraHandler.stopPreview();
                    }
                    isPreviewRunning = false; // Reset preview running flag
                }
            });
        }
    }


    private void setupScreenRecordButton() {
        if (screenRecordButton != null) {
            screenRecordButton.setOnClickListener(v -> {
                if (isRecording) {
                    stopScreenRecording();
                } else {
                    requestScreenRecordingPermissions();
                }
            });
        }
    }

    private void setupScreenshotButton() {
        if (screenshotButton != null) {
            screenshotButton.setOnClickListener(v -> takeScreenshot());
        }
    }

    private void initializeUSBHandlers() {
        mUSBMonitor = new USBMonitor(getActivity(), mOnDeviceConnectListener);
        mCameraHandler = UVCCameraHandler.createHandler(
                getActivity(), mUVCCameraView, 2, PREVIEW_WIDTH, PREVIEW_HEIGHT, PREVIEW_MODE);

        requestCameraPermission();
    }

    private void handleOrientation(int orientation) {
        if (screenshotButton == null || screenRecordButton == null) {
            Log.e(TAG, "UI elements are null, skipping orientation handling");
            return;
        }

        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            Log.d(TAG, "Switching to portrait mode");
            screenshotButton.setVisibility(View.GONE);
            screenRecordButton.setVisibility(View.GONE);
        } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
            Log.d(TAG, "Switching to landscape mode");
            screenshotButton.setVisibility(View.VISIBLE);
            screenRecordButton.setVisibility(View.VISIBLE);
        }
    }

    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        // Handle orientation change
        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ||
                newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {

            // Re-initialize camera view and start preview after orientation change
            if (mCameraHandler != null && mUVCCameraView != null) {
                if (mUVCCameraView.getSurfaceTexture() != null) {
                    mCameraHandler.startPreview(new Surface(mUVCCameraView.getSurfaceTexture()));
                }
            }
            handleOrientation(newConfig.orientation);
        }
    }


    private void requestScreenRecordingPermissions() {
        List<String> permissionsToRequest = new ArrayList<>();

        if (!hasPermission(Manifest.permission.RECORD_AUDIO)) {
            permissionsToRequest.add(Manifest.permission.RECORD_AUDIO);
        }
        if (!hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            permissionsToRequest.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }

        if (!permissionsToRequest.isEmpty()) {
            ActivityCompat.requestPermissions(
                    getActivity(),
                    permissionsToRequest.toArray(new String[0]),
                    PERMISSION_REQUEST_CODE);
        } else {
            requestScreenRecording();
        }
    }

    private void requestScreenRecording() {
        MediaProjectionManager mediaProjectionManager =
                (MediaProjectionManager) getActivity().getSystemService(Context.MEDIA_PROJECTION_SERVICE);
        if (mediaProjectionManager != null) {
            Intent screenCaptureIntent = mediaProjectionManager.createScreenCaptureIntent();
            startActivityForResult(screenCaptureIntent, SCREEN_RECORD_REQUEST_CODE);
        }
    }

    private void stopScreenRecording() {
        Intent serviceIntent = new Intent(getActivity(), ScreenRecordingService.class);
        getActivity().stopService(serviceIntent);
        isRecording = false;
        updateRecordButton();
    }

    private void startScreenRecordingService(int resultCode, Intent data) {
        Intent serviceIntent = new Intent(getActivity(), ScreenRecordingService.class);
        serviceIntent.putExtra("RESULT_CODE", resultCode);
        serviceIntent.putExtra("DATA", data);
        ContextCompat.startForegroundService(getActivity(), serviceIntent);
        isRecording = true;
        updateRecordButton();
    }

    private void updateRecordButton() {
        if (isRecording) {
            screenRecordButton.setImageResource(R.drawable.ic_stop); // Update to stop icon
        } else {
            screenRecordButton.setImageResource(R.drawable.ic_record); // Update to record icon
        }
    }

    private void takeScreenshot() {
        View rootView = getActivity().getWindow().getDecorView().getRootView();

        // Hide buttons temporarily
        toggleButtonsVisibility(View.INVISIBLE);

        // Capture the screenshot
        rootView.setDrawingCacheEnabled(true);
        Bitmap bitmap = Bitmap.createBitmap(rootView.getDrawingCache());
        rootView.setDrawingCacheEnabled(false);

        // Show buttons again
        toggleButtonsVisibility(View.VISIBLE);

        saveScreenshot(bitmap);
    }

    private void toggleButtonsVisibility(int visibility) {
        if (screenRecordButton != null) {
            screenRecordButton.setVisibility(visibility);
        }
        if (screenshotButton != null) {
            screenshotButton.setVisibility(visibility);
        }
    }

    private void saveScreenshot(Bitmap bitmap) {
        String fileName = "screenshot_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".png";
        OutputStream fos = null;

        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                fos = saveScreenshotToMediaStore(bitmap, fileName);
            } else {
                fos = saveScreenshotToFile(bitmap, fileName);
            }

            if (fos != null) {
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
                fos.close();
                Toast.makeText(getActivity(), "Screenshot saved", Toast.LENGTH_SHORT).show();
            }
        } catch (IOException e) {
            Log.e(TAG, "Error saving screenshot", e);
            Toast.makeText(getActivity(), "Error saving screenshot", Toast.LENGTH_SHORT).show();
        }
    }

    private OutputStream saveScreenshotToMediaStore(Bitmap bitmap, String fileName) throws IOException {
        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
        values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
        Uri uri = getActivity().getContentResolver().insert(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        if (uri == null) {
            throw new IOException("Failed to create new MediaStore record.");
        }
        return getActivity().getContentResolver().openOutputStream(uri);
    }

    private OutputStream saveScreenshotToFile(Bitmap bitmap, String fileName) throws IOException {
        String filePath = Environment.getExternalStorageDirectory() + "/" + Environment.DIRECTORY_PICTURES;
        File dir = new File(filePath);
        if (!dir.exists() && !dir.mkdirs()) {
            throw new IOException("Failed to create directory: " + filePath);
        }
        File file = new File(filePath, fileName);
        return new FileOutputStream(file);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        if (requestCode == SCREEN_RECORD_REQUEST_CODE && resultCode == Activity.RESULT_OK && data != null) {
            startScreenRecordingService(resultCode, data);
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        if (DEBUG) Log.v(TAG, "onStart:");

        // Register USB monitor only if not already registered
        if (!mUSBMonitor.isRegistered()) {
            mUSBMonitor.register();
        }

        // Ensure the camera view resumes without reinitializing the preview
        if (mUVCCameraView != null) mUVCCameraView.onResume();
    }

    @Override
    public void onStop() {
        super.onStop();
        if (DEBUG) Log.v(TAG, "onStop:");

        // Avoid unregistering USB monitor on stop to keep the stream running
        if (mUVCCameraView != null) mUVCCameraView.onPause();
    }

    // Only fully clean up in onDestroy
    @Override
    public void onDestroy() {
        super.onDestroy();
        if (DEBUG) Log.v(TAG, "onDestroy:");

        // Release resources only on fragment destruction
        if (mCameraHandler != null) {
            mCameraHandler.release();
            mCameraHandler = null;
        }
        if (mUSBMonitor != null) {
            mUSBMonitor.unregister();
            mUSBMonitor.destroy();
            mUSBMonitor = null;
        }
        mUVCCameraView = null;
        mCaptureButton = null;
        getActivity().unregisterReceiver(mUsbReceiver);

        // Reset preview state flag
        isPreviewRunning = false;
    }


    private final View.OnClickListener mOnClickListener = view -> {
        if (view.getId() == R.id.capture_button && mCameraHandler.isOpened()) {
            if (hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) && hasPermission(Manifest.permission.RECORD_AUDIO)) {
                if (!mCameraHandler.isRecording()) {
                    mCaptureButton.setColorFilter(0xffff0000);
                    mCameraHandler.startRecording();
                } else {
                    mCaptureButton.setColorFilter(0);
                    mCameraHandler.stopRecording();
                }
            }
        }
    };

    private void startPreview() {
        if (mUVCCameraView != null) {
            final SurfaceTexture st = mUVCCameraView.getSurfaceTexture();
            if (st != null) {
                mCameraHandler.startPreview(new Surface(st));
                getActivity().runOnUiThread(() -> mCaptureButton.setVisibility(View.VISIBLE));
                isPreviewRunning = true;
            }
        }
    }


    private final USBMonitor.OnDeviceConnectListener mOnDeviceConnectListener = new USBMonitor.OnDeviceConnectListener() {

        @Override
        public void onAttach(@NonNull final UsbDevice device) {
            if (DEBUG) Log.v(TAG, "onAttach:");
            Toast.makeText(getActivity(), "USB Device Connected", Toast.LENGTH_SHORT).show();

            if (mUSBMonitor != null && !mUSBMonitor.hasPermission(device)) {
                mUSBMonitor.requestPermission(device);
            } else if (!isPreviewRunning) {
                // Only start preview if it's not already running
                mCameraHandler.open(mUSBMonitor.openDevice(device));
                startPreview();
                isPreviewRunning = true; // Set flag to indicate preview is running
            }
        }



        @Override
        public void onConnect(@NonNull final UsbDevice device,
                              @NonNull final USBMonitor.UsbControlBlock ctrlBlock, boolean createNew) {
            if (DEBUG) Log.v(TAG, "onConnect:");
            if (!isPreviewRunning) { // Check if preview is already running
                mCameraHandler.open(ctrlBlock);
                startPreview();
                isPreviewRunning = true;
            }
        }

        @Override
        public void onDisconnect(@NonNull final UsbDevice device,
                                 @NonNull final USBMonitor.UsbControlBlock ctrlBlock) {
            if (DEBUG) Log.v(TAG, "onDisconnect:");
            if (mCameraHandler != null) {
                mCameraHandler.close();
            }
        }

        @Override
        public void onDettach(@NonNull final UsbDevice device) {
            if (DEBUG) Log.v(TAG, "onDettach:");
            if (mCameraHandler.isOpened()) {
                mCameraHandler.close();
            }
            Toast.makeText(getActivity(), "USB Device Detached", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onCancel(@NonNull final UsbDevice device) {
            if (DEBUG) Log.v(TAG, "onCancel:");
        }
    };

    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (ACTION_USB_PERMISSION.equals(intent.getAction())) {
                synchronized (this) {
                    UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        if (device != null) {
                            Toast.makeText(getActivity(),
                                    "Permission granted for device: " + device.getDeviceName(),
                                    Toast.LENGTH_SHORT).show();
                            // Automatically start preview when permission is granted
                            mCameraHandler.open(mUSBMonitor.openDevice(device));
                            startPreview();
                        }
                    } else {
                        if (device != null) {
                            Toast.makeText(getActivity(),
                                    "Permission denied for device: " + device.getDeviceName(),
                                    Toast.LENGTH_SHORT).show();
                        }
                    }
                }
            }
        }
    };

    private void requestCameraPermission() {
        if (!hasPermission(Manifest.permission.CAMERA)) {
            ActivityCompat.requestPermissions(
                    getActivity(),
                    new String[]{Manifest.permission.CAMERA},
                    REQUEST_CAMERA_PERMISSION);
        }
    }

    @Override
    public void onDialogResult(boolean canceled) {
        if (DEBUG) Log.v(TAG, "onDialogResult:canceled=" + canceled);
    }

    @Override
    public USBMonitor getUSBMonitor() {
        return mUSBMonitor;
    }

    @Override
    public void onRequestPermissionsResult(
            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == PERMISSION_REQUEST_CODE) {
            if (allPermissionsGranted(grantResults)) {
                requestScreenRecording();
            } else {
                Toast.makeText(getActivity(), "Permissions denied", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private boolean allPermissionsGranted(int[] grantResults) {
        for (int grantResult : grantResults) {
            if (grantResult != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }
}

Editor is loading...
Leave a Comment