/** * Create a new rendering session and test that rendering /layout/activity.xml on nexus 5 doesn't * throw any exceptions. */ @Test public void testRendering() throws ClassNotFoundException { // Create the layout pull parser. LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/activity.xml"); // Create LayoutLibCallback. LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger()); layoutLibCallback.initResources(); // TODO: Set up action bar handler properly to test menu rendering. // Create session params. SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5, layoutLibCallback); RenderSession session = mBridge.createSession(params); if (!session.getResult().isSuccess()) { getLogger().error(session.getResult().getException(), session.getResult().getErrorMessage()); } // Render the session with a timeout of 50s. Result renderResult = session.render(50000); if (!renderResult.isSuccess()) { getLogger().error(session.getResult().getException(), session.getResult().getErrorMessage()); } try { String goldenImagePath = APP_TEST_DIR + "/golden/activity.png"; ImageUtils.requireSimilar(goldenImagePath, session.getImage()); } catch (IOException e) { getLogger().error(e, e.getMessage()); } }
/** * Tear down the session after rendering. * * <p>The counterpart is {@link #setUp()}. */ private void tearDown() { // The context may be null, if there was an error during init(). if (mContext != null) { // Make sure to remove static references, otherwise we could not unload the lib mContext.disposeResources(); } if (sCurrentContext != null) { // quit HandlerThread created during this session. HandlerThread_Delegate.cleanUp(sCurrentContext); } // clear the stored ViewConfiguration since the map is per density and not per context. ViewConfiguration_Accessor.clearConfigurations(); // remove the InputMethodManager InputMethodManager_Accessor.resetInstance(); sCurrentContext = null; Bridge.setLog(null); if (mContext != null) { mContext.getRenderResources().setFrameworkResourceIdProvider(null); mContext.getRenderResources().setLogger(null); } ParserFactory.setParserFactory(null); }
/** Initialize the bridge and the resource maps. */ @Before public void setUp() { File data_dir = new File(PLATFORM_DIR, "data"); File res = new File(data_dir, "res"); mFrameworkRepo = new FrameworkResources(new FolderWrapper(res)); mFrameworkRepo.loadResources(); mFrameworkRepo.loadPublicResources(getLogger()); mProjectResources = new ResourceRepository(new FolderWrapper(TEST_RES_DIR + APP_TEST_RES), false) { @NonNull @Override protected ResourceItem createResourceItem(String name) { return new ResourceItem(name); } }; mProjectResources.loadResources(); File fontLocation = new File(data_dir, "fonts"); File buildProp = new File(PLATFORM_DIR, "build.prop"); File attrs = new File(res, "values" + File.separator + "attrs.xml"); mBridge = new Bridge(); mBridge.init( ConfigGenerator.loadProperties(buildProp), fontLocation, ConfigGenerator.getEnumMap(attrs), getLayoutLog()); }
/** * Acquire the lock so that the scene can be acted upon. * * <p>This returns null if the lock was just acquired, otherwise it returns {@link * Result.Status#SUCCESS} if the lock already belonged to that thread, or another instance (see * {@link Result#getStatus()}) if an error occurred. * * @param timeout the time to wait if another rendering is happening. * @return null if the lock was just acquire or another result depending on the state. * @throws IllegalStateException if the current context is different than the one owned by the * scene. */ private Result acquireLock(long timeout) { ReentrantLock lock = Bridge.getLock(); if (!lock.isHeldByCurrentThread()) { try { boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS); if (!acquired) { return ERROR_TIMEOUT.createResult(); } } catch (InterruptedException e) { return ERROR_LOCK_INTERRUPTED.createResult(); } } else { // This thread holds the lock already. Checks that this wasn't for a different context. // If this is called by init, mContext will be null and so should sCurrentContext // anyway if (mContext != sCurrentContext) { throw new IllegalStateException( "Acquiring different scenes from same thread without releases"); } return SUCCESS.createResult(); } return null; }
/** * Checks that the lock is owned by the current thread and that the current context is the one * from this scene. * * @throws IllegalStateException if the current context is different than the one owned by the * scene, or if {@link #acquire(long)} was not called. */ protected void checkLock() { ReentrantLock lock = Bridge.getLock(); if (!lock.isHeldByCurrentThread()) { throw new IllegalStateException("scene must be acquired first. see #acquire(long)"); } if (sCurrentContext != mContext) { throw new IllegalStateException("Thread acquired a scene but is rendering a different one"); } }
/** * Returns a {@link NinePatchChunk} object for the given serialized representation. * * <p>If the chunk is present in the cache then the object from the cache is returned, otherwise * the array is deserialized into a {@link NinePatchChunk} object. * * @param array the serialized representation of the chunk. * @return the NinePatchChunk or null if deserialization failed. */ public static NinePatchChunk getChunk(byte[] array) { SoftReference<NinePatchChunk> chunkRef = sChunkCache.get(array); NinePatchChunk chunk = chunkRef.get(); if (chunk == null) { ByteArrayInputStream bais = new ByteArrayInputStream(array); ObjectInputStream ois = null; try { ois = new ObjectInputStream(bais); chunk = (NinePatchChunk) ois.readObject(); // put back the chunk in the cache if (chunk != null) { sChunkCache.put(array, new SoftReference<NinePatchChunk>(chunk)); } } catch (IOException e) { Bridge.getLog() .error( LayoutLog.TAG_BROKEN, "Failed to deserialize NinePatchChunk content.", e, null /*data*/); return null; } catch (ClassNotFoundException e) { Bridge.getLog() .error( LayoutLog.TAG_BROKEN, "Failed to deserialize NinePatchChunk class.", e, null /*data*/); return null; } finally { if (ois != null) { try { ois.close(); } catch (IOException ignored) { } } } } return chunk; }
/** Cleans up the scene after an action. */ public void release() { ReentrantLock lock = Bridge.getLock(); // with the use of finally blocks, it is possible to find ourself calling this // without a successful call to prepareScene. This test makes sure that unlock() will // not throw IllegalMonitorStateException. if (lock.isHeldByCurrentThread()) { tearDown(); lock.unlock(); } }
@LayoutlibDelegate /*package*/ static String formatDateInterval(long address, long fromDate, long toDate) { DateIntervalFormat_Delegate delegate = sManager.getDelegate((int) address); if (delegate == null) { Bridge.getLog() .error(LayoutLog.TAG_BROKEN, "Unable for find native DateIntervalFormat", null); return null; } DateInterval interval = new DateInterval(fromDate, toDate); StringBuffer sb = new StringBuffer(); FieldPosition pos = new FieldPosition(0); delegate.mFormat.format(interval, sb, pos); return sb.toString(); }
/** * Sets up the session for rendering. * * <p>The counterpart is {@link #tearDown()}. */ private void setUp() { // make sure the Resources object references the context (and other objects) for this // scene mContext.initResources(); sCurrentContext = mContext; // create an InputMethodManager InputMethodManager.getInstance(); LayoutLog currentLog = mParams.getLog(); Bridge.setLog(currentLog); mContext.getRenderResources().setFrameworkResourceIdProvider(this); mContext.getRenderResources().setLogger(currentLog); }
/** * Serializes the given chunk. * * @return the serialized data for the chunk. */ public static byte[] serialize(NinePatchChunk chunk) { // serialize the chunk to get a byte[] ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(baos); oos.writeObject(chunk); } catch (IOException e) { Bridge.getLog().error(null, "Failed to serialize NinePatchChunk.", e, null /*data*/); return null; } finally { if (oos != null) { try { oos.close(); } catch (IOException ignored) { } } } // get the array and add it to the cache byte[] array = baos.toByteArray(); sChunkCache.put(array, new SoftReference<NinePatchChunk>(chunk)); return array; }
@Override public Integer getId(ResourceType resType, String resName) { return Bridge.getResourceId(resType, resName); }