88import android .graphics .Paint ;
99import android .graphics .Point ;
1010import android .net .Uri ;
11+
1112import androidx .annotation .IntDef ;
1213import androidx .annotation .NonNull ;
1314import androidx .annotation .StringDef ;
15+
16+ import android .os .Build ;
17+ import android .os .Handler ;
18+ import android .os .HandlerThread ;
1419import android .util .Base64 ;
1520import android .util .Log ;
1621import android .view .TextureView ;
1722import android .view .View ;
1823import android .view .ViewGroup ;
1924import android .widget .ScrollView ;
2025
26+ import com .facebook .react .bridge .LifecycleEventListener ;
2127import com .facebook .react .bridge .Promise ;
2228import com .facebook .react .bridge .ReactApplicationContext ;
2329import com .facebook .react .uimanager .NativeViewHierarchyManager ;
4753/**
4854 * Snapshot utility class allow to screenshot a view.
4955 */
50- public class ViewShot implements UIBlock {
56+ public class ViewShot implements UIBlock , LifecycleEventListener {
5157 //region Constants
5258 /**
5359 * Tag fort Class logs.
@@ -66,6 +72,30 @@ public class ViewShot implements UIBlock {
6672 */
6773 private static final int ARGB_SIZE = 4 ;
6874
75+ private HandlerThread mBgThread ;
76+ private Handler mBgHandler ;
77+
78+ @ Override
79+ public void onHostResume () {
80+
81+ }
82+
83+ @ Override
84+ public void onHostPause () {
85+
86+ }
87+
88+ @ Override
89+ public void onHostDestroy () {
90+ this .reactContext .removeLifecycleEventListener (this );
91+ mBgHandler .post (new Runnable () {
92+ @ Override
93+ public void run () {
94+ cleanup ();
95+ }
96+ });
97+ }
98+
6999 @ SuppressWarnings ("WeakerAccess" )
70100 @ IntDef ({Formats .JPEG , Formats .PNG , Formats .WEBP , Formats .RAW })
71101 public @interface Formats {
@@ -157,44 +187,68 @@ public ViewShot(
157187 this .reactContext = reactContext ;
158188 this .currentActivity = currentActivity ;
159189 this .promise = promise ;
190+
191+ reactContext .addLifecycleEventListener (this );
192+
193+ // bg hanadler for non UI heavy work
194+ mBgThread = new HandlerThread ("RNViewShot-Handler-Thread" );
195+ mBgThread .start ();
196+ mBgHandler = new Handler (mBgThread .getLooper ());
160197 }
161198 //endregion
162199
163- //region Overrides
164- @ Override
165- public void execute (NativeViewHierarchyManager nativeViewHierarchyManager ) {
166- final View view ;
200+ private void cleanup () {
201+ if (mBgThread != null ) {
202+ if (Build .VERSION .SDK_INT < 18 ) {
203+ mBgThread .quit ();
204+ } else {
205+ mBgThread .quitSafely ();
206+ }
167207
168- if (tag == -1 ) {
169- view = currentActivity .getWindow ().getDecorView ().findViewById (android .R .id .content );
170- } else {
171- view = nativeViewHierarchyManager .resolveView (tag );
208+ mBgThread = null ;
172209 }
210+ }
173211
174- if (view == null ) {
175- Log .e (TAG , "No view found with reactTag: " + tag , new AssertionError ());
176- promise .reject (ERROR_UNABLE_TO_SNAPSHOT , "No view found with reactTag: " + tag );
177- return ;
178- }
212+ //region Overrides
213+ @ Override
214+ public void execute (final NativeViewHierarchyManager nativeViewHierarchyManager ) {
215+ mBgHandler .post (new Runnable () {
216+ @ Override
217+ public void run () {
218+ final View view ;
219+
220+ if (tag == -1 ) {
221+ view = currentActivity .getWindow ().getDecorView ().findViewById (android .R .id .content );
222+ } else {
223+ view = nativeViewHierarchyManager .resolveView (tag );
224+ }
179225
180- try {
181- final ReusableByteArrayOutputStream stream = new ReusableByteArrayOutputStream (outputBuffer );
182- stream .setSize (proposeSize (view ));
183- outputBuffer = stream .innerBuffer ();
184-
185- if (Results .TEMP_FILE .equals (result ) && Formats .RAW == this .format ) {
186- saveToRawFileOnDevice (view );
187- } else if (Results .TEMP_FILE .equals (result ) && Formats .RAW != this .format ) {
188- saveToTempFileOnDevice (view );
189- } else if (Results .BASE_64 .equals (result ) || Results .ZIP_BASE_64 .equals (result )) {
190- saveToBase64String (view );
191- } else if (Results .DATA_URI .equals (result )) {
192- saveToDataUriString (view );
226+ if (view == null ) {
227+ Log .e (TAG , "No view found with reactTag: " + tag , new AssertionError ());
228+ promise .reject (ERROR_UNABLE_TO_SNAPSHOT , "No view found with reactTag: " + tag );
229+ return ;
230+ }
231+
232+ try {
233+ final ReusableByteArrayOutputStream stream = new ReusableByteArrayOutputStream (outputBuffer );
234+ stream .setSize (proposeSize (view ));
235+ outputBuffer = stream .innerBuffer ();
236+
237+ if (Results .TEMP_FILE .equals (result ) && Formats .RAW == format ) {
238+ saveToRawFileOnDevice (view );
239+ } else if (Results .TEMP_FILE .equals (result ) && Formats .RAW != format ) {
240+ saveToTempFileOnDevice (view );
241+ } else if (Results .BASE_64 .equals (result ) || Results .ZIP_BASE_64 .equals (result )) {
242+ saveToBase64String (view );
243+ } else if (Results .DATA_URI .equals (result )) {
244+ saveToDataUriString (view );
245+ }
246+ } catch (final Throwable ex ) {
247+ Log .e (TAG , "Failed to capture view snapshot" , ex );
248+ promise .reject (ERROR_UNABLE_TO_SNAPSHOT , "Failed to capture view snapshot" );
249+ }
193250 }
194- } catch (final Throwable ex ) {
195- Log .e (TAG , "Failed to capture view snapshot" , ex );
196- promise .reject (ERROR_UNABLE_TO_SNAPSHOT , "Failed to capture view snapshot" );
197- }
251+ });
198252 }
199253 //endregion
200254
0 commit comments