Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change the white screen to custom design #34

Open
bajjajjrajjesh opened this issue Dec 5, 2023 · 9 comments
Open

change the white screen to custom design #34

bajjajjrajjesh opened this issue Dec 5, 2023 · 9 comments

Comments

@bajjajjrajjesh
Copy link

Currently when screenshot is taken it shows the white screenshot, can we change that design to be custom as my needs?

@riamon-v
Copy link

I'm also interested in this

@applabstudio
Copy link

Has anyone succeeded? @bajjajjrajjesh @riamon-v @killserver

@progtarek
Copy link

any updates?

@amalStorilabs
Copy link

Any updates or example , tried this : if(!DEV) RNPreventScreenshot.enableSecureView(imgUri);
but not working , still coming as a white screen

@riamon-v
Copy link

riamon-v commented Apr 9, 2024

Here is what we've made with @meierrap:

import LockScreenImage from 'path-to-your-image';

const setScreenshotDisabled = async () => {
  const preventScreenImage = Image.resolveAssetSource(LockScreenImage).uri;

  RNPreventScreenshot.enabled(true);
  RNPreventScreenshot.enableSecureView(preventScreenImage);
};

Works only on IOS, we had to implement it natively on android

Then for android we did a little patch package of the library adding this in RNScreenshotPreventModule.java:

+ public void enableSecureView(String imagePath) {

And in the MainActivity.java

import expo.modules.ReactActivityDelegateWrapper;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Bundle; // here
import android.util.Log;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.RelativeLayout;
import android.widget.ImageView;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactActivityDelegate;

public class MainActivity extends ReactActivity {

  private RelativeLayout overlayLayout;
  private boolean secureFlagWasSet;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(null);

    overlayLayout = new RelativeLayout(MainActivity.this);

    overlayLayout.setBackgroundColor(Color.parseColor("#FFFFFF"));
    // Create an ImageView
    ImageView imageView = new ImageView(MainActivity.this);
    RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(
        RelativeLayout.LayoutParams.WRAP_CONTENT, // Use WRAP_CONTENT for both width and height
        RelativeLayout.LayoutParams.WRAP_CONTENT);
    imageParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
    imageParams.width = (int) (getResources().getDisplayMetrics().widthPixels * 0.4); // Set width to 40% of screen

    imageView.setLayoutParams(imageParams);

    // Set image resource
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lock_screen_logo);

    // Calculate height to maintain aspect ratio
    int imageHeight = (int) (bitmap.getHeight() * (imageParams.width / (float) bitmap.getWidth()));

    // Set the scaled bitmap to the ImageView
    Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, imageParams.width, imageHeight, true);
    imageView.setImageBitmap(scaledBitmap);

    // Add ImageView to RelativeLayout
    overlayLayout.addView(imageView);
  }

  /**
   * Triggered when the app go to background
   */
  @Override
  protected void onPause() {
    runOnUiThread(new Runnable() {
      @Override
      public void run() {
        ViewGroup rootView = (ViewGroup) getWindow().getDecorView().getRootView();
        Log.d("PAUSING", "Before addView");
        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT);
        rootView.addView(overlayLayout, layoutParams);

        int flags = getWindow().getAttributes().flags;
        if ((flags & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
          getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
          secureFlagWasSet = true;
        } else {
          secureFlagWasSet = false;
        }
      }
    });

    super.onPause();
  }

  /**
   * Triggered when the app go to foreground
   */
  @Override
  protected void onResume() {
    runOnUiThread(new Runnable() {
      @Override
      public void run() {
        ViewGroup rootView = (ViewGroup) getWindow().getDecorView().getRootView();
        rootView.removeView(overlayLayout);

        if (secureFlagWasSet) {
          getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
          secureFlagWasSet = false;
        }
      }
    });

    super.onResume();
  }

  /**
   * Returns the name of the main component registered from JavaScript. This is
   * used to schedule
   * rendering of the component.
   */
  @Override
  protected String getMainComponentName() {
    return "appName"; // Here your actual app Name
  }

  /**
   * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util
   * class {@link
   * DefaultReactActivityDelegate} which allows you to easily enable Fabric and
   * Concurrent React
   * (aka React 18) with two boolean flags.
   */
  @Override
  protected ReactActivityDelegate createReactActivityDelegate() {
    return new ReactActivityDelegateWrapper(this, BuildConfig.IS_NEW_ARCHITECTURE_ENABLED,
        new DefaultReactActivityDelegate(
            this,
            getMainComponentName(),
            // If you opted-in for the New Architecture, we enable the Fabric Renderer.
            DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled
            // If you opted-in for the New Architecture, we enable Concurrent React (i.e.
            // React 18).
            DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled
        ));
  }
}

@killserver
Copy link
Owner

pls check 1.2.1 version

@declanelcocks
Copy link

@killserver @riamon-v are you able to get this working with an image/background colour on Android? 🤔 When switching between apps on Android I only see a blank white screen. After digging into the source code it seems that Android will hard-code a white screen (e.g. here) on top of your app when using the FLAG_SECURE to disable screenshots which isn't able to be overridden.

@meierrap
Copy link

meierrap commented Oct 4, 2024

@declanelcocks I just saw that this feature doesn't work on Android 11 and above. I have looked for a fix with no success. If anyone has an idea, it would be appreciated

@RVassili
Copy link
Contributor

@declanelcocks please check #54

This has issue on it's own, like:

  • the FLAG_SECURE is still be used to prevent screen recording or screenshot
  • by default, our activity lost focus when an alert or a permission is prompt, I tweaked it a little to detect it so if your app goes in background when an alert is prompted you will still have the white screen and not your custom layout

Please note that the tweak is not working for every alerts as well, for instance biometric prompt will be consider as if the app is in background. I haven't found a proper way to check if the current fragment prompted is a biometry fragment, you can check the class name but beware this might change on new android release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants