1a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)package org.chromium.android_webview;
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.graphics.Picture;
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.os.Handler;
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.os.Looper;
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.os.Message;
113551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import android.os.SystemClock;
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport org.chromium.base.VisibleForTesting;
14a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
15ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport java.util.concurrent.Callable;
16ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * This class is responsible for calling certain client callbacks on the UI thread.
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Most callbacks do no go through here, but get forwarded to AwContentsClient directly. The
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * messages processed here may originate from the IO or UI thread.
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
23a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch@VisibleForTesting
24a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochpublic class AwContentsClientCallbackHelper {
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // TODO(boliu): Consider removing DownloadInfo and LoginRequestInfo by using native
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // MessageLoop to post directly to AwContents.
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private static class DownloadInfo {
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final String mUrl;
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final String mUserAgent;
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final String mContentDisposition;
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final String mMimeType;
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final long mContentLength;
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        DownloadInfo(String url,
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                     String userAgent,
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                     String contentDisposition,
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                     String mimeType,
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                     long contentLength) {
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            mUrl = url;
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            mUserAgent = userAgent;
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            mContentDisposition = contentDisposition;
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            mMimeType = mimeType;
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            mContentLength = contentLength;
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private static class LoginRequestInfo {
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final String mRealm;
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final String mAccount;
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final String mArgs;
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        LoginRequestInfo(String realm, String account, String args) {
55a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            mRealm = realm;
56a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            mAccount = account;
57a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            mArgs = args;
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private static class OnReceivedErrorInfo {
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final int mErrorCode;
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final String mDescription;
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        final String mFailingUrl;
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        OnReceivedErrorInfo(int errorCode, String description, String failingUrl) {
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            mErrorCode = errorCode;
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            mDescription = description;
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            mFailingUrl = failingUrl;
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
73a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private static final int MSG_ON_LOAD_RESOURCE = 1;
74a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private static final int MSG_ON_PAGE_STARTED = 2;
75a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private static final int MSG_ON_DOWNLOAD_START = 3;
76a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private static final int MSG_ON_RECEIVED_LOGIN_REQUEST = 4;
77a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private static final int MSG_ON_RECEIVED_ERROR = 5;
78a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private static final int MSG_ON_NEW_PICTURE = 6;
79a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    private static final int MSG_ON_SCALE_CHANGED_SCALED = 7;
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
813551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    // Minimum period allowed between consecutive onNewPicture calls, to rate-limit the callbacks.
823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    private static final long ON_NEW_PICTURE_MIN_PERIOD_MILLIS = 500;
833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    // Timestamp of the most recent onNewPicture callback.
843551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    private long mLastPictureTime = 0;
853551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    // True when a onNewPicture callback is currenly in flight.
863551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    private boolean mHasPendingOnNewPicture = false;
873551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    private final AwContentsClient mContentsClient;
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
901e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    private final Handler mHandler;
911e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
921e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    private class MyHandler extends Handler {
931e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        private MyHandler(Looper looper) {
941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)            super(looper);
951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        }
961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        @Override
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        public void handleMessage(Message msg) {
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            switch(msg.what) {
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                case MSG_ON_LOAD_RESOURCE: {
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    final String url = (String) msg.obj;
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    mContentsClient.onLoadResource(url);
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    break;
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                }
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                case MSG_ON_PAGE_STARTED: {
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    final String url = (String) msg.obj;
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    mContentsClient.onPageStarted(url);
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    break;
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                }
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                case MSG_ON_DOWNLOAD_START: {
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    DownloadInfo info = (DownloadInfo) msg.obj;
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    mContentsClient.onDownloadStart(info.mUrl, info.mUserAgent,
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            info.mContentDisposition, info.mMimeType, info.mContentLength);
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    break;
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                }
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                case MSG_ON_RECEIVED_LOGIN_REQUEST: {
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    LoginRequestInfo info = (LoginRequestInfo) msg.obj;
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    mContentsClient.onReceivedLoginRequest(info.mRealm, info.mAccount, info.mArgs);
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    break;
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                }
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                case MSG_ON_RECEIVED_ERROR: {
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    OnReceivedErrorInfo info = (OnReceivedErrorInfo) msg.obj;
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    mContentsClient.onReceivedError(info.mErrorCode, info.mDescription,
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            info.mFailingUrl);
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    break;
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                }
127ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                case MSG_ON_NEW_PICTURE: {
128ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                    Picture picture = null;
129ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                    try {
130a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                        if (msg.obj != null) picture = (Picture) ((Callable<?>) msg.obj).call();
131ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                    } catch (Exception e) {
132ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                        throw new RuntimeException("Error getting picture", e);
133ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                    }
134ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                    mContentsClient.onNewPicture(picture);
1353551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                    mLastPictureTime = SystemClock.uptimeMillis();
1363551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                    mHasPendingOnNewPicture = false;
137ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                    break;
138ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                }
1394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                case MSG_ON_SCALE_CHANGED_SCALED: {
1404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                    float oldScale = Float.intBitsToFloat(msg.arg1);
1414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                    float newScale = Float.intBitsToFloat(msg.arg2);
1424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                    mContentsClient.onScaleChangedScaled(oldScale, newScale);
1434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                    break;
1444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                }
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                default:
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    throw new IllegalStateException(
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            "AwContentsClientCallbackHelper: unhandled message " + msg.what);
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            }
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
150a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    public AwContentsClientCallbackHelper(Looper looper, AwContentsClient contentsClient) {
1531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        mHandler = new MyHandler(looper);
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        mContentsClient = contentsClient;
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public void postOnLoadResource(String url) {
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_LOAD_RESOURCE, url));
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public void postOnPageStarted(String url) {
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_PAGE_STARTED, url));
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public void postOnDownloadStart(String url, String userAgent, String contentDisposition,
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            String mimeType, long contentLength) {
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        DownloadInfo info = new DownloadInfo(url, userAgent, contentDisposition, mimeType,
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                contentLength);
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_DOWNLOAD_START, info));
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public void postOnReceivedLoginRequest(String realm, String account, String args) {
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        LoginRequestInfo info = new LoginRequestInfo(realm, account, args);
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_RECEIVED_LOGIN_REQUEST, info));
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    public void postOnReceivedError(int errorCode, String description, String failingUrl) {
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        OnReceivedErrorInfo info = new OnReceivedErrorInfo(errorCode, description, failingUrl);
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_RECEIVED_ERROR, info));
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
181ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
182ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    public void postOnNewPicture(Callable<Picture> pictureProvider) {
1833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        if (mHasPendingOnNewPicture) return;
1843551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        mHasPendingOnNewPicture = true;
1853551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        long pictureTime = java.lang.Math.max(mLastPictureTime + ON_NEW_PICTURE_MIN_PERIOD_MILLIS,
1863551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                SystemClock.uptimeMillis());
1873551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ON_NEW_PICTURE, pictureProvider),
1883551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                pictureTime);
189ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    }
1904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    public void postOnScaleChangedScaled(float oldScale, float newScale) {
1924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        // The float->int->float conversion here is to avoid unnecessary allocations. The
1934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        // documentation states that intBitsToFloat(floatToIntBits(a)) == a for all values of a
1944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        // (except for NaNs which are collapsed to a single canonical NaN, but we don't care for
1954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        // that case).
1964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_SCALE_CHANGED_SCALED,
1974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                    Float.floatToIntBits(oldScale), Float.floatToIntBits(newScale)));
1984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
200