18f8784c2622bc9900e772853bced17d091c4daf5Jason Monk/*
28f8784c2622bc9900e772853bced17d091c4daf5Jason Monk * Copyright 2018 The Android Open Source Project
38f8784c2622bc9900e772853bced17d091c4daf5Jason Monk *
48f8784c2622bc9900e772853bced17d091c4daf5Jason Monk * Licensed under the Apache License, Version 2.0 (the "License");
58f8784c2622bc9900e772853bced17d091c4daf5Jason Monk * you may not use this file except in compliance with the License.
68f8784c2622bc9900e772853bced17d091c4daf5Jason Monk * You may obtain a copy of the License at
78f8784c2622bc9900e772853bced17d091c4daf5Jason Monk *
88f8784c2622bc9900e772853bced17d091c4daf5Jason Monk *      http://www.apache.org/licenses/LICENSE-2.0
98f8784c2622bc9900e772853bced17d091c4daf5Jason Monk *
108f8784c2622bc9900e772853bced17d091c4daf5Jason Monk * Unless required by applicable law or agreed to in writing, software
118f8784c2622bc9900e772853bced17d091c4daf5Jason Monk * distributed under the License is distributed on an "AS IS" BASIS,
128f8784c2622bc9900e772853bced17d091c4daf5Jason Monk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138f8784c2622bc9900e772853bced17d091c4daf5Jason Monk * See the License for the specific language governing permissions and
148f8784c2622bc9900e772853bced17d091c4daf5Jason Monk * limitations under the License.
158f8784c2622bc9900e772853bced17d091c4daf5Jason Monk */
168f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
1785ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikaspackage androidx.slice.render;
188f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
198f8784c2622bc9900e772853bced17d091c4daf5Jason Monkimport static android.view.View.MeasureSpec.makeMeasureSpec;
208f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
218f8784c2622bc9900e772853bced17d091c4daf5Jason Monkimport android.app.Activity;
228f8784c2622bc9900e772853bced17d091c4daf5Jason Monkimport android.app.ProgressDialog;
238f8784c2622bc9900e772853bced17d091c4daf5Jason Monkimport android.graphics.Bitmap;
248f8784c2622bc9900e772853bced17d091c4daf5Jason Monkimport android.graphics.Canvas;
2507a4a56611cc044fd48b052db05aea332201216eJason Monkimport android.net.Uri;
268f8784c2622bc9900e772853bced17d091c4daf5Jason Monkimport android.os.Handler;
278f8784c2622bc9900e772853bced17d091c4daf5Jason Monkimport android.util.Log;
288f8784c2622bc9900e772853bced17d091c4daf5Jason Monkimport android.util.TypedValue;
298f8784c2622bc9900e772853bced17d091c4daf5Jason Monkimport android.view.LayoutInflater;
308f8784c2622bc9900e772853bced17d091c4daf5Jason Monkimport android.view.View;
318f8784c2622bc9900e772853bced17d091c4daf5Jason Monkimport android.view.ViewGroup;
328f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
33cdfd2c2d98a8ff23b18ef3a960a5bb5c90a67d43Mady Mellorimport androidx.recyclerview.widget.RecyclerView;
3485ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikasimport androidx.slice.Slice;
3585ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikasimport androidx.slice.SliceProvider;
3607a4a56611cc044fd48b052db05aea332201216eJason Monkimport androidx.slice.SliceUtils;
3785ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikasimport androidx.slice.view.test.R;
3885ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikasimport androidx.slice.widget.SliceLiveData;
3985ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikasimport androidx.slice.widget.SliceView;
408f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
4107a4a56611cc044fd48b052db05aea332201216eJason Monkimport java.io.ByteArrayInputStream;
4207a4a56611cc044fd48b052db05aea332201216eJason Monkimport java.io.ByteArrayOutputStream;
4307a4a56611cc044fd48b052db05aea332201216eJason Monkimport java.io.File;
4407a4a56611cc044fd48b052db05aea332201216eJason Monkimport java.io.FileOutputStream;
4507a4a56611cc044fd48b052db05aea332201216eJason Monkimport java.util.concurrent.CountDownLatch;
4607a4a56611cc044fd48b052db05aea332201216eJason Monkimport java.util.concurrent.ExecutorService;
4707a4a56611cc044fd48b052db05aea332201216eJason Monkimport java.util.concurrent.Executors;
4807a4a56611cc044fd48b052db05aea332201216eJason Monk
498f8784c2622bc9900e772853bced17d091c4daf5Jason Monkpublic class SliceRenderer {
508f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
518f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    private static final String TAG = "SliceRenderer";
526d23526ab0ebfee286a772d3038fb194a88c0c0eJason Monk    public static final String SCREENSHOT_DIR = "slice-screenshots";
5307a4a56611cc044fd48b052db05aea332201216eJason Monk
5407a4a56611cc044fd48b052db05aea332201216eJason Monk    private static final int MAX_CONCURRENT = 5;
5507a4a56611cc044fd48b052db05aea332201216eJason Monk
568f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    private static File sScreenshotDirectory;
578f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
5807a4a56611cc044fd48b052db05aea332201216eJason Monk    private final Object mRenderLock = new Object();
5907a4a56611cc044fd48b052db05aea332201216eJason Monk
608f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    private final Activity mContext;
618f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    private final View mLayout;
628f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    private final SliceView mSV1;
638f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    private final SliceView mSV2;
648f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    private final SliceView mSV3;
658f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    private final ViewGroup mParent;
668f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    private final Handler mHandler;
678f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    private final SliceCreator mSliceCreator;
688f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    private CountDownLatch mDoneLatch;
698f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
708f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    public SliceRenderer(Activity context) {
718f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        mContext = context;
728f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        mParent = new ViewGroup(mContext) {
738f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            @Override
748f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            protected void onLayout(boolean changed, int l, int t, int r, int b) {
750f591e3e9b5147d04d6379fd2b7ba6cab66f1b4fMady Mellor                int width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1000,
768f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                        mContext.getResources().getDisplayMetrics());
77cdfd2c2d98a8ff23b18ef3a960a5bb5c90a67d43Mady Mellor                int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 330,
788f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                        mContext.getResources().getDisplayMetrics());
798f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                mLayout.measure(makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
808f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                        makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
818f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                mLayout.layout(0, 0, width, height);
828f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            }
838f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
848f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            @Override
858f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
868f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                return false;
878f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            }
888f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        };
898f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        mLayout = LayoutInflater.from(context).inflate(R.layout.render_layout, null);
908f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        mSV1 = mLayout.findViewById(R.id.sv1);
918f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        mSV1.setMode(SliceView.MODE_SHORTCUT);
928f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        mSV2 = mLayout.findViewById(R.id.sv2);
938f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        mSV2.setMode(SliceView.MODE_SMALL);
948f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        mSV3 = mLayout.findViewById(R.id.sv3);
958f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        mSV3.setMode(SliceView.MODE_LARGE);
968f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        disableAnims(mLayout);
978f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        mHandler = new Handler();
988f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        ((ViewGroup) mContext.getWindow().getDecorView()).addView(mParent);
998f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        mParent.addView(mLayout);
1008f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
1018f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        mSliceCreator = new SliceCreator(mContext);
1028f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    }
1038f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
1048f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    private void disableAnims(View view) {
1058f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        if (view instanceof RecyclerView) {
1068f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            ((RecyclerView) view).setItemAnimator(null);
1078f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        }
1088f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        if (view instanceof ViewGroup) {
1098f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            ViewGroup viewGroup = (ViewGroup) view;
1108f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            for (int i = 0; i < viewGroup.getChildCount(); i++) {
1118f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                disableAnims(viewGroup.getChildAt(i));
1128f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            }
1138f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        }
1148f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    }
1158f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
1168f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
1176d23526ab0ebfee286a772d3038fb194a88c0c0eJason Monk    private File getScreenshotDirectory() {
1188f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        if (sScreenshotDirectory == null) {
11938db00ef5d4b5a9d0d171f1bb15a5df567d448f6Jason Monk            File storage = mContext.getFilesDir();
1208f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            sScreenshotDirectory = new File(storage, SCREENSHOT_DIR);
1218f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            if (!sScreenshotDirectory.exists()) {
1228f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                if (!sScreenshotDirectory.mkdirs()) {
1238f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                    throw new RuntimeException(
1248f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                            "Failed to create a screenshot directory.");
1258f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                }
1268f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            }
1278f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        }
1288f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        return sScreenshotDirectory;
1298f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    }
1308f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
1318f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
1328f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    private void doRender() {
13307a4a56611cc044fd48b052db05aea332201216eJason Monk        final File output = getScreenshotDirectory();
1348f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        if (!output.exists()) {
1358f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            output.mkdir();
1368f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        }
13707a4a56611cc044fd48b052db05aea332201216eJason Monk        mDoneLatch = new CountDownLatch(SliceCreator.URI_PATHS.length * 2 + 2);
13807a4a56611cc044fd48b052db05aea332201216eJason Monk
13907a4a56611cc044fd48b052db05aea332201216eJason Monk        ExecutorService executor = Executors.newFixedThreadPool(5);
14007a4a56611cc044fd48b052db05aea332201216eJason Monk        for (final String slice : SliceCreator.URI_PATHS) {
14107a4a56611cc044fd48b052db05aea332201216eJason Monk            final Slice s = mSliceCreator.onBindSlice(SliceCreator.getUri(slice, mContext));
14207a4a56611cc044fd48b052db05aea332201216eJason Monk
14307a4a56611cc044fd48b052db05aea332201216eJason Monk            executor.execute(new Runnable() {
14407a4a56611cc044fd48b052db05aea332201216eJason Monk                @Override
14507a4a56611cc044fd48b052db05aea332201216eJason Monk                public void run() {
14607a4a56611cc044fd48b052db05aea332201216eJason Monk                    doRender(slice, s, new File(output, String.format("%s.png", slice)),
14707a4a56611cc044fd48b052db05aea332201216eJason Monk                            true /* scrollable */);
14807a4a56611cc044fd48b052db05aea332201216eJason Monk                }
14907a4a56611cc044fd48b052db05aea332201216eJason Monk            });
15007a4a56611cc044fd48b052db05aea332201216eJason Monk            final Slice serialized = serAndUnSer(s);
15107a4a56611cc044fd48b052db05aea332201216eJason Monk            executor.execute(new Runnable() {
15207a4a56611cc044fd48b052db05aea332201216eJason Monk                @Override
15307a4a56611cc044fd48b052db05aea332201216eJason Monk                public void run() {
15407a4a56611cc044fd48b052db05aea332201216eJason Monk                    doRender(slice + "-ser", serialized, new File(output, String.format(
15507a4a56611cc044fd48b052db05aea332201216eJason Monk                            "%s-serialized.png", slice)), true /* scrollable */);
15607a4a56611cc044fd48b052db05aea332201216eJason Monk                }
15707a4a56611cc044fd48b052db05aea332201216eJason Monk            });
15853380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor            if (slice.equals("wifi") || slice.equals("wifi2")) {
15953380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor                // Test scrolling
16007a4a56611cc044fd48b052db05aea332201216eJason Monk                executor.execute(new Runnable() {
16107a4a56611cc044fd48b052db05aea332201216eJason Monk                    @Override
16207a4a56611cc044fd48b052db05aea332201216eJason Monk                    public void run() {
16307a4a56611cc044fd48b052db05aea332201216eJason Monk                        doRender(slice + "-ns", s, new File(output, String.format(
16407a4a56611cc044fd48b052db05aea332201216eJason Monk                                "%s-no-scroll.png", slice)), false /* scrollable */);
16507a4a56611cc044fd48b052db05aea332201216eJason Monk                    }
16607a4a56611cc044fd48b052db05aea332201216eJason Monk                });
16753380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor            }
1688f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        }
16907a4a56611cc044fd48b052db05aea332201216eJason Monk        try {
17007a4a56611cc044fd48b052db05aea332201216eJason Monk            mDoneLatch.await();
17107a4a56611cc044fd48b052db05aea332201216eJason Monk        } catch (InterruptedException e) {
17207a4a56611cc044fd48b052db05aea332201216eJason Monk        }
1738f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        Log.d(TAG, "Wrote render to " + output.getAbsolutePath());
1748f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        mContext.runOnUiThread(new Runnable() {
1758f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            @Override
1768f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            public void run() {
1778f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                ((ViewGroup) mParent.getParent()).removeView(mParent);
1788f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            }
1798f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        });
18007a4a56611cc044fd48b052db05aea332201216eJason Monk    }
18107a4a56611cc044fd48b052db05aea332201216eJason Monk
18207a4a56611cc044fd48b052db05aea332201216eJason Monk    private Slice serAndUnSer(Slice s) {
1838f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        try {
18407a4a56611cc044fd48b052db05aea332201216eJason Monk            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
18507a4a56611cc044fd48b052db05aea332201216eJason Monk            SliceUtils.serializeSlice(s, mContext, outputStream, "UTF-8",
18607a4a56611cc044fd48b052db05aea332201216eJason Monk                    new SliceUtils.SerializeOptions()
18707a4a56611cc044fd48b052db05aea332201216eJason Monk                            .setImageMode(SliceUtils.SerializeOptions.MODE_CONVERT)
18807a4a56611cc044fd48b052db05aea332201216eJason Monk                            .setActionMode(SliceUtils.SerializeOptions.MODE_CONVERT));
18907a4a56611cc044fd48b052db05aea332201216eJason Monk
19007a4a56611cc044fd48b052db05aea332201216eJason Monk            byte[] bytes = outputStream.toByteArray();
19138db00ef5d4b5a9d0d171f1bb15a5df567d448f6Jason Monk            Log.d(TAG, "Serialized: " + new String(bytes));
19207a4a56611cc044fd48b052db05aea332201216eJason Monk            ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
19307a4a56611cc044fd48b052db05aea332201216eJason Monk            return SliceUtils.parseSlice(mContext, inputStream, "UTF-8",
19407a4a56611cc044fd48b052db05aea332201216eJason Monk                    new SliceUtils.SliceActionListener() {
19507a4a56611cc044fd48b052db05aea332201216eJason Monk                        @Override
19638db00ef5d4b5a9d0d171f1bb15a5df567d448f6Jason Monk                        public void onSliceAction(Uri actionUri) { }
19707a4a56611cc044fd48b052db05aea332201216eJason Monk                    });
19807a4a56611cc044fd48b052db05aea332201216eJason Monk        } catch (Exception e) {
19907a4a56611cc044fd48b052db05aea332201216eJason Monk            throw new RuntimeException(e);
2008f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        }
2018f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    }
2028f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
20307a4a56611cc044fd48b052db05aea332201216eJason Monk    private void doRender(final String slice, final Slice s, final File file,
20407a4a56611cc044fd48b052db05aea332201216eJason Monk            final boolean scrollable) {
2058f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        Log.d(TAG, "Rendering " + slice + " to " + file.getAbsolutePath());
2068f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
20707a4a56611cc044fd48b052db05aea332201216eJason Monk        try {
20807a4a56611cc044fd48b052db05aea332201216eJason Monk            final CountDownLatch l = new CountDownLatch(1);
20907a4a56611cc044fd48b052db05aea332201216eJason Monk            final Bitmap[] b = new Bitmap[1];
21007a4a56611cc044fd48b052db05aea332201216eJason Monk            synchronized (mRenderLock) {
21107a4a56611cc044fd48b052db05aea332201216eJason Monk                mContext.runOnUiThread(new Runnable() {
2128f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                    @Override
21307a4a56611cc044fd48b052db05aea332201216eJason Monk                    public void run() {
21407a4a56611cc044fd48b052db05aea332201216eJason Monk                        mSV1.setSlice(s);
21507a4a56611cc044fd48b052db05aea332201216eJason Monk                        mSV2.setSlice(s);
21607a4a56611cc044fd48b052db05aea332201216eJason Monk                        mSV3.setSlice(s);
21707a4a56611cc044fd48b052db05aea332201216eJason Monk                        mSV3.setScrollable(scrollable);
21807a4a56611cc044fd48b052db05aea332201216eJason Monk                        mSV1.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
2198f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                            @Override
22007a4a56611cc044fd48b052db05aea332201216eJason Monk                            public void onLayoutChange(View v, int left, int top, int right,
22107a4a56611cc044fd48b052db05aea332201216eJason Monk                                    int bottom,
22207a4a56611cc044fd48b052db05aea332201216eJason Monk                                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
22307a4a56611cc044fd48b052db05aea332201216eJason Monk                                mSV1.removeOnLayoutChangeListener(this);
22407a4a56611cc044fd48b052db05aea332201216eJason Monk                                mSV1.postDelayed(new Runnable() {
22507a4a56611cc044fd48b052db05aea332201216eJason Monk                                    @Override
22607a4a56611cc044fd48b052db05aea332201216eJason Monk                                    public void run() {
22707a4a56611cc044fd48b052db05aea332201216eJason Monk                                        Log.d(TAG, "Drawing " + slice);
22807a4a56611cc044fd48b052db05aea332201216eJason Monk                                        b[0] = Bitmap.createBitmap(mLayout.getMeasuredWidth(),
22907a4a56611cc044fd48b052db05aea332201216eJason Monk                                                mLayout.getMeasuredHeight(),
23007a4a56611cc044fd48b052db05aea332201216eJason Monk                                                Bitmap.Config.ARGB_8888);
23107a4a56611cc044fd48b052db05aea332201216eJason Monk
23207a4a56611cc044fd48b052db05aea332201216eJason Monk                                        mLayout.draw(new Canvas(b[0]));
23307a4a56611cc044fd48b052db05aea332201216eJason Monk                                        l.countDown();
23407a4a56611cc044fd48b052db05aea332201216eJason Monk                                    }
23507a4a56611cc044fd48b052db05aea332201216eJason Monk                                }, 10);
2368f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                            }
23707a4a56611cc044fd48b052db05aea332201216eJason Monk                        });
2388f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                    }
2398f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                });
24007a4a56611cc044fd48b052db05aea332201216eJason Monk                l.await();
2418f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            }
24207a4a56611cc044fd48b052db05aea332201216eJason Monk            doCompress(slice, b[0], new FileOutputStream(file));
24307a4a56611cc044fd48b052db05aea332201216eJason Monk        } catch (Exception e) {
24407a4a56611cc044fd48b052db05aea332201216eJason Monk            throw new RuntimeException(e);
2458f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        }
2468f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    }
2478f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
2488f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    private void doCompress(final String slice, final Bitmap b, final FileOutputStream s) {
24907a4a56611cc044fd48b052db05aea332201216eJason Monk        Log.d(TAG, "Compressing " + slice);
25007a4a56611cc044fd48b052db05aea332201216eJason Monk        if (!b.compress(Bitmap.CompressFormat.PNG, 100, s)) {
25107a4a56611cc044fd48b052db05aea332201216eJason Monk            throw new RuntimeException("Unable to compress");
25207a4a56611cc044fd48b052db05aea332201216eJason Monk        }
2538f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
25407a4a56611cc044fd48b052db05aea332201216eJason Monk        b.recycle();
25507a4a56611cc044fd48b052db05aea332201216eJason Monk        mDoneLatch.countDown();
25607a4a56611cc044fd48b052db05aea332201216eJason Monk        Log.d(TAG, "Done " + slice);
2578f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    }
2588f8784c2622bc9900e772853bced17d091c4daf5Jason Monk
2598f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    public void renderAll(final Runnable runnable) {
2608f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        final ProgressDialog dialog = ProgressDialog.show(mContext, null, "Rendering...");
2618f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        new Thread(new Runnable() {
2628f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            @Override
2638f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            public void run() {
2648f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                doRender();
2658f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                mContext.runOnUiThread(new Runnable() {
2668f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                    @Override
2678f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                    public void run() {
2688f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                        dialog.dismiss();
2698f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                        runnable.run();
2708f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                    }
2718f8784c2622bc9900e772853bced17d091c4daf5Jason Monk                });
2728f8784c2622bc9900e772853bced17d091c4daf5Jason Monk            }
2738f8784c2622bc9900e772853bced17d091c4daf5Jason Monk        }).start();
2748f8784c2622bc9900e772853bced17d091c4daf5Jason Monk    }
2758f8784c2622bc9900e772853bced17d091c4daf5Jason Monk}
276