Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2eea6fab authored by Xavier Ducrohet's avatar Xavier Ducrohet
Browse files

Layoutlib: Animation support.

New locking mechanims to prevent concurrent renderings.

There's now a thread specific prepareThread() method (only
prepares the looper) and its associated cleanupThread().

For the rendering itself, acquire must be called before doing
any type of Android specific work on the scene (inflate or rendering)
After instantiation, init() must be called, which also acts as acquire.

Added a lot of checks to make sure method aren't called without
acquire or if scenes try to be rendered while acquire was called
from the same thread but on another scene.

Animation implementation:
- Handler delegate to use our own queue (since the animation runs
  through handler messages). This uses a callback to process
  the message. This callback is per-thread and only used in
  animation threads.
- SystemClock delegate to provide clock implementation.
- AnimationThread to handle playing the animation and calling back
  to the animation listener.

Change-Id: Ia39aba7ed476759df1da3200e413fe3e92590d15
parent 7750c2ac
Loading
Loading
Loading
Loading
+50 −4
Original line number Diff line number Diff line
@@ -366,13 +366,59 @@ public class Canvas_Delegate {


    /*package*/ static void native_concat(int nCanvas, int nMatrix) {
        // FIXME
        throw new UnsupportedOperationException();
        // get the delegate from the native int.
        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
        if (canvasDelegate == null) {
            assert false;
            return;
        }

        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
        if (matrixDelegate == null) {
            assert false;
            return;
        }

        // get the current top graphics2D object.
        Graphics2D g = canvasDelegate.getGraphics2d();

        // get its current matrix
        AffineTransform currentTx = g.getTransform();
        // get the AffineTransform of the given matrix
        AffineTransform matrixTx = matrixDelegate.getAffineTransform();

        // combine them so that the given matrix is applied after.
        currentTx.preConcatenate(matrixTx);

        // give it to the graphics2D as a new matrix replacing all previous transform
        g.setTransform(currentTx);
    }

    /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) {
        // FIXME
        throw new UnsupportedOperationException();
        // get the delegate from the native int.
        Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
        if (canvasDelegate == null) {
            assert false;
        }

        Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
        if (matrixDelegate == null) {
            assert false;
        }

        // get the current top graphics2D object.
        Graphics2D g = canvasDelegate.getGraphics2d();

        // get the AffineTransform of the given matrix
        AffineTransform matrixTx = matrixDelegate.getAffineTransform();

        // give it to the graphics2D as a new matrix replacing all previous transform
        g.setTransform(matrixTx);

        // FIXME: log
//        if (mLogger != null && matrixDelegate.hasPerspective()) {
//            mLogger.warning("android.graphics.Canvas#setMatrix(android.graphics.Matrix) only supports affine transformations in the Layout Editor.");
//        }
    }

    /*package*/ static boolean native_clipRect(int nCanvas,
+15 −7
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ public final class Matrix_Delegate {
            return null;
        }

        return getAffineTransform(delegate);
        return delegate.getAffineTransform();
    }

    public static boolean hasPerspective(Matrix m) {
@@ -74,7 +74,7 @@ public final class Matrix_Delegate {
            return false;
        }

        return (delegate.mValues[6] != 0 || delegate.mValues[7] != 0 || delegate.mValues[8] != 1);
        return delegate.hasPerspective();
    }

    /**
@@ -106,6 +106,18 @@ public final class Matrix_Delegate {
        return true;
    }

    /**
     * Returns an {@link AffineTransform} matching the matrix.
     */
    public AffineTransform getAffineTransform() {
        return getAffineTransform(mValues);
    }

    public boolean hasPerspective() {
        return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1);
    }



    // ---- native methods ----

@@ -599,7 +611,7 @@ public final class Matrix_Delegate {


        try {
            AffineTransform affineTransform = getAffineTransform(d);
            AffineTransform affineTransform = d.getAffineTransform();
            AffineTransform inverseTransform = affineTransform.createInverse();
            inv_mtx.mValues[0] = (float)inverseTransform.getScaleX();
            inv_mtx.mValues[1] = (float)inverseTransform.getShearX();
@@ -713,10 +725,6 @@ public final class Matrix_Delegate {

    // ---- Private helper methods ----

    private static AffineTransform getAffineTransform(Matrix_Delegate d) {
        return getAffineTransform(d.mValues);
    }

    /*package*/ static AffineTransform getAffineTransform(float[] matrix) {
        // the AffineTransform constructor takes the value in a different order
        // for a matrix [ 0 1 2 ]
+54 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.os;


/**
 * Delegate overriding selected methods of android.os.Handler
 *
 * Through the layoutlib_create tool, selected methods of Handler have been replaced
 * by calls to methods of the same name in this delegate class.
 *
 *
 */
public class Handler_Delegate {

    // -------- Delegate methods

    /*package*/ static boolean sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
        // get the callback
        IHandlerCallback callback = sCallbacks.get();
        if (callback != null) {
            callback.sendMessageAtTime(handler, msg, uptimeMillis);
        }
        return true;
    }

    // -------- Delegate implementation

    public interface IHandlerCallback {
        void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis);
    }

    private final static ThreadLocal<IHandlerCallback> sCallbacks =
        new ThreadLocal<IHandlerCallback>();

    public static void setCallback(IHandlerCallback callback) {
        sCallbacks.set(callback);
    }

}
+66 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.os;

import com.android.layoutlib.bridge.impl.DelegateManager;

/**
 * Delegate implementing the native methods of android.os.SystemClock
 *
 * Through the layoutlib_create tool, the original native methods of SystemClock have been replaced
 * by calls to methods of the same name in this delegate class.
 *
 * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
 * around to map int to instance of the delegate.
 *
 */
public class SystemClock_Delegate {
    private static long sBootTime = System.currentTimeMillis();

    /*package*/ static boolean setCurrentTimeMillis(long millis) {
        return true;
    }

    /**
     * Returns milliseconds since boot, not counting time spent in deep sleep.
     * <b>Note:</b> This value may get reset occasionally (before it would
     * otherwise wrap around).
     *
     * @return milliseconds of non-sleep uptime since boot.
     */
    /*package*/ static long uptimeMillis() {
        return System.currentTimeMillis() - sBootTime;
    }

    /**
     * Returns milliseconds since boot, including time spent in sleep.
     *
     * @return elapsed milliseconds since boot.
     */
    /*package*/ static long elapsedRealtime() {
        return System.currentTimeMillis() - sBootTime;
    }

    /**
     * Returns milliseconds running in the current thread.
     *
     * @return elapsed milliseconds in the thread
     */
    /*package*/ static long currentThreadTimeMillis() {
        return System.currentTimeMillis();
    }
}
+47 −34
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Main entry point of the LayoutLib Bridge.
@@ -56,6 +57,12 @@ public final class Bridge extends LayoutBridge {
        }
    }

    /**
     * Lock to ensure only one rendering/inflating happens at a time.
     * This is due to some singleton in the Android framework.
     */
    private final static ReentrantLock sLock = new ReentrantLock();

    /**
     * Maps from id to resource name/type. This is for android.R only.
     */
@@ -160,7 +167,6 @@ public final class Bridge extends LayoutBridge {

        BridgeAssetManager.initSystem();


        // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener
        // on static (native) methods which prints the signature on the console and
        // throws an exception.
@@ -251,27 +257,6 @@ public final class Bridge extends LayoutBridge {
        return true;
    }

    /**
     * Sets a 9 patch chunk in a project cache or in the framework cache.
     * @param value the path of the 9 patch
     * @param ninePatch the 9 patch object
     * @param projectKey the key of the project, or null to put the bitmap in the framework cache.
     */
    public static void setCached9Patch(String value, NinePatchChunk ninePatch, Object projectKey) {
        if (projectKey != null) {
            Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey);

            if (map == null) {
                map = new HashMap<String, SoftReference<NinePatchChunk>>();
                sProject9PatchCache.put(projectKey, map);
            }

            map.put(value, new SoftReference<NinePatchChunk>(ninePatch));
        } else {
            sFramework9PatchCache.put(value, new SoftReference<NinePatchChunk>(ninePatch));
        }
    }

    /**
     * Starts a layout session by inflating and rendering it. The method returns a
     * {@link ILayoutScene} on which further actions can be taken.
@@ -306,27 +291,25 @@ public final class Bridge extends LayoutBridge {
    public BridgeLayoutScene createScene(SceneParams params) {
        try {
            SceneResult lastResult = SceneResult.SUCCESS;
            LayoutSceneImpl scene = null;
            synchronized (this) {
            LayoutSceneImpl scene = new LayoutSceneImpl(params);
            try {
                    scene = new LayoutSceneImpl(params);

                    scene.prepare();
                scene.prepareThread();
                lastResult = scene.init(params.getTimeout());
                if (lastResult == SceneResult.SUCCESS) {
                    lastResult = scene.inflate();
                    if (lastResult == SceneResult.SUCCESS) {
                        lastResult = scene.render();
                    }
                } finally {
                    if (scene != null) {
                        scene.cleanup();
                    }
                }
            } finally {
                scene.release();
                scene.cleanupThread();
            }

            return new BridgeLayoutScene(this, scene, lastResult);
            return new BridgeLayoutScene(scene, lastResult);
        } catch (Throwable t) {
            t.printStackTrace();
            return new BridgeLayoutScene(this, null, new SceneResult("error!", t));
            return new BridgeLayoutScene(null, new SceneResult("error!", t));
        }
    }

@@ -342,6 +325,13 @@ public final class Bridge extends LayoutBridge {
        }
    }

    /**
     * Returns the lock for the bridge
     */
    public static ReentrantLock getLock() {
        return sLock;
    }

    /**
     * Returns details of a framework resource from its integer value.
     * @param value the integer value
@@ -461,4 +451,27 @@ public final class Bridge extends LayoutBridge {

        return null;
    }

    /**
     * Sets a 9 patch chunk in a project cache or in the framework cache.
     * @param value the path of the 9 patch
     * @param ninePatch the 9 patch object
     * @param projectKey the key of the project, or null to put the bitmap in the framework cache.
     */
    public static void setCached9Patch(String value, NinePatchChunk ninePatch, Object projectKey) {
        if (projectKey != null) {
            Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey);

            if (map == null) {
                map = new HashMap<String, SoftReference<NinePatchChunk>>();
                sProject9PatchCache.put(projectKey, map);
            }

            map.put(value, new SoftReference<NinePatchChunk>(ninePatch));
        } else {
            sFramework9PatchCache.put(value, new SoftReference<NinePatchChunk>(ninePatch));
        }
    }


}
Loading