From 9dd6b202fabc819c9de5622756e6eaad5941157b Mon Sep 17 00:00:00 2001 From: CaiFeng <2397707574@qq.com> Date: Tue, 6 Jun 2023 18:32:17 +0800 Subject: [PATCH 1/5] support remote ime --- .../cloudapp/ui/CasCloudPhoneActivity.java | 1 + .../huawei/cloudphone/ICASAidlInterface.aidl | 2 + .../huawei/cloudphone/ICASAidlListener.aidl | 2 + cloudphone/src/main/cpp/CasCommon.h | 1 + cloudphone/src/main/cpp/CasController.cpp | 38 ++++ cloudphone/src/main/cpp/CasController.h | 4 + cloudphone/src/main/cpp/CasJniBridge.cpp | 6 +- cloudphone/src/main/cpp/CasJniBridge.h | 2 + cloudphone/src/main/cpp/cas_common/CasMsg.h | 2 + .../cpp/cas_stream/CasStreamBuildSender.cpp | 3 + .../huawei/cloudphone/api/ICloudPhone.java | 7 + .../cloudphone/apiimpl/CloudPhoneImeMgr.java | 193 ++++++++++++++++++ .../cloudphone/apiimpl/CloudPhoneImpl.java | 43 ++++ .../apiimpl/CloudPhoneTextWatchListener.java | 5 + .../cloudphone/jniwrapper/JNIWrapper.java | 2 + .../cloudphone/jniwrapper/JniBridge.java | 3 + .../huawei/cloudphone/service/CASClient.java | 26 +++ .../cloudphone/service/CasProcessor.java | 22 ++ 18 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImeMgr.java create mode 100644 cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneTextWatchListener.java diff --git a/app/src/main/java/com/huawei/cloudapp/ui/CasCloudPhoneActivity.java b/app/src/main/java/com/huawei/cloudapp/ui/CasCloudPhoneActivity.java index 424cbe9..88d4933 100644 --- a/app/src/main/java/com/huawei/cloudapp/ui/CasCloudPhoneActivity.java +++ b/app/src/main/java/com/huawei/cloudapp/ui/CasCloudPhoneActivity.java @@ -308,6 +308,7 @@ public class CasCloudPhoneActivity extends FragmentActivity implements View.OnCl mCloudPhone.setMediaConfig(mediaConfig); } + mCloudPhone.enableRemoteIme(true); mCloudPhone.startCloudPhone(this, mFrameLayout, parasMap); } catch (Exception e) { CASLog.e(TAG, "startCloudApp start failed." + e.getMessage()); diff --git a/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlInterface.aidl b/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlInterface.aidl index 21b9d45..67e47a1 100644 --- a/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlInterface.aidl +++ b/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlInterface.aidl @@ -73,4 +73,6 @@ interface ICASAidlInterface { void mute(in boolean isMute); void sendData(byte type, in byte[] data); + + boolean enableRemoteIme(boolean isEnableRemote); } diff --git a/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlListener.aidl b/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlListener.aidl index c3a9fee..948d7c2 100644 --- a/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlListener.aidl +++ b/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlListener.aidl @@ -28,4 +28,6 @@ interface ICASAidlListener { void onChannelDataRecv(in byte[] data); void onVirtualDevDataRecv(in byte[] data); + + void onImeMsgRecv(in byte[] data); } diff --git a/cloudphone/src/main/cpp/CasCommon.h b/cloudphone/src/main/cpp/CasCommon.h index 3b75466..3c09ce2 100644 --- a/cloudphone/src/main/cpp/CasCommon.h +++ b/cloudphone/src/main/cpp/CasCommon.h @@ -33,6 +33,7 @@ const std::string KEY_AVAILABLE_PLAYTIME = "available_playtime"; const std::string KEY_CLIENT_TYPE = "client_type"; const std::string KEY_MEDIA_CONFIG = "media_config"; const std::string KEY_USER_ID = "user_id"; +const std::string KEY_REMOTE_IME = "remote_ime"; const std::string KEY_FRAME_RATE = "frame_rate"; const std::string KEY_FRAME_TYPE = "frame_type"; diff --git a/cloudphone/src/main/cpp/CasController.cpp b/cloudphone/src/main/cpp/CasController.cpp index 2b5a948..d32f339 100644 --- a/cloudphone/src/main/cpp/CasController.cpp +++ b/cloudphone/src/main/cpp/CasController.cpp @@ -68,6 +68,7 @@ CasController::CasController() m_virtualDeviceStream = nullptr; m_controlStream = nullptr; m_channelStream = nullptr; + m_imeDataStream = nullptr; m_cmdController = nullptr; m_streamParseThread = nullptr; m_heartbeatThread = nullptr; @@ -89,6 +90,7 @@ CasController::~CasController() m_virtualDeviceStream = nullptr; m_controlStream = nullptr; m_channelStream = nullptr; + m_imeDataStream = nullptr; m_cmdController = nullptr; m_streamParseThread = nullptr; m_heartbeatThread = nullptr; @@ -180,6 +182,13 @@ bool CasController::Start(ANativeWindow *nativeWindow, bool isHome) StartWorkers(!m_retainVideoDecode); + std::string isEnableIme; + if (m_isEnableRemoteIme) { + isEnableIme = "1"; + } else { + isEnableIme = "0"; + } + std::string startCmd = CMD_START_APP; map parameters = { { KEY_COMMAND, startCmd }, { KEY_TICKET, m_ticket }, @@ -192,6 +201,7 @@ bool CasController::Start(ANativeWindow *nativeWindow, bool isHome) { KEY_PROTOCOL_VERSION, m_conf.protocolVersion }, { KEY_CLIENT_TYPE, m_clientType }, { KEY_MEDIA_CONFIG, mediaConfigStr }, + { KEY_REMOTE_IME, isEnableIme }, { KEY_MAX_DISCONNECT_DURATION, m_maxDisconnectDuration } }; res = SendCommand(parameters); @@ -279,6 +289,7 @@ bool CasController::Stop(bool isHome) CloseDataStream(); m_isNotifyFirstFrame = false; + m_isEnableRemoteIme = false; m_mediaConfig.clear(); m_jniConf.clear(); return true; @@ -405,6 +416,7 @@ bool CasController::CreateWorkers(ANativeWindow *nativeWindow, bool needVideoDec m_streamParser->SetServiceHandle(CasMsgType::Audio, m_audioPacketStream); m_streamParser->SetServiceHandle(CasMsgType::Channel, m_channelStream); m_streamParser->SetServiceHandle(CasMsgType::VirtualDevice, m_virtualDeviceStream); + m_streamParser->SetServiceHandle(CasMsgType::ImeData, m_imeDataStream); if (needVideoDecode) { m_videoDecodeThread = new (std::nothrow) CasVideoHDecodeThread(nativeWindow, m_frameType); @@ -606,6 +618,12 @@ bool CasController::InitDataStream() return false; } + m_imeDataStream = new (std::nothrow) CasDataPipe(); + if (m_imeDataStream == nullptr) { + ERR("Failed to new ime data packet stream."); + return false; + } + return true; } @@ -635,6 +653,10 @@ bool CasController::ClearDataStream() m_virtualDeviceStream->Clear(); } + if (m_imeDataStream != nullptr) { + m_imeDataStream->Clear(); + } + INFO("Succeed to clear data stream "); return true; } @@ -666,6 +688,11 @@ bool CasController::CloseDataStream() m_channelStream = nullptr; } + if (m_imeDataStream != nullptr) { + delete m_imeDataStream; + m_imeDataStream = nullptr; + } + INFO("Succeed to close data stream "); return true; } @@ -768,6 +795,11 @@ int CasController::JniRecvData(CasMsgType type, uint8_t *data, int length) pPkt = m_virtualDeviceStream->GetNextPkt(); } break; + case CasMsgType::ImeData: + if (m_imeDataStream != nullptr) { + pPkt = m_imeDataStream->GetNextPkt(); + } + break; default: ERR("Invalid type %d, length %d.", type, length); return 0; @@ -922,6 +954,12 @@ void CasController::NotifyFirstVideoFrame() } } +bool CasController::EnableRemoteIme(bool isEnable) +{ + m_isEnableRemoteIme = isEnable; + return true; +} + void CasController::OnCmdRecv(int code, string msg) { NotifyCommand(code, msg); diff --git a/cloudphone/src/main/cpp/CasController.h b/cloudphone/src/main/cpp/CasController.h index 02b111d..1be054f 100644 --- a/cloudphone/src/main/cpp/CasController.h +++ b/cloudphone/src/main/cpp/CasController.h @@ -78,6 +78,8 @@ public: void NotifyFirstVideoFrame(); + bool EnableRemoteIme(bool isEnable); + private: bool Release(); @@ -126,6 +128,7 @@ private: CasDataPipe *m_controlStream = nullptr; CasDataPipe *m_channelStream = nullptr; CasDataPipe *m_virtualDeviceStream = nullptr; + CasDataPipe *m_imeDataStream = nullptr; CasCmdController *m_cmdController = nullptr; CasHeartbeatController *m_heartbeatController = nullptr; @@ -158,6 +161,7 @@ private: const bool m_retainVideoDecode = true; const bool m_needVideoDecode = true; bool m_isNotifyFirstFrame = false; + bool m_isEnableRemoteIme = false; std::map m_mediaConfig; }; diff --git a/cloudphone/src/main/cpp/CasJniBridge.cpp b/cloudphone/src/main/cpp/CasJniBridge.cpp index 8c4afd1..6200951 100644 --- a/cloudphone/src/main/cpp/CasJniBridge.cpp +++ b/cloudphone/src/main/cpp/CasJniBridge.cpp @@ -174,7 +174,11 @@ extern "C" JNIEXPORT jint JNICALL JNI(sendData)(JNIEnv *env, jclass clazz, jbyte int ret = gJniApiCtrl->JniSendData((CasMsgType)type, data, length); env->ReleaseByteArrayElements(jData, (jbyte *)data, JNI_COMMIT); return ret; - return 0; +} + +extern "C" JNIEXPORT jboolean JNICALL JNI(enableRemoteIme)(JNIEnv *env, jclass, jboolean isEnable) +{ + return gJniApiCtrl->EnableRemoteIme(isEnable); } extern "C" JNIEXPORT jboolean JNICALL JNI(sendTouchEvent)(JNIEnv *env, jclass, jint id, jint action, jint x, jint y, diff --git a/cloudphone/src/main/cpp/CasJniBridge.h b/cloudphone/src/main/cpp/CasJniBridge.h index 47a4c8d..2e15786 100644 --- a/cloudphone/src/main/cpp/CasJniBridge.h +++ b/cloudphone/src/main/cpp/CasJniBridge.h @@ -50,6 +50,8 @@ JNIEXPORT jboolean JNICALL JNI(sendMotionEvent)(JNIEnv *env, jclass, jint master JNIEXPORT jboolean JNICALL JNI(setRotation)(JNIEnv *, jclass, int h); +JNIEXPORT jboolean JNICALL JNI(enableRemoteIme)(JNIEnv *, jclass, jboolean isEnable); + JNIEXPORT jboolean JNICALL JNI(getConnectStatus)(JNIEnv *env, jclass); } #endif // CLOUDAPPSDK_JNIRENDER_H \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_common/CasMsg.h b/cloudphone/src/main/cpp/cas_common/CasMsg.h index b4a5d9a..fcea1f5 100644 --- a/cloudphone/src/main/cpp/cas_common/CasMsg.h +++ b/cloudphone/src/main/cpp/cas_common/CasMsg.h @@ -31,6 +31,7 @@ enum CasMsgType : uint8_t { HeartBeat = 8, Orientation = 9, Recorder = 11, + ImeData = 14, KeyEventInput = 15, MotionEventInput = 16, VirtualDevice = 20, @@ -53,6 +54,7 @@ enum CasMsgType : uint8_t { #define CAS_MSG_CHECKSUM_VIDEO GET_CAS_CHECKSUM(CasMsgType::Video) #define CAS_MSG_CHECKSUM_VERIFY GET_CAS_CHECKSUM(CasMsgType::Verify) #define CAS_MSG_CHECKSUM_HEARTBEAT GET_CAS_CHECKSUM(CasMsgType::HeartBeat) +#define CAS_MSG_CHECKSUM_IMEDATA GET_CAS_CHECKSUM(CasMsgType::ImeData) #define CAS_MSG_CHECKSUM_KEYEVENTINPUT GET_CAS_CHECKSUM(CasMsgType::KeyEventInput) #define CAS_MSG_CHECKSUM_MOTIONEVENTINPUT GET_CAS_CHECKSUM(CasMsgType::MotionEventInput) #define CAS_MSG_CHECKSUM_CHANNEL GET_CAS_CHECKSUM(CasMsgType::Channel) diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp index 1257654..92bbf8a 100644 --- a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp +++ b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp @@ -63,6 +63,9 @@ int CasStreamBuildSender::SendDataToServer(CasMsgType type, const void *buf, siz case (HeartBeat): msgHead.checksum = CAS_MSG_CHECKSUM_HEARTBEAT; break; + case (ImeData): + msgHead.checksum = CAS_MSG_CHECKSUM_IMEDATA; + break; case (KeyEventInput): msgHead.checksum = CAS_MSG_CHECKSUM_KEYEVENTINPUT; break; diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhone.java b/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhone.java index 5cf371a..f81658d 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhone.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhone.java @@ -135,4 +135,11 @@ public interface ICloudPhone { * @return 网络时延 */ int getRtt(); + + /** + * 使能远端输入法 + * + * @param isEnable + */ + int enableRemoteIme(boolean isEnable); } diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImeMgr.java b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImeMgr.java new file mode 100644 index 0000000..16a07a2 --- /dev/null +++ b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImeMgr.java @@ -0,0 +1,193 @@ +package com.huawei.cloudphone.apiimpl; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Color; +import android.os.Handler; +import android.os.Looper; +import android.text.Editable; +import android.text.InputFilter; +import android.text.TextWatcher; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.PopupWindow; +import android.widget.RelativeLayout; + +import com.huawei.cloudphone.common.CASLog; + +public class CloudPhoneImeMgr { + private static final String TAG = "CloudPhoneImeMgr"; + + private static final int TEXT_SIZE = 14; + private static final int TEXT_MAX_LEN = 40; + private static final int TEXT_MAX_LINES = 1; + private static final int BOX_INPUT_HEIGHT = 50; + private static final int BUTTON_WIDTH = 65; + private static final int BUTTON_HEIGHT = 40; + private static final int IME_MSG_SHOW = 0; + private static final int IME_MSG_HIDE = 1; + private static final int IME_MSG_TEXT = 2; + private static final int IME_MSG_HEADER_LEN = 3; + + private ViewGroup mRootViewGroup; + private ViewGroup mImeViewGroup; + private Context mContext; + private EditText mImeEditText; + private PopupWindow mPopWindow; + private CloudPhoneTextWatchListener mTextWatchListener; + + @SuppressLint("Range") + public CloudPhoneImeMgr(Context context, ViewGroup viewGroup) { + mContext = context; + mRootViewGroup = viewGroup; + + mImeViewGroup = new RelativeLayout(context); + RelativeLayout.LayoutParams imeViewGroupLp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, dip2px(BOX_INPUT_HEIGHT)); + imeViewGroupLp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + mImeViewGroup.setLayoutParams(imeViewGroupLp); + mImeViewGroup.setBackgroundColor(Color.WHITE); + mImeViewGroup.setAlpha(50); + + mImeEditText = new EditText(context); + RelativeLayout.LayoutParams imeEditTextLp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); + mImeEditText.setLayoutParams(imeEditTextLp); + mImeEditText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); + mImeEditText.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); + mImeEditText.setHint("请输入"); + mImeEditText.setTextSize(TypedValue.COMPLEX_UNIT_PX, dip2px(TEXT_SIZE)); + mImeEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(TEXT_MAX_LEN)}); + mImeEditText.setMaxLines(TEXT_MAX_LINES); + mImeViewGroup.addView(mImeEditText); + + Button button = new Button(context); + RelativeLayout.LayoutParams marginLp = new RelativeLayout.LayoutParams(dip2px(BUTTON_WIDTH), dip2px(BUTTON_HEIGHT)); + marginLp.setMargins(0, dip2px(5), dip2px(10), 0); + RelativeLayout.LayoutParams buttonLp = new RelativeLayout.LayoutParams(marginLp); + buttonLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + button.setLayoutParams(buttonLp); + button.setText("下一步"); + button.setTextSize(TypedValue.COMPLEX_UNIT_PX, dip2px(12)); + mImeViewGroup.addView(button); + + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + hide(); + } + }); + + mImeEditText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + byte[] data = charSequence.toString().getBytes(); + byte[] msg = new byte[IME_MSG_HEADER_LEN + data.length]; + msg[0] = IME_MSG_TEXT; + msg[1] = (byte) ((data.length >> 8) & 0xFF); + msg[2] = (byte) (data.length & 0XFF); + + System.arraycopy(data, 0, msg, 3, data.length); + if (mTextWatchListener != null) { + mTextWatchListener.onTextChange(msg); + } + } + + @Override + public void afterTextChanged(Editable editable) { + } + }); + + mPopWindow = new PopupWindow(mImeViewGroup, WindowManager.LayoutParams.MATCH_PARENT, dip2px(BOX_INPUT_HEIGHT), true); + mPopWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); + mPopWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + mPopWindow.setOutsideTouchable(true); + mPopWindow.setTouchable(true); + } + + public int show(String text) { + if (mPopWindow.isShowing()) { + return 0; + } + if (text != null && text.length() > 0) { + mImeEditText.getText().replace(0, mImeEditText.length(), text); + } else { + mImeEditText.getText().clear(); + } + mPopWindow.setFocusable(true); + mImeEditText.requestFocus(); + mPopWindow.showAtLocation(mRootViewGroup, Gravity.BOTTOM, 0, 0); + + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + InputMethodManager inputMethodManager = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.showSoftInput(mImeEditText, 0); + } + }, 100); + return 0; + } + + public synchronized int hide() { + if (!mPopWindow.isShowing()) { + return 0; + } + mPopWindow.dismiss(); + InputMethodManager inputMethodManager = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.hideSoftInputFromWindow(mImeEditText.getWindowToken(), 0); + return 0; + } + + public void processImeMsg(byte[] data) { + if (data == null) { + return; + } + final byte type = data[0]; + int msgLen = (data[1] << 8) | (data[2] & 0xFF); + String text = null; + if (msgLen > 0) { + text = new String(data,3, msgLen); + } + final String finalText = text; + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + switch (type) { + case IME_MSG_SHOW: + showKeyBoard(true, finalText); + break; + case IME_MSG_HIDE: + showKeyBoard(false, null); + default: + break; + } + } + }); + } + + public void setTextWatchListener(CloudPhoneTextWatchListener listener) { + mTextWatchListener = listener; + } + + private void showKeyBoard(boolean isShow, String text) { + if (isShow) { + this.show(text); + } else { + this.hide(); + } + } + + private int dip2px(float dipValue) { + final float scale = mContext.getResources().getDisplayMetrics().density; + return (int) (dipValue * scale + 0.5f); + } +} \ No newline at end of file diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java index 3bec087..35b44d8 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java @@ -90,6 +90,8 @@ public class CloudPhoneImpl implements ICloudPhone { private static final int CMD_RECONNECT = 16; private static final int CMD_SET_MEDIA_CONFIG = 17; private static final byte CUSTOM_DATA = 18; + private static final int CMD_ENABLE_REMOTE_IME = 19; + private static final byte IME_DATA =14; private final Object mCloudPhoneLock = new Object(); private CASClient mCASClient = null; @@ -132,6 +134,9 @@ public class CloudPhoneImpl implements ICloudPhone { private long initTime = 0; + private CloudPhoneImeMgr mImeMgr; + private boolean mIsEnableRemoteIme = false; + public CloudPhoneImpl() { mCurrentState = STATE_DEINIT; } @@ -324,6 +329,19 @@ public class CloudPhoneImpl implements ICloudPhone { } } + @Override + public int enableRemoteIme(boolean isEnable) { + if (mCmdHandler == null) { + return -1; + } + mIsEnableRemoteIme = isEnable; + Message msg = new Message(); + msg.what = CMD_ENABLE_REMOTE_IME; + msg.obj = Boolean.valueOf(isEnable); + mCmdHandler.sendMessage(msg); + return 0; + } + public void handleStartCmd(final Activity activity, final ViewGroup views) { if (null == activity) { CASLog.e(TAG, "handle start cmd fail, activity is null."); @@ -488,6 +506,10 @@ public class CloudPhoneImpl implements ICloudPhone { FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); mSurfaceView.setLayoutParams(layoutParams); + if (mIsEnableRemoteIme) { + mImeMgr = new CloudPhoneImeMgr(mContext, views); + mImeMgr.setTextWatchListener(new TextWatchListener()); + } } }); } @@ -652,6 +674,10 @@ public class CloudPhoneImpl implements ICloudPhone { } } + private void handleEnableRemoteImeCmd(boolean isEnable) { + mCASClient.enableRemoteIme(isEnable); + } + private boolean sendTouchEvent(int id, int action, final int x1, final int y1, final int pressure, long time) { int orientation = 0; int new_orientation = 0; @@ -743,6 +769,10 @@ public class CloudPhoneImpl implements ICloudPhone { CASLog.i(TAG, "CMD_RECONNECT"); handleReconnectCmd(); break; + case CMD_ENABLE_REMOTE_IME: + CASLog.i(TAG, "CMD_ENABLE_REMOTE_IME"); + handleEnableRemoteImeCmd((boolean) msg.obj); + break; default: break; } @@ -788,6 +818,12 @@ public class CloudPhoneImpl implements ICloudPhone { mVirtualDevDataListener.onRecvVirtualDevData(data, data.length); } } + + public void onImeMsgRecv(byte[] data) throws RemoteException { + if (mImeMgr != null) { + mImeMgr.processImeMsg(data); + } + } } private class BackgroundTimerTask extends TimerTask { @@ -861,4 +897,11 @@ public class CloudPhoneImpl implements ICloudPhone { } } + class TextWatchListener implements CloudPhoneTextWatchListener { + @Override + public void onTextChange(byte[] data) { + mCASClient.sendDataToIme(data); + } + } + } \ No newline at end of file diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneTextWatchListener.java b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneTextWatchListener.java new file mode 100644 index 0000000..3fb505f --- /dev/null +++ b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneTextWatchListener.java @@ -0,0 +1,5 @@ +package com.huawei.cloudphone.apiimpl; + +public interface CloudPhoneTextWatchListener { + void onTextChange(byte[] data); +} diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java index ccf80ac..0c2f393 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java @@ -46,6 +46,7 @@ public class JNIWrapper { public static final byte RECORDER = 11; + public static final byte IMEDATA = 14; public static final byte RECONNECT = 15; public static final byte RECONNECT_HEAD = 16; public static final byte NOTIFY = 17; @@ -90,4 +91,5 @@ public class JNIWrapper { public static native boolean setRotation(int rotation); + public static native boolean enableRemoteIme(boolean isEnable); } \ No newline at end of file diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java index 566e030..89045dc 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java @@ -98,5 +98,8 @@ public class JniBridge { } + public boolean enableRemoteIme(boolean isEnable) { + return JNIWrapper.enableRemoteIme(isEnable); + } } diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/service/CASClient.java b/cloudphone/src/main/java/com/huawei/cloudphone/service/CASClient.java index 1bef8cd..3a83c62 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/service/CASClient.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/service/CASClient.java @@ -17,6 +17,7 @@ package com.huawei.cloudphone.service; import static com.huawei.cloudphone.jniwrapper.JNIWrapper.CHANNEL; +import static com.huawei.cloudphone.jniwrapper.JNIWrapper.IMEDATA; import android.os.IBinder; import android.os.RemoteException; @@ -339,4 +340,29 @@ public class CASClient { CASLog.e(TAG, "failed to send data to virtual device."); } } + + public void sendDataToIme(byte[] data) { + if (mCasInterface == null) { + return; + } + try { + mCasInterface.sendData(IMEDATA, data); + } catch (RemoteException e) { + CASLog.e(TAG, "failed to send data to virtual device."); + } + } + + public int enableRemoteIme(boolean isEnable) { + if (mCasInterface == null) { + return -1; + } + int ret = 0; + try { + mCasInterface.enableRemoteIme(isEnable); + } catch (RemoteException e) { + CASLog.e(TAG, "failed to enable remote ime."); + ret = -1; + } + return ret; + } } diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java b/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java index a189ad4..ec45d7c 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java @@ -88,6 +88,7 @@ public class CasProcessor extends ICASAidlInterface.Stub { private ICASAidlListener mListener = null; private NewRotationDirectionPacket mNewRotationDirPkt; private NewVirtualDevDataPacket mVirtualDevDataPkt; + private NewImeDataPacket mImeDataPkt; @Override public void init() throws RemoteException { @@ -216,11 +217,13 @@ public class CasProcessor extends ICASAidlInterface.Stub { mNewRotationDirPkt = new NewRotationDirectionPacket(); mChannelDataCallback = new NewChannelDataPacket(); mVirtualDevDataPkt = new NewVirtualDevDataPacket(); + mImeDataPkt = new NewImeDataPacket(); mUpstreamReceiveDispatcher.addNewPacketCallback((byte) JNIWrapper.ORIENTATION, mNewRotationDirPkt); mUpstreamReceiveDispatcher.addNewPacketCallback((byte) JNIWrapper.AUDIO, mAudioTrackerCallback); mUpstreamReceiveDispatcher.addNewPacketCallback((byte) JNIWrapper.CHANNEL, mChannelDataCallback); mUpstreamReceiveDispatcher.addNewPacketCallback((byte) JNIWrapper.VIRTUAL_DEVICE_DATA, mVirtualDevDataPkt); + mUpstreamReceiveDispatcher.addNewPacketCallback((byte) JNIWrapper.IMEDATA, mImeDataPkt); mUpstreamReceiveDispatcher.start(); return true; } @@ -232,6 +235,7 @@ public class CasProcessor extends ICASAidlInterface.Stub { mUpstreamReceiveDispatcher.deleteNewPacketCallback((byte) JNIWrapper.ORIENTATION); mUpstreamReceiveDispatcher.deleteNewPacketCallback((byte) JNIWrapper.CHANNEL); mUpstreamReceiveDispatcher.deleteNewPacketCallback((byte) JNIWrapper.VIRTUAL_DEVICE_DATA); + mUpstreamReceiveDispatcher.deleteNewPacketCallback((byte) JNIWrapper.IMEDATA); mUpstreamReceiveDispatcher.stopBlocked(); mAudioTrackerCallback.closeAudioTrack(); mUpstreamReceiveDispatcher = null; @@ -272,6 +276,11 @@ public class CasProcessor extends ICASAidlInterface.Stub { JniBridge.getInstance().sendData(devType, data, data.length); } + @Override + public boolean enableRemoteIme(boolean isEnable) throws RemoteException { + return JniBridge.getInstance().enableRemoteIme(isEnable); + } + /** * build verify data info */ @@ -353,4 +362,17 @@ public class CasProcessor extends ICASAidlInterface.Stub { } } } + + private class NewImeDataPacket implements NewPacketCallback { + @Override + public void onNewPacket(byte[] data) { + if (mListener != null) { + try { + mListener.onImeMsgRecv(data); + } catch (RemoteException e) { + CASLog.e(TAG, "call onImeMsgRecv failed."); + } + } + } + } } -- Gitee From 408cba4e170f45a93ef0817b00a6835a6014ad04 Mon Sep 17 00:00:00 2001 From: CaiFeng <2397707574@qq.com> Date: Fri, 30 Jun 2023 17:04:12 +0800 Subject: [PATCH 2/5] support remote ime --- .../cloudapp/ui/CasCloudPhoneActivity.java | 1 - .../huawei/cloudphone/ICASAidlInterface.aidl | 2 -- cloudphone/src/main/cpp/CasController.cpp | 15 ------------- cloudphone/src/main/cpp/CasController.h | 3 --- cloudphone/src/main/cpp/CasJniBridge.cpp | 5 ----- cloudphone/src/main/cpp/CasJniBridge.h | 2 -- .../huawei/cloudphone/api/ICloudPhone.java | 7 ------ .../cloudphone/apiimpl/CloudPhoneImpl.java | 22 ------------------- .../cloudphone/jniwrapper/JNIWrapper.java | 2 -- .../cloudphone/jniwrapper/JniBridge.java | 6 ----- .../huawei/cloudphone/service/CASClient.java | 13 ----------- .../cloudphone/service/CasProcessor.java | 5 ----- 12 files changed, 83 deletions(-) diff --git a/app/src/main/java/com/huawei/cloudapp/ui/CasCloudPhoneActivity.java b/app/src/main/java/com/huawei/cloudapp/ui/CasCloudPhoneActivity.java index 88d4933..424cbe9 100644 --- a/app/src/main/java/com/huawei/cloudapp/ui/CasCloudPhoneActivity.java +++ b/app/src/main/java/com/huawei/cloudapp/ui/CasCloudPhoneActivity.java @@ -308,7 +308,6 @@ public class CasCloudPhoneActivity extends FragmentActivity implements View.OnCl mCloudPhone.setMediaConfig(mediaConfig); } - mCloudPhone.enableRemoteIme(true); mCloudPhone.startCloudPhone(this, mFrameLayout, parasMap); } catch (Exception e) { CASLog.e(TAG, "startCloudApp start failed." + e.getMessage()); diff --git a/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlInterface.aidl b/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlInterface.aidl index 67e47a1..21b9d45 100644 --- a/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlInterface.aidl +++ b/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlInterface.aidl @@ -73,6 +73,4 @@ interface ICASAidlInterface { void mute(in boolean isMute); void sendData(byte type, in byte[] data); - - boolean enableRemoteIme(boolean isEnableRemote); } diff --git a/cloudphone/src/main/cpp/CasController.cpp b/cloudphone/src/main/cpp/CasController.cpp index d32f339..198516a 100644 --- a/cloudphone/src/main/cpp/CasController.cpp +++ b/cloudphone/src/main/cpp/CasController.cpp @@ -182,13 +182,6 @@ bool CasController::Start(ANativeWindow *nativeWindow, bool isHome) StartWorkers(!m_retainVideoDecode); - std::string isEnableIme; - if (m_isEnableRemoteIme) { - isEnableIme = "1"; - } else { - isEnableIme = "0"; - } - std::string startCmd = CMD_START_APP; map parameters = { { KEY_COMMAND, startCmd }, { KEY_TICKET, m_ticket }, @@ -201,7 +194,6 @@ bool CasController::Start(ANativeWindow *nativeWindow, bool isHome) { KEY_PROTOCOL_VERSION, m_conf.protocolVersion }, { KEY_CLIENT_TYPE, m_clientType }, { KEY_MEDIA_CONFIG, mediaConfigStr }, - { KEY_REMOTE_IME, isEnableIme }, { KEY_MAX_DISCONNECT_DURATION, m_maxDisconnectDuration } }; res = SendCommand(parameters); @@ -289,7 +281,6 @@ bool CasController::Stop(bool isHome) CloseDataStream(); m_isNotifyFirstFrame = false; - m_isEnableRemoteIme = false; m_mediaConfig.clear(); m_jniConf.clear(); return true; @@ -954,12 +945,6 @@ void CasController::NotifyFirstVideoFrame() } } -bool CasController::EnableRemoteIme(bool isEnable) -{ - m_isEnableRemoteIme = isEnable; - return true; -} - void CasController::OnCmdRecv(int code, string msg) { NotifyCommand(code, msg); diff --git a/cloudphone/src/main/cpp/CasController.h b/cloudphone/src/main/cpp/CasController.h index 1be054f..835a883 100644 --- a/cloudphone/src/main/cpp/CasController.h +++ b/cloudphone/src/main/cpp/CasController.h @@ -78,8 +78,6 @@ public: void NotifyFirstVideoFrame(); - bool EnableRemoteIme(bool isEnable); - private: bool Release(); @@ -161,7 +159,6 @@ private: const bool m_retainVideoDecode = true; const bool m_needVideoDecode = true; bool m_isNotifyFirstFrame = false; - bool m_isEnableRemoteIme = false; std::map m_mediaConfig; }; diff --git a/cloudphone/src/main/cpp/CasJniBridge.cpp b/cloudphone/src/main/cpp/CasJniBridge.cpp index 6200951..94faf44 100644 --- a/cloudphone/src/main/cpp/CasJniBridge.cpp +++ b/cloudphone/src/main/cpp/CasJniBridge.cpp @@ -176,11 +176,6 @@ extern "C" JNIEXPORT jint JNICALL JNI(sendData)(JNIEnv *env, jclass clazz, jbyte return ret; } -extern "C" JNIEXPORT jboolean JNICALL JNI(enableRemoteIme)(JNIEnv *env, jclass, jboolean isEnable) -{ - return gJniApiCtrl->EnableRemoteIme(isEnable); -} - extern "C" JNIEXPORT jboolean JNICALL JNI(sendTouchEvent)(JNIEnv *env, jclass, jint id, jint action, jint x, jint y, jint pressure, jlong time, jint orientation, jint height, jint width) { diff --git a/cloudphone/src/main/cpp/CasJniBridge.h b/cloudphone/src/main/cpp/CasJniBridge.h index 2e15786..47a4c8d 100644 --- a/cloudphone/src/main/cpp/CasJniBridge.h +++ b/cloudphone/src/main/cpp/CasJniBridge.h @@ -50,8 +50,6 @@ JNIEXPORT jboolean JNICALL JNI(sendMotionEvent)(JNIEnv *env, jclass, jint master JNIEXPORT jboolean JNICALL JNI(setRotation)(JNIEnv *, jclass, int h); -JNIEXPORT jboolean JNICALL JNI(enableRemoteIme)(JNIEnv *, jclass, jboolean isEnable); - JNIEXPORT jboolean JNICALL JNI(getConnectStatus)(JNIEnv *env, jclass); } #endif // CLOUDAPPSDK_JNIRENDER_H \ No newline at end of file diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhone.java b/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhone.java index f81658d..5cf371a 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhone.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhone.java @@ -135,11 +135,4 @@ public interface ICloudPhone { * @return 网络时延 */ int getRtt(); - - /** - * 使能远端输入法 - * - * @param isEnable - */ - int enableRemoteIme(boolean isEnable); } diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java index 35b44d8..10451c0 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java @@ -90,7 +90,6 @@ public class CloudPhoneImpl implements ICloudPhone { private static final int CMD_RECONNECT = 16; private static final int CMD_SET_MEDIA_CONFIG = 17; private static final byte CUSTOM_DATA = 18; - private static final int CMD_ENABLE_REMOTE_IME = 19; private static final byte IME_DATA =14; private final Object mCloudPhoneLock = new Object(); @@ -329,19 +328,6 @@ public class CloudPhoneImpl implements ICloudPhone { } } - @Override - public int enableRemoteIme(boolean isEnable) { - if (mCmdHandler == null) { - return -1; - } - mIsEnableRemoteIme = isEnable; - Message msg = new Message(); - msg.what = CMD_ENABLE_REMOTE_IME; - msg.obj = Boolean.valueOf(isEnable); - mCmdHandler.sendMessage(msg); - return 0; - } - public void handleStartCmd(final Activity activity, final ViewGroup views) { if (null == activity) { CASLog.e(TAG, "handle start cmd fail, activity is null."); @@ -674,10 +660,6 @@ public class CloudPhoneImpl implements ICloudPhone { } } - private void handleEnableRemoteImeCmd(boolean isEnable) { - mCASClient.enableRemoteIme(isEnable); - } - private boolean sendTouchEvent(int id, int action, final int x1, final int y1, final int pressure, long time) { int orientation = 0; int new_orientation = 0; @@ -769,10 +751,6 @@ public class CloudPhoneImpl implements ICloudPhone { CASLog.i(TAG, "CMD_RECONNECT"); handleReconnectCmd(); break; - case CMD_ENABLE_REMOTE_IME: - CASLog.i(TAG, "CMD_ENABLE_REMOTE_IME"); - handleEnableRemoteImeCmd((boolean) msg.obj); - break; default: break; } diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java index 0c2f393..ce3d4d3 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java @@ -90,6 +90,4 @@ public class JNIWrapper { public static native boolean setMediaConfig(HashMap mediaConfigMap); public static native boolean setRotation(int rotation); - - public static native boolean enableRemoteIme(boolean isEnable); } \ No newline at end of file diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java index 89045dc..02dab1a 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java @@ -96,10 +96,4 @@ public class JniBridge { public boolean setRotation(int rotation) { return JNIWrapper.setRotation(rotation); } - - - public boolean enableRemoteIme(boolean isEnable) { - return JNIWrapper.enableRemoteIme(isEnable); - } - } diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/service/CASClient.java b/cloudphone/src/main/java/com/huawei/cloudphone/service/CASClient.java index 3a83c62..512428c 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/service/CASClient.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/service/CASClient.java @@ -352,17 +352,4 @@ public class CASClient { } } - public int enableRemoteIme(boolean isEnable) { - if (mCasInterface == null) { - return -1; - } - int ret = 0; - try { - mCasInterface.enableRemoteIme(isEnable); - } catch (RemoteException e) { - CASLog.e(TAG, "failed to enable remote ime."); - ret = -1; - } - return ret; - } } diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java b/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java index ec45d7c..1d9de02 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java @@ -276,11 +276,6 @@ public class CasProcessor extends ICASAidlInterface.Stub { JniBridge.getInstance().sendData(devType, data, data.length); } - @Override - public boolean enableRemoteIme(boolean isEnable) throws RemoteException { - return JniBridge.getInstance().enableRemoteIme(isEnable); - } - /** * build verify data info */ -- Gitee From 3d6add839e3da71d0b2ea38f443fc7c1bcd4e907 Mon Sep 17 00:00:00 2001 From: CaiFeng <2397707574@qq.com> Date: Fri, 30 Jun 2023 17:08:08 +0800 Subject: [PATCH 3/5] support remote ime --- .../com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java | 8 +++----- .../java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java | 1 + .../java/com/huawei/cloudphone/jniwrapper/JniBridge.java | 3 +++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java index 10451c0..ffee3d7 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java @@ -134,7 +134,6 @@ public class CloudPhoneImpl implements ICloudPhone { private long initTime = 0; private CloudPhoneImeMgr mImeMgr; - private boolean mIsEnableRemoteIme = false; public CloudPhoneImpl() { mCurrentState = STATE_DEINIT; @@ -492,10 +491,9 @@ public class CloudPhoneImpl implements ICloudPhone { FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); mSurfaceView.setLayoutParams(layoutParams); - if (mIsEnableRemoteIme) { - mImeMgr = new CloudPhoneImeMgr(mContext, views); - mImeMgr.setTextWatchListener(new TextWatchListener()); - } + + mImeMgr = new CloudPhoneImeMgr(mContext, views); + mImeMgr.setTextWatchListener(new TextWatchListener()); } }); } diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java index ce3d4d3..0ea60bd 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java @@ -90,4 +90,5 @@ public class JNIWrapper { public static native boolean setMediaConfig(HashMap mediaConfigMap); public static native boolean setRotation(int rotation); + } \ No newline at end of file diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java index 02dab1a..566e030 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java @@ -96,4 +96,7 @@ public class JniBridge { public boolean setRotation(int rotation) { return JNIWrapper.setRotation(rotation); } + + + } -- Gitee From e2e40e0fada6c400fde6b2ed72a71d9686fb3772 Mon Sep 17 00:00:00 2001 From: CaiFeng <2397707574@qq.com> Date: Tue, 6 Jun 2023 18:32:17 +0800 Subject: [PATCH 4/5] support remote ime --- .../huawei/cloudphone/ICASAidlListener.aidl | 2 + cloudphone/src/main/cpp/CasCommon.h | 1 + cloudphone/src/main/cpp/CasController.cpp | 23 +++ cloudphone/src/main/cpp/CasController.h | 1 + cloudphone/src/main/cpp/CasJniBridge.cpp | 1 - cloudphone/src/main/cpp/cas_common/CasMsg.h | 2 + .../cpp/cas_stream/CasStreamBuildSender.cpp | 3 + .../cloudphone/apiimpl/CloudPhoneImeMgr.java | 193 ++++++++++++++++++ .../cloudphone/apiimpl/CloudPhoneImpl.java | 19 ++ .../apiimpl/CloudPhoneTextWatchListener.java | 5 + .../cloudphone/jniwrapper/JNIWrapper.java | 1 + .../huawei/cloudphone/service/CASClient.java | 13 ++ .../cloudphone/service/CasProcessor.java | 17 ++ 13 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImeMgr.java create mode 100644 cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneTextWatchListener.java diff --git a/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlListener.aidl b/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlListener.aidl index c3a9fee..948d7c2 100644 --- a/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlListener.aidl +++ b/cloudphone/src/main/aidl/com/huawei/cloudphone/ICASAidlListener.aidl @@ -28,4 +28,6 @@ interface ICASAidlListener { void onChannelDataRecv(in byte[] data); void onVirtualDevDataRecv(in byte[] data); + + void onImeMsgRecv(in byte[] data); } diff --git a/cloudphone/src/main/cpp/CasCommon.h b/cloudphone/src/main/cpp/CasCommon.h index 3b75466..3c09ce2 100644 --- a/cloudphone/src/main/cpp/CasCommon.h +++ b/cloudphone/src/main/cpp/CasCommon.h @@ -33,6 +33,7 @@ const std::string KEY_AVAILABLE_PLAYTIME = "available_playtime"; const std::string KEY_CLIENT_TYPE = "client_type"; const std::string KEY_MEDIA_CONFIG = "media_config"; const std::string KEY_USER_ID = "user_id"; +const std::string KEY_REMOTE_IME = "remote_ime"; const std::string KEY_FRAME_RATE = "frame_rate"; const std::string KEY_FRAME_TYPE = "frame_type"; diff --git a/cloudphone/src/main/cpp/CasController.cpp b/cloudphone/src/main/cpp/CasController.cpp index 2b5a948..198516a 100644 --- a/cloudphone/src/main/cpp/CasController.cpp +++ b/cloudphone/src/main/cpp/CasController.cpp @@ -68,6 +68,7 @@ CasController::CasController() m_virtualDeviceStream = nullptr; m_controlStream = nullptr; m_channelStream = nullptr; + m_imeDataStream = nullptr; m_cmdController = nullptr; m_streamParseThread = nullptr; m_heartbeatThread = nullptr; @@ -89,6 +90,7 @@ CasController::~CasController() m_virtualDeviceStream = nullptr; m_controlStream = nullptr; m_channelStream = nullptr; + m_imeDataStream = nullptr; m_cmdController = nullptr; m_streamParseThread = nullptr; m_heartbeatThread = nullptr; @@ -405,6 +407,7 @@ bool CasController::CreateWorkers(ANativeWindow *nativeWindow, bool needVideoDec m_streamParser->SetServiceHandle(CasMsgType::Audio, m_audioPacketStream); m_streamParser->SetServiceHandle(CasMsgType::Channel, m_channelStream); m_streamParser->SetServiceHandle(CasMsgType::VirtualDevice, m_virtualDeviceStream); + m_streamParser->SetServiceHandle(CasMsgType::ImeData, m_imeDataStream); if (needVideoDecode) { m_videoDecodeThread = new (std::nothrow) CasVideoHDecodeThread(nativeWindow, m_frameType); @@ -606,6 +609,12 @@ bool CasController::InitDataStream() return false; } + m_imeDataStream = new (std::nothrow) CasDataPipe(); + if (m_imeDataStream == nullptr) { + ERR("Failed to new ime data packet stream."); + return false; + } + return true; } @@ -635,6 +644,10 @@ bool CasController::ClearDataStream() m_virtualDeviceStream->Clear(); } + if (m_imeDataStream != nullptr) { + m_imeDataStream->Clear(); + } + INFO("Succeed to clear data stream "); return true; } @@ -666,6 +679,11 @@ bool CasController::CloseDataStream() m_channelStream = nullptr; } + if (m_imeDataStream != nullptr) { + delete m_imeDataStream; + m_imeDataStream = nullptr; + } + INFO("Succeed to close data stream "); return true; } @@ -768,6 +786,11 @@ int CasController::JniRecvData(CasMsgType type, uint8_t *data, int length) pPkt = m_virtualDeviceStream->GetNextPkt(); } break; + case CasMsgType::ImeData: + if (m_imeDataStream != nullptr) { + pPkt = m_imeDataStream->GetNextPkt(); + } + break; default: ERR("Invalid type %d, length %d.", type, length); return 0; diff --git a/cloudphone/src/main/cpp/CasController.h b/cloudphone/src/main/cpp/CasController.h index 02b111d..835a883 100644 --- a/cloudphone/src/main/cpp/CasController.h +++ b/cloudphone/src/main/cpp/CasController.h @@ -126,6 +126,7 @@ private: CasDataPipe *m_controlStream = nullptr; CasDataPipe *m_channelStream = nullptr; CasDataPipe *m_virtualDeviceStream = nullptr; + CasDataPipe *m_imeDataStream = nullptr; CasCmdController *m_cmdController = nullptr; CasHeartbeatController *m_heartbeatController = nullptr; diff --git a/cloudphone/src/main/cpp/CasJniBridge.cpp b/cloudphone/src/main/cpp/CasJniBridge.cpp index 8c4afd1..94faf44 100644 --- a/cloudphone/src/main/cpp/CasJniBridge.cpp +++ b/cloudphone/src/main/cpp/CasJniBridge.cpp @@ -174,7 +174,6 @@ extern "C" JNIEXPORT jint JNICALL JNI(sendData)(JNIEnv *env, jclass clazz, jbyte int ret = gJniApiCtrl->JniSendData((CasMsgType)type, data, length); env->ReleaseByteArrayElements(jData, (jbyte *)data, JNI_COMMIT); return ret; - return 0; } extern "C" JNIEXPORT jboolean JNICALL JNI(sendTouchEvent)(JNIEnv *env, jclass, jint id, jint action, jint x, jint y, diff --git a/cloudphone/src/main/cpp/cas_common/CasMsg.h b/cloudphone/src/main/cpp/cas_common/CasMsg.h index b4a5d9a..fcea1f5 100644 --- a/cloudphone/src/main/cpp/cas_common/CasMsg.h +++ b/cloudphone/src/main/cpp/cas_common/CasMsg.h @@ -31,6 +31,7 @@ enum CasMsgType : uint8_t { HeartBeat = 8, Orientation = 9, Recorder = 11, + ImeData = 14, KeyEventInput = 15, MotionEventInput = 16, VirtualDevice = 20, @@ -53,6 +54,7 @@ enum CasMsgType : uint8_t { #define CAS_MSG_CHECKSUM_VIDEO GET_CAS_CHECKSUM(CasMsgType::Video) #define CAS_MSG_CHECKSUM_VERIFY GET_CAS_CHECKSUM(CasMsgType::Verify) #define CAS_MSG_CHECKSUM_HEARTBEAT GET_CAS_CHECKSUM(CasMsgType::HeartBeat) +#define CAS_MSG_CHECKSUM_IMEDATA GET_CAS_CHECKSUM(CasMsgType::ImeData) #define CAS_MSG_CHECKSUM_KEYEVENTINPUT GET_CAS_CHECKSUM(CasMsgType::KeyEventInput) #define CAS_MSG_CHECKSUM_MOTIONEVENTINPUT GET_CAS_CHECKSUM(CasMsgType::MotionEventInput) #define CAS_MSG_CHECKSUM_CHANNEL GET_CAS_CHECKSUM(CasMsgType::Channel) diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp index 1257654..92bbf8a 100644 --- a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp +++ b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp @@ -63,6 +63,9 @@ int CasStreamBuildSender::SendDataToServer(CasMsgType type, const void *buf, siz case (HeartBeat): msgHead.checksum = CAS_MSG_CHECKSUM_HEARTBEAT; break; + case (ImeData): + msgHead.checksum = CAS_MSG_CHECKSUM_IMEDATA; + break; case (KeyEventInput): msgHead.checksum = CAS_MSG_CHECKSUM_KEYEVENTINPUT; break; diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImeMgr.java b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImeMgr.java new file mode 100644 index 0000000..16a07a2 --- /dev/null +++ b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImeMgr.java @@ -0,0 +1,193 @@ +package com.huawei.cloudphone.apiimpl; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Color; +import android.os.Handler; +import android.os.Looper; +import android.text.Editable; +import android.text.InputFilter; +import android.text.TextWatcher; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.PopupWindow; +import android.widget.RelativeLayout; + +import com.huawei.cloudphone.common.CASLog; + +public class CloudPhoneImeMgr { + private static final String TAG = "CloudPhoneImeMgr"; + + private static final int TEXT_SIZE = 14; + private static final int TEXT_MAX_LEN = 40; + private static final int TEXT_MAX_LINES = 1; + private static final int BOX_INPUT_HEIGHT = 50; + private static final int BUTTON_WIDTH = 65; + private static final int BUTTON_HEIGHT = 40; + private static final int IME_MSG_SHOW = 0; + private static final int IME_MSG_HIDE = 1; + private static final int IME_MSG_TEXT = 2; + private static final int IME_MSG_HEADER_LEN = 3; + + private ViewGroup mRootViewGroup; + private ViewGroup mImeViewGroup; + private Context mContext; + private EditText mImeEditText; + private PopupWindow mPopWindow; + private CloudPhoneTextWatchListener mTextWatchListener; + + @SuppressLint("Range") + public CloudPhoneImeMgr(Context context, ViewGroup viewGroup) { + mContext = context; + mRootViewGroup = viewGroup; + + mImeViewGroup = new RelativeLayout(context); + RelativeLayout.LayoutParams imeViewGroupLp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, dip2px(BOX_INPUT_HEIGHT)); + imeViewGroupLp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + mImeViewGroup.setLayoutParams(imeViewGroupLp); + mImeViewGroup.setBackgroundColor(Color.WHITE); + mImeViewGroup.setAlpha(50); + + mImeEditText = new EditText(context); + RelativeLayout.LayoutParams imeEditTextLp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); + mImeEditText.setLayoutParams(imeEditTextLp); + mImeEditText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); + mImeEditText.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); + mImeEditText.setHint("请输入"); + mImeEditText.setTextSize(TypedValue.COMPLEX_UNIT_PX, dip2px(TEXT_SIZE)); + mImeEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(TEXT_MAX_LEN)}); + mImeEditText.setMaxLines(TEXT_MAX_LINES); + mImeViewGroup.addView(mImeEditText); + + Button button = new Button(context); + RelativeLayout.LayoutParams marginLp = new RelativeLayout.LayoutParams(dip2px(BUTTON_WIDTH), dip2px(BUTTON_HEIGHT)); + marginLp.setMargins(0, dip2px(5), dip2px(10), 0); + RelativeLayout.LayoutParams buttonLp = new RelativeLayout.LayoutParams(marginLp); + buttonLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + button.setLayoutParams(buttonLp); + button.setText("下一步"); + button.setTextSize(TypedValue.COMPLEX_UNIT_PX, dip2px(12)); + mImeViewGroup.addView(button); + + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + hide(); + } + }); + + mImeEditText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + byte[] data = charSequence.toString().getBytes(); + byte[] msg = new byte[IME_MSG_HEADER_LEN + data.length]; + msg[0] = IME_MSG_TEXT; + msg[1] = (byte) ((data.length >> 8) & 0xFF); + msg[2] = (byte) (data.length & 0XFF); + + System.arraycopy(data, 0, msg, 3, data.length); + if (mTextWatchListener != null) { + mTextWatchListener.onTextChange(msg); + } + } + + @Override + public void afterTextChanged(Editable editable) { + } + }); + + mPopWindow = new PopupWindow(mImeViewGroup, WindowManager.LayoutParams.MATCH_PARENT, dip2px(BOX_INPUT_HEIGHT), true); + mPopWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); + mPopWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + mPopWindow.setOutsideTouchable(true); + mPopWindow.setTouchable(true); + } + + public int show(String text) { + if (mPopWindow.isShowing()) { + return 0; + } + if (text != null && text.length() > 0) { + mImeEditText.getText().replace(0, mImeEditText.length(), text); + } else { + mImeEditText.getText().clear(); + } + mPopWindow.setFocusable(true); + mImeEditText.requestFocus(); + mPopWindow.showAtLocation(mRootViewGroup, Gravity.BOTTOM, 0, 0); + + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + InputMethodManager inputMethodManager = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.showSoftInput(mImeEditText, 0); + } + }, 100); + return 0; + } + + public synchronized int hide() { + if (!mPopWindow.isShowing()) { + return 0; + } + mPopWindow.dismiss(); + InputMethodManager inputMethodManager = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.hideSoftInputFromWindow(mImeEditText.getWindowToken(), 0); + return 0; + } + + public void processImeMsg(byte[] data) { + if (data == null) { + return; + } + final byte type = data[0]; + int msgLen = (data[1] << 8) | (data[2] & 0xFF); + String text = null; + if (msgLen > 0) { + text = new String(data,3, msgLen); + } + final String finalText = text; + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + switch (type) { + case IME_MSG_SHOW: + showKeyBoard(true, finalText); + break; + case IME_MSG_HIDE: + showKeyBoard(false, null); + default: + break; + } + } + }); + } + + public void setTextWatchListener(CloudPhoneTextWatchListener listener) { + mTextWatchListener = listener; + } + + private void showKeyBoard(boolean isShow, String text) { + if (isShow) { + this.show(text); + } else { + this.hide(); + } + } + + private int dip2px(float dipValue) { + final float scale = mContext.getResources().getDisplayMetrics().density; + return (int) (dipValue * scale + 0.5f); + } +} \ No newline at end of file diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java index 3bec087..ffee3d7 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java @@ -90,6 +90,7 @@ public class CloudPhoneImpl implements ICloudPhone { private static final int CMD_RECONNECT = 16; private static final int CMD_SET_MEDIA_CONFIG = 17; private static final byte CUSTOM_DATA = 18; + private static final byte IME_DATA =14; private final Object mCloudPhoneLock = new Object(); private CASClient mCASClient = null; @@ -132,6 +133,8 @@ public class CloudPhoneImpl implements ICloudPhone { private long initTime = 0; + private CloudPhoneImeMgr mImeMgr; + public CloudPhoneImpl() { mCurrentState = STATE_DEINIT; } @@ -488,6 +491,9 @@ public class CloudPhoneImpl implements ICloudPhone { FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); mSurfaceView.setLayoutParams(layoutParams); + + mImeMgr = new CloudPhoneImeMgr(mContext, views); + mImeMgr.setTextWatchListener(new TextWatchListener()); } }); } @@ -788,6 +794,12 @@ public class CloudPhoneImpl implements ICloudPhone { mVirtualDevDataListener.onRecvVirtualDevData(data, data.length); } } + + public void onImeMsgRecv(byte[] data) throws RemoteException { + if (mImeMgr != null) { + mImeMgr.processImeMsg(data); + } + } } private class BackgroundTimerTask extends TimerTask { @@ -861,4 +873,11 @@ public class CloudPhoneImpl implements ICloudPhone { } } + class TextWatchListener implements CloudPhoneTextWatchListener { + @Override + public void onTextChange(byte[] data) { + mCASClient.sendDataToIme(data); + } + } + } \ No newline at end of file diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneTextWatchListener.java b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneTextWatchListener.java new file mode 100644 index 0000000..3fb505f --- /dev/null +++ b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneTextWatchListener.java @@ -0,0 +1,5 @@ +package com.huawei.cloudphone.apiimpl; + +public interface CloudPhoneTextWatchListener { + void onTextChange(byte[] data); +} diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java index ccf80ac..0ea60bd 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java @@ -46,6 +46,7 @@ public class JNIWrapper { public static final byte RECORDER = 11; + public static final byte IMEDATA = 14; public static final byte RECONNECT = 15; public static final byte RECONNECT_HEAD = 16; public static final byte NOTIFY = 17; diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/service/CASClient.java b/cloudphone/src/main/java/com/huawei/cloudphone/service/CASClient.java index 1bef8cd..512428c 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/service/CASClient.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/service/CASClient.java @@ -17,6 +17,7 @@ package com.huawei.cloudphone.service; import static com.huawei.cloudphone.jniwrapper.JNIWrapper.CHANNEL; +import static com.huawei.cloudphone.jniwrapper.JNIWrapper.IMEDATA; import android.os.IBinder; import android.os.RemoteException; @@ -339,4 +340,16 @@ public class CASClient { CASLog.e(TAG, "failed to send data to virtual device."); } } + + public void sendDataToIme(byte[] data) { + if (mCasInterface == null) { + return; + } + try { + mCasInterface.sendData(IMEDATA, data); + } catch (RemoteException e) { + CASLog.e(TAG, "failed to send data to virtual device."); + } + } + } diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java b/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java index a189ad4..1d9de02 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java @@ -88,6 +88,7 @@ public class CasProcessor extends ICASAidlInterface.Stub { private ICASAidlListener mListener = null; private NewRotationDirectionPacket mNewRotationDirPkt; private NewVirtualDevDataPacket mVirtualDevDataPkt; + private NewImeDataPacket mImeDataPkt; @Override public void init() throws RemoteException { @@ -216,11 +217,13 @@ public class CasProcessor extends ICASAidlInterface.Stub { mNewRotationDirPkt = new NewRotationDirectionPacket(); mChannelDataCallback = new NewChannelDataPacket(); mVirtualDevDataPkt = new NewVirtualDevDataPacket(); + mImeDataPkt = new NewImeDataPacket(); mUpstreamReceiveDispatcher.addNewPacketCallback((byte) JNIWrapper.ORIENTATION, mNewRotationDirPkt); mUpstreamReceiveDispatcher.addNewPacketCallback((byte) JNIWrapper.AUDIO, mAudioTrackerCallback); mUpstreamReceiveDispatcher.addNewPacketCallback((byte) JNIWrapper.CHANNEL, mChannelDataCallback); mUpstreamReceiveDispatcher.addNewPacketCallback((byte) JNIWrapper.VIRTUAL_DEVICE_DATA, mVirtualDevDataPkt); + mUpstreamReceiveDispatcher.addNewPacketCallback((byte) JNIWrapper.IMEDATA, mImeDataPkt); mUpstreamReceiveDispatcher.start(); return true; } @@ -232,6 +235,7 @@ public class CasProcessor extends ICASAidlInterface.Stub { mUpstreamReceiveDispatcher.deleteNewPacketCallback((byte) JNIWrapper.ORIENTATION); mUpstreamReceiveDispatcher.deleteNewPacketCallback((byte) JNIWrapper.CHANNEL); mUpstreamReceiveDispatcher.deleteNewPacketCallback((byte) JNIWrapper.VIRTUAL_DEVICE_DATA); + mUpstreamReceiveDispatcher.deleteNewPacketCallback((byte) JNIWrapper.IMEDATA); mUpstreamReceiveDispatcher.stopBlocked(); mAudioTrackerCallback.closeAudioTrack(); mUpstreamReceiveDispatcher = null; @@ -353,4 +357,17 @@ public class CasProcessor extends ICASAidlInterface.Stub { } } } + + private class NewImeDataPacket implements NewPacketCallback { + @Override + public void onNewPacket(byte[] data) { + if (mListener != null) { + try { + mListener.onImeMsgRecv(data); + } catch (RemoteException e) { + CASLog.e(TAG, "call onImeMsgRecv failed."); + } + } + } + } } -- Gitee From 067af28617e1266eb935c8ca68843e6ec45e4511 Mon Sep 17 00:00:00 2001 From: CaiFeng <2397707574@qq.com> Date: Fri, 30 Jun 2023 20:20:53 +0800 Subject: [PATCH 5/5] support remote ime --- cloudphone/src/main/cpp/CasCommon.h | 1 - 1 file changed, 1 deletion(-) diff --git a/cloudphone/src/main/cpp/CasCommon.h b/cloudphone/src/main/cpp/CasCommon.h index 3c09ce2..3b75466 100644 --- a/cloudphone/src/main/cpp/CasCommon.h +++ b/cloudphone/src/main/cpp/CasCommon.h @@ -33,7 +33,6 @@ const std::string KEY_AVAILABLE_PLAYTIME = "available_playtime"; const std::string KEY_CLIENT_TYPE = "client_type"; const std::string KEY_MEDIA_CONFIG = "media_config"; const std::string KEY_USER_ID = "user_id"; -const std::string KEY_REMOTE_IME = "remote_ime"; const std::string KEY_FRAME_RATE = "frame_rate"; const std::string KEY_FRAME_TYPE = "frame_type"; -- Gitee