Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ dependencies {

### Layout Setup

First, you need a parent `ViewGroup` that can vertically stack:
First, you need a parent `ViewGroup` that can vertically stack:

1. a behind-View to be revealed
2. a foreground-View to be scratched away
Expand Down Expand Up @@ -94,7 +94,7 @@ public class MainActivity extends Activity implements ScratchoffController.Thres
}
```

### Finding the `ScratchoffController`
### Finding the `ScratchoffController`

Find the `ScratchoffController` from the `ScratchableLayout` instances defined in the layout resource.

Expand All @@ -108,7 +108,7 @@ ScratchoffController.findByViewId(view, R.id.scratch_view)
// Find the ScratchoffController manually
((ScratchableLayout) activity.findViewById(R.id.scratch_view))
.getScratchoffController()
```
```

### Listening for threshold change / completion events

Expand Down Expand Up @@ -138,15 +138,15 @@ ScratchoffController.findByViewId(activity, R.id.scratch_view)

### Attaching the `ScratchoffController`

To start the processors and allow scratching, call `attach()` on the `ScratchoffController` instance.
To start the processors and allow scratching, call `attach()` on the `ScratchoffController` instance.

```java
ScratchoffController.findByViewId(activity, R.id.scratch_view)
...
.attach();
```

If the `ScratchableLayout` View has been restored, the dimensions match the persisted values, and state-restoration is enabled on the `ScratchoffController` instance, then attaching will attempt to restore the scratched path history from the cached state. If the restored state's threshold has already been reached, the content will be automatically cleared, regardless of desired clear animation behavior.
If the `ScratchableLayout` View has been restored, the dimensions match the persisted values, and state-restoration is enabled on the `ScratchoffController` instance, then attaching will attempt to restore the scratched path history from the cached state. If the restored state's threshold has already been reached, the content will be automatically cleared, regardless of desired clear animation behavior.

### Lifecycle

Expand All @@ -163,13 +163,13 @@ public void onDestroy(){

### Re-using the `ScratchoffController`

The `ScratchoffController` can be reset with the same call that started it: `ScratchController.attach()`.
The `ScratchoffController` can be reset with the same call that started it: `ScratchController.attach()`.

However, **the background color of your scratchable layout must be manually set back to something opaque before calling it**, as the `ScratchableLayoutDrawer` will set the background to transparent when scratching is enabled.

```java
public void onScratchThresholdReached(ScratchoffController controller) {
// Make sure to set the background of the foreground-View.
// Make sure to set the background of the foreground-View.
// Don't worry, it's hidden if it cleared or still clearing.
findViewById(R.id.scratch_view)
.setBackgroundColor(0xFF3C9ADF);
Expand Down Expand Up @@ -237,9 +237,10 @@ The size of the Bitmap used by the `ScratchoffThresholdProcessor` is determined

It is recommended that you calculate the positions of the desired regions by their relative positioning from the edges of the original Bitmap. e.g. left = 0.25 * bitmap.width

## Upgrading from Version 1.x to Version 2.0.0
## Migration Guides

Follow the [upgrade guide](https://github.com/jackpocket/android-scratchoff/raw/main/upgrade_1.x-2.0.md).
* [1.x-2.x](upgrade_1.x-2.0.md)
* [3.x-4.x](upgrade_3.x-4.0.md)

### Moved to MavenCentral

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ class MainActivity: AppCompatActivity(),
.setClearAnimationEnabled(true)
.setClearAnimationDuration(1, TimeUnit.SECONDS)
.setClearAnimationInterpolator(LinearInterpolator())
.setUsePreDrawOverGlobalLayoutEnabled(true)
// .setAttemptLastDitchPostForLayoutResolutionFailure(true)
.setKeepListeningForDrawUntilValidSizeDiscovered(true)
// .setTouchRadiusPx(25)
// .setThresholdAccuracyQuality(Quality.LOW)
// .setThresholdTargetRegionsProvider({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
Expand Down Expand Up @@ -62,13 +60,8 @@ public interface Delegate {

private Long activeClearTag = 0L;

private boolean usePreDrawOverGlobalLayoutEnabled = false;
private boolean attemptLastDitchPostForLayoutResolutionFailure = false;
private boolean keepListeningForDrawUntilValidSizeDiscovered = false;

private WeakReference<View> initializeLayoutTarget = new WeakReference<>(null);
private ViewTreeObserver.OnGlobalLayoutListener initializationGlobalLayoutListener;
private ViewTreeObserver.OnPreDrawListener initializationPreDrawListener;

public ScratchableLayoutDrawer(Delegate delegate) {
this.delegate = new WeakReference<>(delegate);
Expand Down Expand Up @@ -305,16 +298,6 @@ private void removeInitializationViewTreeObservers() {

this.initializationGlobalLayoutListener = null;
}

ViewTreeObserver.OnPreDrawListener preDrawListener = this.initializationPreDrawListener;

if (preDrawListener != null) {
layoutTarget
.getViewTreeObserver()
.removeOnPreDrawListener(preDrawListener);

this.initializationPreDrawListener = null;
}
}

public void clear(boolean animationEnabled) {
Expand Down Expand Up @@ -398,58 +381,27 @@ protected void hideAndMarkScratchableSurfaceViewCleared() {
private void deferRunnableUntilViewIsLaidOut(final View view, final Runnable runnable) {
this.initializeLayoutTarget = new WeakReference<>(view);

if (usePreDrawOverGlobalLayoutEnabled) {
deferRunnableWithPreDrawListener(view, runnable);
}
else {
deferRunnableWithGlobalLayoutListener(view, runnable);
}
deferRunnableWithGlobalLayoutListener(view, runnable);

view.requestLayout();
}

private void deferRunnableWithPreDrawListener(final View view, final Runnable runnable) {
ViewTreeObserver.OnPreDrawListener preDrawListenerForInit = new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
boolean sizeValid = isViewSizeValidForInitialization(view);

if (keepListeningForDrawUntilValidSizeDiscovered && !sizeValid) {
return true;
}

ScratchableLayoutDrawer.this.initializationPreDrawListener = null;

triggerOrPostRunnableOnLaidOut(runnable, sizeValid);

view
.getViewTreeObserver()
.removeOnPreDrawListener(this);

return true;
}
};

view
.getViewTreeObserver()
.addOnPreDrawListener(preDrawListenerForInit);

this.initializationPreDrawListener = preDrawListenerForInit;
}

private void deferRunnableWithGlobalLayoutListener(final View view, final Runnable runnable) {
ViewTreeObserver.OnGlobalLayoutListener globalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
boolean sizeValid = isViewSizeValidForInitialization(view);

if (keepListeningForDrawUntilValidSizeDiscovered && !sizeValid) {
if (!sizeValid) {
return;
}

ScratchableLayoutDrawer.this.initializationGlobalLayoutListener = null;

triggerOrPostRunnableOnLaidOut(runnable, sizeValid);
if (runnable != null) {
runnable.run();
}

removeGlobalLayoutListener(view, this);
}
};
Expand All @@ -461,31 +413,6 @@ public void onGlobalLayout() {
this.initializationGlobalLayoutListener = globalLayoutListener;
}

/**
* This function will be removed in 4.x, given {@link attemptLastDitchPostForLayoutResolutionFailure}
* did more harm than good, and replaced with directly calling ``run`` on the ``Runnable``.
*/
protected void triggerOrPostRunnableOnLaidOut(
Runnable runnable,
boolean sizeValidForInitialization
) {

if (runnable == null) {
return;
}

if (attemptLastDitchPostForLayoutResolutionFailure) {
if (!sizeValidForInitialization) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(runnable);

return;
}
}

runnable.run();
}

private boolean isViewSizeValidForInitialization(View view) {
return 0 < view.getWidth() && 0 < view.getHeight();
}
Expand Down Expand Up @@ -516,30 +443,4 @@ public ScratchableLayoutDrawer setClearAnimationInterpolator(Interpolator clearA

return this;
}

@SuppressWarnings("WeakerAccess")
public ScratchableLayoutDrawer setUsePreDrawOverGlobalLayoutEnabled(boolean usePreDrawOverGlobalLayoutEnabled) {
this.usePreDrawOverGlobalLayoutEnabled = usePreDrawOverGlobalLayoutEnabled;

return this;
}

@SuppressWarnings("WeakerAccess")
public ScratchableLayoutDrawer setAttemptLastDitchPostForLayoutResolutionFailure(
boolean attemptLastDitchPostForLayoutResolutionFailure
) {

this.attemptLastDitchPostForLayoutResolutionFailure = attemptLastDitchPostForLayoutResolutionFailure;

return this;
}

public ScratchableLayoutDrawer setKeepListeningForDrawUntilValidSizeDiscovered(
boolean keepListeningForDrawUntilValidSizeDiscovered
) {

this.keepListeningForDrawUntilValidSizeDiscovered = keepListeningForDrawUntilValidSizeDiscovered;

return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,6 @@ public interface ThresholdChangedListener {
private final LinkedBlockingQueue<ScratchPathPoint> history = new LinkedBlockingQueue<ScratchPathPoint>();
private boolean stateRestorationEnabled;

private boolean usePreDrawOverGlobalLayoutEnabled = false;
private boolean attemptLastDitchPostForLayoutResolutionFailure = false;
private boolean keepListeningForDrawUntilValidSizeDiscovered = false;

/**
* Create a new {@link ScratchoffController} instance targeting a scratchable layout.
*/
Expand Down Expand Up @@ -156,10 +152,7 @@ public ScratchoffController attach() {
protected ScratchableLayoutDrawer createLayoutDrawer() {
return new ScratchableLayoutDrawer(this)
.setClearAnimationDurationMs(clearAnimationDurationMs)
.setClearAnimationInterpolator(clearAnimationInterpolator)
.setUsePreDrawOverGlobalLayoutEnabled(usePreDrawOverGlobalLayoutEnabled)
.setAttemptLastDitchPostForLayoutResolutionFailure(attemptLastDitchPostForLayoutResolutionFailure)
.setKeepListeningForDrawUntilValidSizeDiscovered(keepListeningForDrawUntilValidSizeDiscovered);
.setClearAnimationInterpolator(clearAnimationInterpolator);
}

protected ScratchoffThresholdProcessor createThresholdProcessor() {
Expand Down Expand Up @@ -487,56 +480,6 @@ public ScratchoffController setStateRestorationEnabled(boolean stateRestorationE
return this;
}

/**
* Set whether or not to use the new {@link android.view.ViewTreeObserver.OnPreDrawListener}
* code paths to determine the layout sizing, instead of the original
* {@link android.view.ViewTreeObserver.OnGlobalLayoutListener} implementation.
* This is in attempt to fix #19 caused by the width or height of the View being
* zero when attempting to create the scratchable {@link Bitmap} instances.
* The default for this value is false for the original (crashing) behavior.
*/
public ScratchoffController setUsePreDrawOverGlobalLayoutEnabled(boolean usePreDrawOverGlobalLayoutEnabled) {
this.usePreDrawOverGlobalLayoutEnabled = usePreDrawOverGlobalLayoutEnabled;

return this;
}

/**
* Set whether or not to attempt one final last-ditch {@link android.os.Handler#post} on
* the main Thread when determining the layout sizing of our {@link #layoutDrawer} if
* our {@link android.view.ViewTreeObserver} attempt ran while the {@link #scratchableLayout}'s
* width or height is still zero.
* This is in attempt to fix #19 caused by the width or height of the View being
* zero when attempting to create the scratchable {@link Bitmap} instances.
* The default for this value is false for the original (crashing) behavior.
*/
public ScratchoffController setAttemptLastDitchPostForLayoutResolutionFailure(
boolean attemptLastDitchPostForLayoutResolutionFailure
) {

this.attemptLastDitchPostForLayoutResolutionFailure = attemptLastDitchPostForLayoutResolutionFailure;

return this;
}

/**
* Set whether or not to continue listening for {@link android.view.ViewTreeObserver.OnGlobalLayoutListener}
* or {@link android.view.ViewTreeObserver.OnPreDrawListener} events when the callbacks are
* triggered with a size that is invalid for initialization. Setting this to true will override the
* behavior for {@link setAttemptLastDitchPostForLayoutResolutionFailure}.
* This is in attempt to fix #19 caused by the width or height of the View being
* zero when attempting to create the scratchable {@link Bitmap} instances.
* The default for this value is false for the original (crashing) behavior.
*/
public ScratchoffController setKeepListeningForDrawUntilValidSizeDiscovered(
boolean keepListeningForDrawUntilValidSizeDiscovered
) {

this.keepListeningForDrawUntilValidSizeDiscovered = keepListeningForDrawUntilValidSizeDiscovered;

return this;
}

public View getViewBehind() {
return behindView.get();
}
Expand Down
Loading