How can i implement Dropbox oAuth2 flow method on my Android app

40 Views Asked by At

I want to implement Dropbox oAuth2 flow method on my Android app so that user can get refresh access token without update my code every 4 hours i have add a key to my camera app so when the user click on it its turn on the oAuth flow operation but i had a problem when i press on it then its open the url in google that i designed then i press allow in the website of the dropbox and i get the Authorized code in my url and i returned to the app directly but when i press on the photo capture logo to take the photo the app keep crashing here is my app code

this is my app code:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final int CAMERA_PERMISSION_REQUEST = 100;
    private static final String[] REQUIRED_CAMERA_PERMISSIONS = new String[]{Manifest.permission.CAMERA};

    private static final String DROPBOX_APP_KEY = "hc2p6uj44p9pyoc";
    private static final String DROPBOX_APP_SECRET = "this is my secret code i cannot showed it";
    private static final String DROPBOX_REDIRECT_URI = "https://decamera://auth-finish";

    private String DROPBOX_ACCESS_TOKEN = null;
    private PreviewView previewView;
    private ImageCapture imageCapture;
    private TextView addressTextView;

    private ExecutorService cameraExecutor = Executors.newSingleThreadExecutor();
    private FusedLocationProviderClient fusedLocationProviderClient;
    private Geocoder geocoder;

    // Add latitude and longitude variables
    private double latitudeValue;
    private double longitudeValue;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        previewView = findViewById(R.id.previewView);
        ImageButton captureButton = findViewById(R.id.captureButton);
        ImageButton leftCornerButton = findViewById(R.id.leftCornerButton);
        addressTextView = findViewById(R.id.addressTextView);

        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
        geocoder = new Geocoder(this, Locale.getDefault());

        captureButton.setOnClickListener(this);

        leftCornerButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                initiateDropboxAuthorization();
            }
        });

        addressTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showEditAddressDialog();
            }
        });

        if (allPermissionsGranted()) {
            startCamera();
            getLastLocation();
        } else {
            ActivityCompat.requestPermissions(this, REQUIRED_CAMERA_PERMISSIONS, CAMERA_PERMISSION_REQUEST);
        }

        // Initialize Dropbox access token if available
        SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
        DROPBOX_ACCESS_TOKEN = prefs.getString("dropboxAccessToken", null);

        // Check if the app was opened with a Dropbox authorization callback
        handleDropboxAuthorizationCallback(getIntent());
    }

    // Override onNewIntent to handle the Dropbox authorization callback
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        handleDropboxAuthorizationCallback(intent);
    }
    private void initiateDropboxAuthorization() {
        // Construct the Dropbox authorization URL
        String authorizationUrl = "https://www.dropbox.com/oauth2/authorize" +
                "?client_id=" + DROPBOX_APP_KEY +
                "&response_type=code" +
                "&token_access_type=offline" +
                "&state=myState" +  // Replace with your own state
                "&redirect_uri=" + DROPBOX_REDIRECT_URI;

        // Open the authorization URL in a web browser or WebView
        Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(authorizationUrl));
        startActivity(browserIntent);
    }
    private String performPostRequest(String requestUrl, String urlParameters) {
        try {
            URL url = new URL(requestUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            connection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length));
            connection.setDoOutput(true);

            // Log the request details
            Log.d("Dropbox", "POST Request URL: " + requestUrl);
            Log.d("Dropbox", "POST Request Body: " + urlParameters);

            // Write the request body
            try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) {
                wr.writeBytes(urlParameters);
                wr.flush();
            }

            // Get the response
            try (InputStream is = connection.getInputStream();
                 BufferedReader rd = new BufferedReader(new InputStreamReader(is))) {
                StringBuilder response = new StringBuilder();
                String line;
                while ((line = rd.readLine()) != null) {
                    response.append(line);
                    response.append('\r');
                }

                // Log the response
                Log.d("Dropbox", "POST Response: " + response.toString());

                return response.toString();
            }
        } catch (Exception e) {
            // Log any exceptions
            Log.e("Dropbox", "Error in HTTP request: " + e.getMessage());
            return null;
        }
    }
    private void handleDropboxAuthorizationCallback(Intent intent) {
        Uri data = intent.getData();
        if (data != null && data.toString().startsWith(DROPBOX_REDIRECT_URI)) {
            // Authorization successful, extract the authorization code
            String code = data.getQueryParameter("code");

            // Log the authorization code for debugging
            Log.d("Dropbox", "Authorization Code: " + code);

            // Now, exchange the authorization code for an access token and refresh token
            exchangeAuthorizationCodeForTokens(code);
        }
    }
    private void exchangeAuthorizationCodeForTokens(String code) {
        try {
            String url = "https://api.dropbox.com/oauth2/token";
            String requestBody = "code=" + code +
                    "&grant_type=authorization_code" +
                    "&client_id=" + DROPBOX_APP_KEY +
                    "&client_secret=" + DROPBOX_APP_SECRET +
                    "&redirect_uri=" + DROPBOX_REDIRECT_URI;

            // Log the request details
            Log.d("Dropbox", "Token Exchange Request URL: " + url);
            Log.d("Dropbox", "Token Exchange Request Body: " + requestBody);

            // Perform the POST request and obtain the JSON response
            String jsonResponse = performPostRequest(url, requestBody);

            // Log the response
            Log.d("Dropbox", "Token Exchange Response: " + jsonResponse);

            // Parse the JSON response to extract access and refresh tokens
            JSONObject jsonObject = new JSONObject(jsonResponse);
            DROPBOX_ACCESS_TOKEN = jsonObject.getString("access_token");

            // Log the obtained access token
            Log.d("Dropbox", "Access Token Obtained: " + DROPBOX_ACCESS_TOKEN);

            // Save the Dropbox access token
            SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
            prefs.edit().putString("dropboxAccessToken", DROPBOX_ACCESS_TOKEN).apply();

        } catch (Exception e) {
            // Log any exceptions
            Log.e("Dropbox", "Error in token exchange: " + e.getMessage());
        }
    }



    private void showEditAddressDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Edit Address");

        // Set up the input
        View viewInflated = LayoutInflater.from(this).inflate(R.layout.edit_address_dialog, null);
        final EditText input = viewInflated.findViewById(R.id.editTextAddress);
        input.setText(addressTextView.getText()); // Pre-fill with existing address
        builder.setView(viewInflated);

        // Set up buttons
        builder.setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // Update the addressTextView with the edited address
                String editedAddress = input.getText().toString();
                addressTextView.setText(editedAddress);
            }
        });

        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel(); // Cancel the update
            }
        });

        // Make the dialog cancelable by tapping outside
        builder.setCancelable(true);

        // Show the dialog
        builder.show();
    }

    private boolean allPermissionsGranted() {
        for (String permission : REQUIRED_CAMERA_PERMISSIONS) {
            if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

    private void startCamera() {
        ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);

        cameraProviderFuture.addListener(() -> {
            try {
                ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
                bindPreview(cameraProvider);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }, ContextCompat.getMainExecutor(this));
    }

    private void bindPreview(ProcessCameraProvider cameraProvider) {
        Preview preview = new Preview.Builder().build();

        CameraSelector cameraSelector = new CameraSelector.Builder()
                .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                .build();

        Camera camera = cameraProvider.bindToLifecycle((LifecycleOwner) this, cameraSelector, preview);

        preview.setSurfaceProvider(previewView.getSurfaceProvider());

        imageCapture = new ImageCapture.Builder()
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .setTargetRotation(previewView.getDisplay().getRotation())
                .build();

        cameraProvider.unbindAll();
        cameraProvider.bindToLifecycle((LifecycleOwner) this, cameraSelector, preview, imageCapture);
    }
    private void combineImageWithAddressAndChoices(File photoFile, String addressText, String firstQuestionChoice, String secondQuestionChoice, double latitude, double longitude) {
        Bitmap photoBitmap = BitmapFactory.decodeFile(photoFile.getAbsolutePath());

        int rotation = getRotationAngle(photoFile.getAbsolutePath());
        if (rotation != 0) {
            photoBitmap = rotateBitmap(photoBitmap, rotation);
        }

        Bitmap combinedBitmap = photoBitmap.copy(photoBitmap.getConfig(), true);
        Canvas canvas = new Canvas(combinedBitmap);

        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));

        float x = 130;
        float yAddress = combinedBitmap.getHeight() - 800 + 130;
        float yTimestamp = yAddress + paint.getTextSize() + 130; // Add more space between lines
        float yFirstChoice = yTimestamp + paint.getTextSize() + 130; // Add more space between lines
        float ySecondChoice = yFirstChoice + paint.getTextSize() + 130; // Add more space between lines
        float yCoordinates = ySecondChoice + paint.getTextSize() + 130; // Add more space between lines

        // Increase text size
        float textSize = 130; // Adjust the text size as needed
        paint.setTextSize(textSize);

        // Add address with background
        drawTextWithBackground(canvas, paint, "Address: " + addressText, x, yAddress);

        // Add timestamp with background
        String timeStamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date());
        drawTextWithBackground(canvas, paint, "Timestamp: " + timeStamp, x, yTimestamp);

        // Add user's choices with background
        drawTextWithBackground(canvas, paint, "Betons: " + firstQuestionChoice, x, yFirstChoice);
        drawTextWithBackground(canvas, paint, "Holzes: " + secondQuestionChoice, x, ySecondChoice);

        // Add coordinates with background
        String coordinatesText = String.format(Locale.getDefault(), "Latitude: %.6f, Longitude: %.6f", latitude, longitude);
        drawTextWithBackground(canvas, paint, coordinatesText, x, yCoordinates);

        saveCombinedImage(combinedBitmap, photoFile);
    }

    // Helper method to draw text with background
    private void drawTextWithBackground(Canvas canvas, Paint paint, String text, float x, float y) {
        // Get text bounds to determine the background width and height
        Rect bounds = new Rect();
        paint.getTextBounds(text, 0, text.length(), bounds);

        // Add a semi-transparent background for the text
        paint.setColor(Color.parseColor("#80000000")); // Adjust the color and transparency as needed
        RectF backgroundRect = new RectF(x, y - bounds.height() + 20, x + bounds.width() + 40, y + 20);
        canvas.drawRect(backgroundRect, paint);
        paint.setColor(Color.WHITE); // Set text color back to white

        // Draw the text
        canvas.drawText(text, x + 20, y, paint);
    }

    private void getLastLocation() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            fusedLocationProviderClient.getLastLocation()
                    .addOnSuccessListener(this, new OnSuccessListener<Location>() {
                        @Override
                        public void onSuccess(Location location) {
                            if (location != null) {
                                latitudeValue = location.getLatitude();
                                longitudeValue = location.getLongitude();
                                updateAddress(location);
                            }
                        }
                    });
        } else {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, CAMERA_PERMISSION_REQUEST);
        }
    }

    private void updateAddress(Location location) {
        try {
            List<Address> addresses = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1);
            if (addresses != null && addresses.size() > 0) {
                Address address = addresses.get(0);
                String fullAddress = address.getAddressLine(0);
                addressTextView.setText(fullAddress);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void captureImage() {
        // Create an AlertDialog to ask the user the first question
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Was ist die Art des Betons?");

        // Add options to the dialog
        String[] options = {"steif", "plastisch", "fließfähig"};
        builder.setItems(options, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // Handle the user's choice
                String firstQuestionChoice = options[which];

                // Now, ask the user the second question
                askSecondQuestion(firstQuestionChoice);
            }
        });

        // Show the dialog
        builder.show();
    }

    private void askSecondQuestion(String firstQuestionChoice) {
        // Create an AlertDialog to ask the user the second question
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Was ist die Art des holz?");

        // Add options to the dialog
        String[] options = {"Fichtenholz", "Eichenholz", "Buchenholz"};
        builder.setItems(options, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // Handle the user's choice for the second question
                String secondQuestionChoice = options[which];

                // Now, capture the image with both choices
                captureImageAfterUserChoices(firstQuestionChoice, secondQuestionChoice);
            }
        });

        // Show the dialog
        builder.show();
    }

    private void captureImageAfterUserChoices(String firstQuestionChoice, String secondQuestionChoice) {
        // Now, capture the image as before

        File photoFile = createImageFile();
        if (photoFile != null) {
            ImageCapture.OutputFileOptions outputFileOptions =
                    new ImageCapture.OutputFileOptions.Builder(photoFile).build();

            imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(this), new ImageCapture.OnImageSavedCallback() {
                @Override
                public void onImageSaved(@Nullable ImageCapture.OutputFileResults outputFileResults) {
                    // Combine the image with the edited address, timestamp, latitude, and longitude before uploading to Dropbox
                    combineImageWithAddressAndChoices(photoFile, addressTextView.getText().toString(), firstQuestionChoice, secondQuestionChoice, latitudeValue, longitudeValue);

                    // Show the drawn photo to the user before uploading
                    showPreviewDialog(photoFile);
                }

                @Override
                public void onError(@NonNull ImageCaptureException exception) {
                    runOnUiThread(() -> Toast.makeText(MainActivity.this, "Error capturing image: " + exception.getMessage(), Toast.LENGTH_SHORT).show());
                }
            });

        } else {
            runOnUiThread(() -> Toast.makeText(MainActivity.this, "Error creating photo file", Toast.LENGTH_SHORT).show());
        }
    }

    // Helper method to show a preview dialog to the user
    private void showPreviewDialog(File photoFile) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("To DropBox Server");

        // Inflate the layout containing an ImageView
        View viewInflated = LayoutInflater.from(this).inflate(R.layout.preview_dialog, null);
        ImageView imageViewPreview = viewInflated.findViewById(R.id.imageViewPreview);
        imageViewPreview.setImageBitmap(BitmapFactory.decodeFile(photoFile.getAbsolutePath()));

        builder.setView(viewInflated);

        // Set up buttons
        builder.setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // Call the AsyncTask to upload the combined photo to Dropbox in the background
                new AsyncTask<Void, Void, Void>() {
                    @Override
                    protected Void doInBackground(Void... voids) {
                        uploadToDropbox(photoFile);
                        return null;
                    }
                }.execute();
            }
        });

        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // Delete the temporary photo file
                if (photoFile.exists()) {
                    photoFile.delete();
                }
            }
        });

        // Make the dialog cancelable by tapping outside
        builder.setCancelable(true);

        // Show the dialog
        builder.show();
    }

    private File createImageFile() {
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
        String imageFileName = "JPEG_" + timeStamp + "_";
        File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);

        try {
            if (storageDir != null) {
                File tempFile = File.createTempFile(imageFileName, ".jpg", storageDir);
                System.out.println("File path: " + tempFile.getAbsolutePath());
                return tempFile;
            } else {
                runOnUiThread(() -> Toast.makeText(MainActivity.this, "Error creating storage directory", Toast.LENGTH_SHORT).show());
            }
        } catch (IOException e) {
            e.printStackTrace();
            runOnUiThread(() -> Toast.makeText(MainActivity.this, "Error creating photo file: " + e.getMessage(), Toast.LENGTH_SHORT).show());
        }

        return null;
    }

    private int getRotationAngle(String imagePath) {
        ExifInterface exifInterface;
        try {
            exifInterface = new ExifInterface(imagePath);
        } catch (IOException e) {
            e.printStackTrace();
            return 0;
        }

        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                return 90;
            case ExifInterface.ORIENTATION_ROTATE_180:
                return 180;
            case ExifInterface.ORIENTATION_ROTATE_270:
                return 270;
            default:
                return 0;
        }
    }

    private Bitmap rotateBitmap(Bitmap bitmap, int angle) {
        Matrix matrix = new Matrix();
        matrix.postRotate(angle);
        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }

    private void saveCombinedImage(Bitmap combinedBitmap, File photoFile) {
        try {
            FileOutputStream out = new FileOutputStream(photoFile);
            combinedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
            runOnUiThread(() -> Toast.makeText(MainActivity.this, "Error combining image with address: " + e.getMessage(), Toast.LENGTH_SHORT).show());
        }
    }

    private void uploadToDropbox(File photoFile) {
        if (photoFile == null || !photoFile.exists()) {
            runOnUiThread(() -> Toast.makeText(MainActivity.this, "Error: Photo file does not exist", Toast.LENGTH_SHORT).show());
            return;
        }

        if (DROPBOX_ACCESS_TOKEN == null) {
            runOnUiThread(() -> Toast.makeText(MainActivity.this, "Error: Dropbox access token is null", Toast.LENGTH_SHORT).show());
            return;
        }

        DbxRequestConfig config = DbxRequestConfig.newBuilder("Decamera").build();
        DbxClientV2 client = new DbxClientV2(config, DROPBOX_ACCESS_TOKEN);

        try {
            String remotePath = "/Decamera/" + photoFile.getName();

            try (InputStream in = new FileInputStream(photoFile)) {
                client.files().uploadBuilder(remotePath)
                        .withMode(WriteMode.ADD)
                        .uploadAndFinish(in);

                runOnUiThread(() -> Toast.makeText(MainActivity.this, "Photo uploaded to Dropbox", Toast.LENGTH_SHORT).show());
            }

        } catch (Exception e) {
            e.printStackTrace();
            runOnUiThread(() -> Toast.makeText(MainActivity.this, "Error uploading to Dropbox: " + e.getMessage(), Toast.LENGTH_SHORT).show());
        }
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.captureButton) {
            captureImage();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if (requestCode == CAMERA_PERMISSION_REQUEST) {
            if (allPermissionsGranted()) {
                startCamera();
                getLastLocation();
            } else {
                runOnUiThread(() -> Toast.makeText(this, "Camera permissions not granted.", Toast.LENGTH_SHORT).show());
                finish();
            }
        }
    }
}
0

There are 0 best solutions below