IInputMethodSessionWrapper.java revision a308c0326bef49293396ceca843bb595139ba38f
195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau/*
295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau * Copyright (C) 2008 The Android Open Source Project
395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau *
495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau * Licensed under the Apache License, Version 2.0 (the "License");
595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau * you may not use this file except in compliance with the License.
695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau * You may obtain a copy of the License at
795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau *
895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau *      http://www.apache.org/licenses/LICENSE-2.0
995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau *
1095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau * Unless required by applicable law or agreed to in writing, software
1195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau * distributed under the License is distributed on an "AS IS" BASIS,
1295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau * See the License for the specific language governing permissions and
1495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau * limitations under the License.
1595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau */
1695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
1795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Laupackage android.inputmethodservice;
1895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
1995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lauimport com.android.internal.os.HandlerCaller;
2095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lauimport com.android.internal.view.IInputMethodCallback;
2195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lauimport com.android.internal.view.IInputMethodSession;
2295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
2395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lauimport android.content.Context;
2495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lauimport android.graphics.Rect;
2595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lauimport android.os.Bundle;
2695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lauimport android.os.Message;
2795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lauimport android.os.RemoteException;
2895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lauimport android.util.Log;
2995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lauimport android.view.KeyEvent;
3095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lauimport android.view.MotionEvent;
3195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lauimport android.view.inputmethod.CompletionInfo;
3295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lauimport android.view.inputmethod.ExtractedText;
3395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lauimport android.view.inputmethod.InputMethodSession;
3495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
3595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lauclass IInputMethodSessionWrapper extends IInputMethodSession.Stub
3695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        implements HandlerCaller.Callback {
3795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    private static final String TAG = "InputMethodWrapper";
3895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    private static final boolean DEBUG = false;
3995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
4095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    private static final int DO_FINISH_INPUT = 60;
4195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    private static final int DO_DISPLAY_COMPLETIONS = 65;
4295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    private static final int DO_UPDATE_EXTRACTED_TEXT = 67;
4395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    private static final int DO_DISPATCH_KEY_EVENT = 70;
4495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    private static final int DO_DISPATCH_TRACKBALL_EVENT = 80;
4595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    private static final int DO_UPDATE_SELECTION = 90;
4695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    private static final int DO_UPDATE_CURSOR = 95;
4795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    private static final int DO_APP_PRIVATE_COMMAND = 100;
4895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    private static final int DO_TOGGLE_SOFT_INPUT = 105;
4995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    private static final int DO_FINISH_SESSION = 110;
5095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
51c6853892c94800e72c0bd676d5d2136d48cea76eGlenn Kasten    HandlerCaller mCaller;
5295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    InputMethodSession mInputMethodSession;
5395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
5495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    // NOTE: we should have a cache of these.
5595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    static class InputMethodEventCallbackWrapper implements InputMethodSession.EventCallback {
5695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        final IInputMethodCallback mCb;
5795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        InputMethodEventCallbackWrapper(IInputMethodCallback cb) {
58ebbc7dd54bb3a6b05ac899af97fc4b7d0ad6f400Jean-Michel Trivi            mCb = cb;
5995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        }
6095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        public void finishedEvent(int seq, boolean handled) {
6195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            try {
6295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                mCb.finishedEvent(seq, handled);
6395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            } catch (RemoteException e) {
6495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            }
6595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        }
66ebbc7dd54bb3a6b05ac899af97fc4b7d0ad6f400Jean-Michel Trivi    }
6795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
6895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    public IInputMethodSessionWrapper(Context context,
6995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            InputMethodSession inputMethodSession) {
7095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        mCaller = new HandlerCaller(context, this);
7195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        mInputMethodSession = inputMethodSession;
7295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    }
7395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
7495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    public InputMethodSession getInternalInputMethodSession() {
7595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        return mInputMethodSession;
7695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    }
7795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
7895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    public void executeMessage(Message msg) {
79ebbc7dd54bb3a6b05ac899af97fc4b7d0ad6f400Jean-Michel Trivi        if (mInputMethodSession == null) return;
8095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
8195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        switch (msg.what) {
8295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            case DO_FINISH_INPUT:
8395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                mInputMethodSession.finishInput();
8495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                return;
8595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            case DO_DISPLAY_COMPLETIONS:
8695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                mInputMethodSession.displayCompletions((CompletionInfo[])msg.obj);
875940f26b6c2da96c0f0b2293623ed692d1fd9026Jean-Michel Trivi                return;
8895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            case DO_UPDATE_EXTRACTED_TEXT:
8995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                mInputMethodSession.updateExtractedText(msg.arg1,
90ebbc7dd54bb3a6b05ac899af97fc4b7d0ad6f400Jean-Michel Trivi                        (ExtractedText)msg.obj);
9195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                return;
9295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            case DO_DISPATCH_KEY_EVENT: {
9395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
9495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                mInputMethodSession.dispatchKeyEvent(msg.arg1,
9595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                        (KeyEvent)args.arg1,
9695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                        new InputMethodEventCallbackWrapper(
9795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                                (IInputMethodCallback)args.arg2));
9895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                mCaller.recycleArgs(args);
9995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                return;
10095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            }
10195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            case DO_DISPATCH_TRACKBALL_EVENT: {
10295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
10395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                mInputMethodSession.dispatchTrackballEvent(msg.arg1,
10495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                        (MotionEvent)args.arg1,
10595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                        new InputMethodEventCallbackWrapper(
10695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                                (IInputMethodCallback)args.arg2));
10795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                mCaller.recycleArgs(args);
10895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                return;
10995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            }
11095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            case DO_UPDATE_SELECTION: {
11195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
11295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                mInputMethodSession.updateSelection(args.argi1, args.argi2,
11395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                        args.argi3, args.argi4, args.argi5, args.argi6);
11495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                mCaller.recycleArgs(args);
11595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                return;
11695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            }
11795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            case DO_UPDATE_CURSOR: {
11895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                mInputMethodSession.updateCursor((Rect)msg.obj);
11995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                return;
12095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            }
12195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            case DO_APP_PRIVATE_COMMAND: {
12295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
12395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                mInputMethodSession.appPrivateCommand((String)args.arg1,
12495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                        (Bundle)args.arg2);
12595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                mCaller.recycleArgs(args);
12695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                return;
12795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            }
12895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            case DO_TOGGLE_SOFT_INPUT: {
12995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                mInputMethodSession.toggleSoftInput(msg.arg1, msg.arg2);
13095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                return;
13195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            }
13295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            case DO_FINISH_SESSION: {
13395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                mInputMethodSession = null;
13495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                return;
13595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            }
13695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        }
13795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        Log.w(TAG, "Unhandled message code: " + msg.what);
13895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    }
13995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
14095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    public void finishInput() {
141c2303eb5497c488db786dcb2b8514db229452536Glenn Kasten        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_INPUT));
14295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    }
14395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
14495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    public void displayCompletions(CompletionInfo[] completions) {
14595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        mCaller.executeOrSendMessage(mCaller.obtainMessageO(
14695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                DO_DISPLAY_COMPLETIONS, completions));
14795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    }
14895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
14995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    public void updateExtractedText(int token, ExtractedText text) {
15095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        mCaller.executeOrSendMessage(mCaller.obtainMessageIO(
15195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                DO_UPDATE_EXTRACTED_TEXT, token, text));
15295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    }
15395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
15495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    public void dispatchKeyEvent(int seq, KeyEvent event, IInputMethodCallback callback) {
15595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_KEY_EVENT, seq,
15695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                event, callback));
15795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    }
15895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
15995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    public void dispatchTrackballEvent(int seq, MotionEvent event, IInputMethodCallback callback) {
16095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_TRACKBALL_EVENT, seq,
16195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                event, callback));
16295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    }
16395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
16495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    public void updateSelection(int oldSelStart, int oldSelEnd,
16595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau            int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
1665940f26b6c2da96c0f0b2293623ed692d1fd9026Jean-Michel Trivi        mCaller.executeOrSendMessage(mCaller.obtainMessageIIIIII(DO_UPDATE_SELECTION,
16795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                oldSelStart, oldSelEnd, newSelStart, newSelEnd,
16895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                candidatesStart, candidatesEnd));
16995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    }
17095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
17195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    public void updateCursor(Rect newCursor) {
17295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_UPDATE_CURSOR,
17395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau                newCursor));
17495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    }
17595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
17695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    public void appPrivateCommand(String action, Bundle data) {
17795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data));
17895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    }
17995ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
18095ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    public void toggleSoftInput(int showFlags, int hideFlags) {
18195ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        mCaller.executeOrSendMessage(mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags));
18295ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    }
18395ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau
18495ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    public void finishSession() {
18595ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_SESSION));
18695ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau    }
18795ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau}
18895ba463b7fd020b23dc48ac0049b9c107add7990Yu Shan Emily Lau