From d79088c18062ba36e2378c7c91ed5cc9e68490af Mon Sep 17 00:00:00 2001 From: omigamedev Date: Fri, 16 Jan 2026 23:12:08 +0100 Subject: [PATCH] create plugin --- Build/Android/project.properties | 14 + Build/Android/res/drawable-hdpi/icon.png | Bin 0 -> 2226 bytes Build/Android/res/drawable-ldpi/icon.png | Bin 0 -> 1011 bytes Build/Android/res/drawable-mdpi/icon.png | Bin 0 -> 1433 bytes Build/Android/res/drawable-xhdpi/icon.png | Bin 0 -> 3602 bytes Build/Android/res/drawable/downloadimageh.png | Bin 0 -> 22639 bytes Build/Android/res/drawable/downloadimagev.png | Bin 0 -> 22787 bytes Build/Android/res/drawable/icon.png | Bin 0 -> 1433 bytes .../res/drawable/splashscreen_landscape.png | Bin 0 -> 5224 bytes .../res/drawable/splashscreen_portrait.png | Bin 0 -> 5294 bytes .../com/epicgames/unreal/DownloadShim.java | 33 + .../omixlab/MosisUnreal/AlarmReceiver.java | 44 + .../MosisUnreal/DownloaderActivity.java | 809 ++++++++++++++++++ .../src/com/omixlab/MosisUnreal/OBBData.java | 26 + .../MosisUnreal/OBBDownloaderService.java | 66 ++ Config/DefaultEngine.ini | 3 + Content/VRTemplate/Maps/VRTemplateMap.umap | 4 +- .../Maps/VRTemplateMap_BuiltData.uasset | 4 +- MosisUnreal.uproject | 2 +- Plugins/MosisSDK/MosisSDK.uplugin | 24 + Plugins/MosisSDK/Resources/Icon128.png | Bin 0 -> 12699 bytes .../AIDL/android/hardware/HardwareBuffer.aidl | 3 + .../com/omixlab/mosis/IMosisListener.aidl | 10 + .../AIDL/com/omixlab/mosis/IMosisService.aidl | 10 + .../aidl/com/omixlab/mosis/BnMosisListener.h | 56 ++ .../aidl/com/omixlab/mosis/BnMosisService.h | 59 ++ .../aidl/com/omixlab/mosis/BpMosisListener.h | 31 + .../aidl/com/omixlab/mosis/BpMosisService.h | 32 + .../aidl/com/omixlab/mosis/IMosisListener.h | 61 ++ .../aidl/com/omixlab/mosis/IMosisService.h | 67 ++ .../com/omixlab/mosis/IMosisListener.cpp | 242 ++++++ .../com/omixlab/mosis/IMosisService.cpp | 360 ++++++++ .../Java/com/omixlab/mosis/MyKotlinSource.kt | 45 + .../Source/MosisSDK/MosisSDK.Build.cs | 70 ++ .../MosisSDK/Source/MosisSDK/MosisSDK_UPL.xml | 50 ++ .../Source/MosisSDK/Private/MosisSDK.cpp | 46 + .../Source/MosisSDK/Public/MosisSDK.h | 15 + 37 files changed, 2181 insertions(+), 5 deletions(-) create mode 100644 Build/Android/project.properties create mode 100644 Build/Android/res/drawable-hdpi/icon.png create mode 100644 Build/Android/res/drawable-ldpi/icon.png create mode 100644 Build/Android/res/drawable-mdpi/icon.png create mode 100644 Build/Android/res/drawable-xhdpi/icon.png create mode 100644 Build/Android/res/drawable/downloadimageh.png create mode 100644 Build/Android/res/drawable/downloadimagev.png create mode 100644 Build/Android/res/drawable/icon.png create mode 100644 Build/Android/res/drawable/splashscreen_landscape.png create mode 100644 Build/Android/res/drawable/splashscreen_portrait.png create mode 100644 Build/Android/src/com/epicgames/unreal/DownloadShim.java create mode 100644 Build/Android/src/com/omixlab/MosisUnreal/AlarmReceiver.java create mode 100644 Build/Android/src/com/omixlab/MosisUnreal/DownloaderActivity.java create mode 100644 Build/Android/src/com/omixlab/MosisUnreal/OBBData.java create mode 100644 Build/Android/src/com/omixlab/MosisUnreal/OBBDownloaderService.java create mode 100644 Plugins/MosisSDK/MosisSDK.uplugin create mode 100644 Plugins/MosisSDK/Resources/Icon128.png create mode 100644 Plugins/MosisSDK/Source/MosisSDK/AIDL/android/hardware/HardwareBuffer.aidl create mode 100644 Plugins/MosisSDK/Source/MosisSDK/AIDL/com/omixlab/mosis/IMosisListener.aidl create mode 100644 Plugins/MosisSDK/Source/MosisSDK/AIDL/com/omixlab/mosis/IMosisService.aidl create mode 100644 Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BnMosisListener.h create mode 100644 Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BnMosisService.h create mode 100644 Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BpMosisListener.h create mode 100644 Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BpMosisService.h create mode 100644 Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/IMosisListener.h create mode 100644 Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/IMosisService.h create mode 100644 Plugins/MosisSDK/Source/MosisSDK/Generated/com/omixlab/mosis/IMosisListener.cpp create mode 100644 Plugins/MosisSDK/Source/MosisSDK/Generated/com/omixlab/mosis/IMosisService.cpp create mode 100644 Plugins/MosisSDK/Source/MosisSDK/Java/com/omixlab/mosis/MyKotlinSource.kt create mode 100644 Plugins/MosisSDK/Source/MosisSDK/MosisSDK.Build.cs create mode 100644 Plugins/MosisSDK/Source/MosisSDK/MosisSDK_UPL.xml create mode 100644 Plugins/MosisSDK/Source/MosisSDK/Private/MosisSDK.cpp create mode 100644 Plugins/MosisSDK/Source/MosisSDK/Public/MosisSDK.h diff --git a/Build/Android/project.properties b/Build/Android/project.properties new file mode 100644 index 0000000..738e84e --- /dev/null +++ b/Build/Android/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-19 diff --git a/Build/Android/res/drawable-hdpi/icon.png b/Build/Android/res/drawable-hdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cf3c2ca069bf00dce16df6baf358fc91afb14c06 GIT binary patch literal 2226 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 literal 0 HcmV?d00001 diff --git a/Build/Android/res/drawable-mdpi/icon.png b/Build/Android/res/drawable-mdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a975d3698c4d08fc3fa9ab35485e605192f4cefa GIT binary patch literal 1433 zcmV;K1!nq*P)x}Mk*DCXeba#GavaN zGtq3yclcE)e{JYwBXJ`NazP@|zJ?GqWeg-yj zF)$l5G7Ezb#yZMq6TRD5mxQsyHlAd9`@j5F;Q6=>>(ahkSry+D+qAIp^Rufq?p&*y zt$hS?Ff}c}BLW&GGb*>IC6&x+Wq1esaR^6ojD82P7c;3#lx9WY;brgx>vWeu$KZAS zsdZalG|a~LxB?US=)yl(k08u%+M3oL2-YZ-;0C+P+I)9s%;xy5gJ&>e4Pteb5rPujWMAJKETS;wy}4H5}al5PCK@U_K}Zzy=N6B%-Qo>4<2Ofh0BQs$*OE<-##Gb}sV&5;9(B0RMbNdjPcaXMaJL}uX)m^}CywTq>SJ2+gSb*$Y$Q%S@y^jZ2>o{G`4>1rwd_hNP zhYbhlJ;d2^T;%8rkZ=VL*7t4EhRsn`BAeu!jVh(m>^)mAOM!&@R(?gnUtZ`s(FWv*e2}Efzh6q$j#%a2Q#r;p35ePy=?Hj1Zj4#fe;kB3gML z>P8@VlxICIXrO2pAt-_VJ`mJH(IG z&^RH=RN*1P15ScYe9akq6ie%gpq`eo@KV7&610$WE8>jJL)K0*H0tH`<-S}A1f@=I z|BNz(^_Fa1G^x#6cCAfQmsJM+}XJQ-}{5&Z=xEyT9| zr|GyZ5JgSHuGNkV!xgru!)LYrA#lu9Sj3zDl;hS-rKdH5-gFDzZ!`pUaw0of-d73{ z!!M?=#ntg-e{B!cg1yf}sXP6Og&TNvFg%oTE{>zk1F(`#ouS|*7k`crI%*JxO&j5* z6^gDk!OI#y;VGOu(9%tux*I+Q;qcwSZqIwWZ50Xc8E)fcq*dyIbz=Dei6yIh>0NCC zg;(xFO}dj++8#P=3Z=vtlzw%rzJctd00000NkvXXu0mjfCS$J) literal 0 HcmV?d00001 diff --git a/Build/Android/res/drawable-xhdpi/icon.png b/Build/Android/res/drawable-xhdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce300e1236d6b8cb2d5837a0fac4e9bf04bded53 GIT binary patch literal 3602 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 literal 0 HcmV?d00001 diff --git a/Build/Android/res/drawable/downloadimageh.png b/Build/Android/res/drawable/downloadimageh.png new file mode 100644 index 0000000000000000000000000000000000000000..9289100fc9f5951fc6547d5018c4a59645b537d7 GIT binary patch literal 22639 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 literal 0 HcmV?d00001 diff --git a/Build/Android/res/drawable/icon.png b/Build/Android/res/drawable/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a975d3698c4d08fc3fa9ab35485e605192f4cefa GIT binary patch literal 1433 zcmV;K1!nq*P)x}Mk*DCXeba#GavaN zGtq3yclcE)e{JYwBXJ`NazP@|zJ?GqWeg-yj zF)$l5G7Ezb#yZMq6TRD5mxQsyHlAd9`@j5F;Q6=>>(ahkSry+D+qAIp^Rufq?p&*y zt$hS?Ff}c}BLW&GGb*>IC6&x+Wq1esaR^6ojD82P7c;3#lx9WY;brgx>vWeu$KZAS zsdZalG|a~LxB?US=)yl(k08u%+M3oL2-YZ-;0C+P+I)9s%;xy5gJ&>e4Pteb5rPujWMAJKETS;wy}4H5}al5PCK@U_K}Zzy=N6B%-Qo>4<2Ofh0BQs$*OE<-##Gb}sV&5;9(B0RMbNdjPcaXMaJL}uX)m^}CywTq>SJ2+gSb*$Y$Q%S@y^jZ2>o{G`4>1rwd_hNP zhYbhlJ;d2^T;%8rkZ=VL*7t4EhRsn`BAeu!jVh(m>^)mAOM!&@R(?gnUtZ`s(FWv*e2}Efzh6q$j#%a2Q#r;p35ePy=?Hj1Zj4#fe;kB3gML z>P8@VlxICIXrO2pAt-_VJ`mJH(IG z&^RH=RN*1P15ScYe9akq6ie%gpq`eo@KV7&610$WE8>jJL)K0*H0tH`<-S}A1f@=I z|BNz(^_Fa1G^x#6cCAfQmsJM+}XJQ-}{5&Z=xEyT9| zr|GyZ5JgSHuGNkV!xgru!)LYrA#lu9Sj3zDl;hS-rKdH5-gFDzZ!`pUaw0of-d73{ z!!M?=#ntg-e{B!cg1yf}sXP6Og&TNvFg%oTE{>zk1F(`#ouS|*7k`crI%*JxO&j5* z6^gDk!OI#y;VGOu(9%tux*I+Q;qcwSZqIwWZ50Xc8E)fcq*dyIbz=Dei6yIh>0NCC zg;(xFO}dj++8#P=3Z=vtlzw%rzJctd00000NkvXXu0mjfCS$J) literal 0 HcmV?d00001 diff --git a/Build/Android/res/drawable/splashscreen_landscape.png b/Build/Android/res/drawable/splashscreen_landscape.png new file mode 100644 index 0000000000000000000000000000000000000000..4b5d4db425e393e5006eba21725072d551cf9826 GIT binary patch literal 5224 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 P|0`&#>#NnO*uDQB%y9Z| literal 0 HcmV?d00001 diff --git a/Build/Android/res/drawable/splashscreen_portrait.png b/Build/Android/res/drawable/splashscreen_portrait.png new file mode 100644 index 0000000000000000000000000000000000000000..dba675ed2554cfb5bbd3ca2c22a36e7e3ff1f37c GIT binary patch literal 5294 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 L3AjS*dF1~AXh`Y^ literal 0 HcmV?d00001 diff --git a/Build/Android/src/com/epicgames/unreal/DownloadShim.java b/Build/Android/src/com/epicgames/unreal/DownloadShim.java new file mode 100644 index 0000000..354bdac --- /dev/null +++ b/Build/Android/src/com/epicgames/unreal/DownloadShim.java @@ -0,0 +1,33 @@ +package com.epicgames.unreal; + +import com.omixlab.MosisUnreal.OBBDownloaderService; +import com.omixlab.MosisUnreal.DownloaderActivity; +import android.app.Activity; +import com.google.android.vending.expansion.downloader.Helpers; +import com.omixlab.MosisUnreal.OBBData; + + +public class DownloadShim +{ + public static OBBDownloaderService DownloaderService; + public static DownloaderActivity DownloadActivity; + public static Class GetDownloaderType() { return DownloaderActivity.class; } + public static boolean expansionFilesDelivered(Activity activity, int version) { + for (OBBData.XAPKFile xf : OBBData.xAPKS) { + String fileName = Helpers.getExpansionAPKFileName(activity, xf.mIsMain, Integer.toString(version), OBBData.AppType); + GameActivity.Log.debug("Checking for file : " + fileName); + String fileForNewFile = Helpers.generateSaveFileName(activity, fileName); + String fileForDevFile = Helpers.generateSaveFileNameDevelopment(activity, fileName); + GameActivity.Log.debug("which is really being resolved to : " + fileForNewFile + "\n Or : " + fileForDevFile); + if (Helpers.doesFileExist(activity, fileName, xf.mFileSize, false)) { + GameActivity.Log.debug("Found OBB here: " + fileForNewFile); + } + else if (Helpers.doesFileExistDev(activity, fileName, xf.mFileSize, false)) { + GameActivity.Log.debug("Found OBB here: " + fileForDevFile); + } + else return false; + } + return true; + } +} + diff --git a/Build/Android/src/com/omixlab/MosisUnreal/AlarmReceiver.java b/Build/Android/src/com/omixlab/MosisUnreal/AlarmReceiver.java new file mode 100644 index 0000000..7c655aa --- /dev/null +++ b/Build/Android/src/com/omixlab/MosisUnreal/AlarmReceiver.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.omixlab.MosisUnreal; + +import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager.NameNotFoundException; + +/** + * You should start your derived downloader class when this receiver gets the message + * from the alarm service using the provided service helper function within the + * DownloaderClientMarshaller. This class must be then registered in your AndroidManifest.xml + * file with a section like this: + * + */ +public class AlarmReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + try { + DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent, OBBDownloaderService.class); + } catch (NameNotFoundException e) { + e.printStackTrace(); + } + } + +} diff --git a/Build/Android/src/com/omixlab/MosisUnreal/DownloaderActivity.java b/Build/Android/src/com/omixlab/MosisUnreal/DownloaderActivity.java new file mode 100644 index 0000000..bd2fd42 --- /dev/null +++ b/Build/Android/src/com/omixlab/MosisUnreal/DownloaderActivity.java @@ -0,0 +1,809 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.omixlab.MosisUnreal; + +import com.android.vending.expansion.zipfile.ZipResourceFile; +import com.android.vending.expansion.zipfile.ZipResourceFile.ZipEntryRO; +import com.google.android.vending.expansion.downloader.Constants; +import com.google.android.vending.expansion.downloader.DownloadProgressInfo; +import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller; +import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller; +import com.google.android.vending.expansion.downloader.Helpers; +import com.google.android.vending.expansion.downloader.IDownloaderClient; +import com.google.android.vending.expansion.downloader.IDownloaderService; +import com.google.android.vending.expansion.downloader.IStub; + +import android.app.Activity; +import android.app.PendingIntent; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.content.Intent; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Messenger; +import android.os.SystemClock; +import android.provider.Settings; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.util.zip.CRC32; +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; + +import com.epicgames.unreal.GameActivity; + +/** + * This is sample code for a project built against the downloader library. It + * implements the IDownloaderClient that the client marshaler will talk to as + * messages are delivered from the DownloaderService. + */ +public class DownloaderActivity extends Activity implements IDownloaderClient { + private static final String LOG_TAG = "LVLDownloader"; + private ProgressBar mPB; + + private TextView mStatusText; + private TextView mProgressFraction; + private TextView mProgressPercent; + private TextView mAverageSpeed; + private TextView mTimeRemaining; + + private View mDashboard; + private View mCellMessage; + + private Button mPauseButton; + private Button mWiFiSettingsButton; + + private boolean mStatePaused; + private int mState; + + private IDownloaderService mRemoteService; + + private IStub mDownloaderClientStub; + + private final CharSequence[] OBBSelectItems = { "Use Store Data", "Use Development Data" }; + + + private void setState(int newState) { + if (mState != newState) { + mState = newState; + mStatusText.setText(Helpers.getDownloaderStringResourceIDFromState(newState)); + } + } + + private void setButtonPausedState(boolean paused) { + mStatePaused = paused; + int stringResourceID = paused ? R.string.text_button_resume : + R.string.text_button_pause; + mPauseButton.setText(stringResourceID); + } + + static DownloaderActivity _download; + + private Intent OutputData; + + /** + * Go through each of the APK Expansion files defined in the structure above + * and determine if the files are present and match the required size. Free + * applications should definitely consider doing this, as this allows the + * application to be launched for the first time without having a network + * connection present. Paid applications that use LVL should probably do at + * least one LVL check that requires the network to be present, so this is + * not as necessary. + * + * @return true if they are present. + */ + boolean expansionFilesDelivered() { + + for (OBBData.XAPKFile xf : OBBData.xAPKS) { + String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsMain, xf.mFileVersion, OBBData.AppType); + GameActivity.Log.debug("Checking for file : " + fileName); + String fileForNewFile = Helpers.generateSaveFileName(this, fileName); + String fileForDevFile = Helpers.generateSaveFileNameDevelopment(this, fileName); + GameActivity.Log.debug("which is really being resolved to : " + fileForNewFile + "\n Or : " + fileForDevFile); + if (!Helpers.doesFileExist(this, fileName, xf.mFileSize, false) && + !Helpers.doesFileExistDev(this, fileName, xf.mFileSize, false)) + return false; + } + return true; + } + + boolean onlySingleExpansionFileFound() { + for (OBBData.XAPKFile xf : OBBData.xAPKS) { + String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsMain, xf.mFileVersion, OBBData.AppType); + GameActivity.Log.debug("Checking for file : " + fileName); + String fileForNewFile = Helpers.generateSaveFileName(this, fileName); + String fileForDevFile = Helpers.generateSaveFileNameDevelopment(this, fileName); + + if (Helpers.doesFileExist(this, fileName, xf.mFileSize, false) && + Helpers.doesFileExistDev(this, fileName, xf.mFileSize, false)) + return false; + } + + return true; + } + + File getFileDetailsCacheFile() { + return new File(this.getExternalFilesDir(null), "cacheFile.txt"); + } + + boolean expansionFilesUptoData() { + + File cacheFile = getFileDetailsCacheFile(); + // Read data into an array or something... + Map fileDetailsMap = new HashMap(); + + if(cacheFile.exists()) { + try { + FileReader fileCache = new FileReader(cacheFile); + BufferedReader bufferedFileCache = new BufferedReader(fileCache); + List lines = new ArrayList(); + String line = null; + while ((line = bufferedFileCache.readLine()) != null) { + lines.add(line); + } + bufferedFileCache.close(); + + for(String dataLine : lines) + { + GameActivity.Log.debug("Splitting dataLine => " + dataLine); + String[] parts = dataLine.split(","); + fileDetailsMap.put(parts[0], Long.parseLong(parts[1])); + } + } + catch(Exception e) + { + GameActivity.Log.debug("Exception thrown during file details reading."); + e.printStackTrace(); + fileDetailsMap.clear(); + } + } + + for (OBBData.XAPKFile xf : OBBData.xAPKS) { + String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsMain, xf.mFileVersion, OBBData.AppType); + String fileForNewFile = Helpers.generateSaveFileName(this, fileName); + String fileForDevFile = Helpers.generateSaveFileNameDevelopment(this, fileName); + // check to see if time/data on files match cached version + // if not return false + File srcFile = new File(fileForNewFile); + File srcDevFile = new File(fileForDevFile); + long lastModified = srcFile.lastModified(); + long lastModifiedDev = srcDevFile.lastModified(); + if(!(srcFile.exists() && fileDetailsMap.containsKey(fileName) && lastModified == fileDetailsMap.get(fileName)) + && + !(srcDevFile.exists() && fileDetailsMap.containsKey(fileName) && lastModifiedDev == fileDetailsMap.get(fileName))) + return false; + } + return true; + } + + static private void RemoveOBBFile(int OBBToDelete) { + + for (OBBData.XAPKFile xf : OBBData.xAPKS) { + String fileName = Helpers.getExpansionAPKFileName(DownloaderActivity._download, xf.mIsMain, xf.mFileVersion, OBBData.AppType); + switch(OBBToDelete) + { + case 0: + String fileForNewFile = Helpers.generateSaveFileName(DownloaderActivity._download, fileName); + File srcFile = new File(fileForNewFile); + srcFile.delete(); + break; + case 1: + String fileForDevFile = Helpers.generateSaveFileNameDevelopment(DownloaderActivity._download, fileName); + File srcDevFile = new File(fileForDevFile); + srcDevFile.delete(); + break; + } + } + } + + private void ProcessOBBFiles() + { + if(GameActivity.Get().VerifyOBBOnStartUp && !expansionFilesUptoData()) { + validateXAPKZipFiles(); + } else { + OutputData.putExtra(GameActivity.DOWNLOAD_RETURN_NAME, GameActivity.DOWNLOAD_FILES_PRESENT); + setResult(RESULT_OK, OutputData); + finish(); + overridePendingTransition(R.anim.noaction, R.anim.noaction); + } + } + + /** + * Calculating a moving average for the validation speed so we don't get + * jumpy calculations for time etc. + */ + static private final float SMOOTHING_FACTOR = 0.005f; + + /** + * Used by the async task + */ + private boolean mCancelValidation; + + /** + * Go through each of the Expansion APK files and open each as a zip file. + * Calculate the CRC for each file and return false if any fail to match. + * + * @return true if XAPKZipFile is successful + */ + void validateXAPKZipFiles() { + AsyncTask validationTask = new AsyncTask() { + + @Override + protected void onPreExecute() { + mDashboard.setVisibility(View.VISIBLE); + mCellMessage.setVisibility(View.GONE); + mStatusText.setText(R.string.text_verifying_download); + mPauseButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + mCancelValidation = true; + } + }); + mPauseButton.setVisibility(View.GONE); + // mPauseButton.setText(R.string.text_button_cancel_verify); + super.onPreExecute(); + } + + @Override + protected Boolean doInBackground(Object... params) { + for (OBBData.XAPKFile xf : OBBData.xAPKS) { + String fileName = Helpers.getExpansionAPKFileName( + DownloaderActivity.this, + xf.mIsMain, xf.mFileVersion, OBBData.AppType); + boolean normalFile = Helpers.doesFileExist(DownloaderActivity.this, fileName, xf.mFileSize, false); + boolean devFile = Helpers.doesFileExistDev(DownloaderActivity.this, fileName, xf.mFileSize, false); + + if (!normalFile && !devFile ) + return false; + + if(normalFile) + { + fileName = Helpers.generateSaveFileName(DownloaderActivity.this, fileName); + } + else + { + fileName = Helpers.generateSaveFileNameDevelopment(DownloaderActivity.this, fileName); + } + + ZipResourceFile zrf; + byte[] buf = new byte[1024 * 256]; + try { + zrf = new ZipResourceFile(fileName); + ZipEntryRO[] entries = zrf.getAllEntries(); + /** + * First calculate the total compressed length + */ + long totalCompressedLength = 0; + for (ZipEntryRO entry : entries) { + totalCompressedLength += entry.mCompressedLength; + } + float averageVerifySpeed = 0; + long totalBytesRemaining = totalCompressedLength; + long timeRemaining; + /** + * Then calculate a CRC for every file in the Zip file, + * comparing it to what is stored in the Zip directory. + * Note that for compressed Zip files we must extract + * the contents to do this comparison. + */ + for (ZipEntryRO entry : entries) { + if (-1 != entry.mCRC32) { + long length = entry.mUncompressedLength; + CRC32 crc = new CRC32(); + DataInputStream dis = null; + try { + dis = new DataInputStream( + zrf.getInputStream(entry.mFileName)); + + long startTime = SystemClock.uptimeMillis(); + while (length > 0) { + int seek = (int) (length > buf.length ? buf.length + : length); + dis.readFully(buf, 0, seek); + crc.update(buf, 0, seek); + length -= seek; + long currentTime = SystemClock.uptimeMillis(); + long timePassed = currentTime - startTime; + if (timePassed > 0) { + float currentSpeedSample = (float) seek + / (float) timePassed; + if (0 != averageVerifySpeed) { + averageVerifySpeed = SMOOTHING_FACTOR + * currentSpeedSample + + (1 - SMOOTHING_FACTOR) + * averageVerifySpeed; + } else { + averageVerifySpeed = currentSpeedSample; + } + totalBytesRemaining -= seek; + timeRemaining = (long) (totalBytesRemaining / averageVerifySpeed); + this.publishProgress( + new DownloadProgressInfo( + totalCompressedLength, + totalCompressedLength + - totalBytesRemaining, + timeRemaining, + averageVerifySpeed) + ); + } + startTime = currentTime; + if (mCancelValidation) + return true; + } + if (crc.getValue() != entry.mCRC32) { + Log.e(Constants.TAG, + "CRC does not match for entry: " + + entry.mFileName); + Log.e(Constants.TAG, + "In file: " + entry.getZipFileName()); + return false; + } + } finally { + if (null != dis) { + dis.close(); + } + } + } + } + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + return true; + } + + @Override + protected void onProgressUpdate(DownloadProgressInfo... values) { + onDownloadProgress(values[0]); + super.onProgressUpdate(values); + } + + @Override + protected void onPostExecute(Boolean result) { + if (result) { + // save details to cache file... + try { + File cacheFile = getFileDetailsCacheFile(); + FileWriter fileCache = new FileWriter(cacheFile); + BufferedWriter bufferedFileCache = new BufferedWriter(fileCache); + + + for (OBBData.XAPKFile xf : OBBData.xAPKS) { + String fileName = Helpers.getExpansionAPKFileName(DownloaderActivity.this, xf.mIsMain, xf.mFileVersion, OBBData.AppType); + String fileForNewFile = Helpers.generateSaveFileName(DownloaderActivity.this, fileName); + String fileForDevFile = Helpers.generateSaveFileNameDevelopment(DownloaderActivity.this, fileName); + + GameActivity.Log.debug("Writing details for file : " + fileName); + + File srcFile = new File(fileForNewFile); + File srcDevFile = new File(fileForDevFile); + if(srcFile.exists()) { + long lastModified = srcFile.lastModified(); + bufferedFileCache.write(fileName); + bufferedFileCache.write(","); + bufferedFileCache.write(new Long(lastModified).toString()); + bufferedFileCache.newLine(); + GameActivity.Log.debug("Details for file : " + fileName + " with modified time of " + new Long(lastModified).toString() ); + } + else { + long lastModified = srcDevFile.lastModified(); + bufferedFileCache.write(fileName); + bufferedFileCache.write(","); + bufferedFileCache.write(new Long(lastModified).toString()); + bufferedFileCache.newLine(); + GameActivity.Log.debug("Details for file : " + fileName + " with modified time of " + new Long(lastModified).toString() ); + } + } + + bufferedFileCache.close(); + + } + catch(Exception e) + { + GameActivity.Log.debug("Exception thrown during file details writing."); + e.printStackTrace(); + } + /* + mDashboard.setVisibility(View.VISIBLE); + mCellMessage.setVisibility(View.GONE); + mStatusText.setText(R.string.text_validation_complete); + mPauseButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + OutputData.putExtra(GameActivity.DOWNLOAD_RETURN_NAME, GameActivity.DOWNLOAD_FILES_PRESENT); + setResult(RESULT_OK, OutputData); + finish(); + } + }); + mPauseButton.setText(android.R.string.ok); + */ + OutputData.putExtra(GameActivity.DOWNLOAD_RETURN_NAME, GameActivity.DOWNLOAD_FILES_PRESENT); + setResult(RESULT_OK, OutputData); + finish(); + + + } else { + // clear cache file if it exists... + File cacheFile = getFileDetailsCacheFile(); + if(cacheFile.exists()) { + cacheFile.delete(); + } + + mDashboard.setVisibility(View.VISIBLE); + mCellMessage.setVisibility(View.GONE); + mStatusText.setText(R.string.text_validation_failed); + mPauseButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + OutputData.putExtra(GameActivity.DOWNLOAD_RETURN_NAME, GameActivity.DOWNLOAD_INVALID); + setResult(RESULT_OK, OutputData); + finish(); + } + }); + mPauseButton.setText(android.R.string.cancel); + } + super.onPostExecute(result); + } + + }; + validationTask.execute(new Object()); + } + + /** + * If the download isn't present, we initialize the download UI. This ties + * all of the controls into the remote service calls. + */ + private void initializeDownloadUI() { + mDownloaderClientStub = DownloaderClientMarshaller.CreateStub + (this, OBBDownloaderService.class); + setContentView(R.layout.downloader_progress); + + mPB = (ProgressBar) findViewById(R.id.progressBar); + mStatusText = (TextView) findViewById(R.id.statusText); + mProgressFraction = (TextView) findViewById(R.id.progressAsFraction); + mProgressPercent = (TextView) findViewById(R.id.progressAsPercentage); + mAverageSpeed = (TextView) findViewById(R.id.progressAverageSpeed); + mTimeRemaining = (TextView) findViewById(R.id.progressTimeRemaining); + mDashboard = findViewById(R.id.downloaderDashboard); + mCellMessage = findViewById(R.id.approveCellular); + mPauseButton = (Button) findViewById(R.id.pauseButton); + mWiFiSettingsButton = (Button) findViewById(R.id.wifiSettingsButton); + + mPauseButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (mStatePaused) { + mRemoteService.requestContinueDownload(); + } else { + mRemoteService.requestPauseDownload(); + } + setButtonPausedState(!mStatePaused); + } + }); + + mWiFiSettingsButton.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)); + } + }); + + Button resumeOnCell = (Button) findViewById(R.id.resumeOverCellular); + resumeOnCell.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + mRemoteService.setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR); + mRemoteService.requestContinueDownload(); + mCellMessage.setVisibility(View.GONE); + } + }); + + } + + /** + * Called when the activity is first create; we wouldn't create a layout in + * the case where we have the file and are moving to another activity + * without downloading. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + GameActivity.Log.debug("Starting DownloaderActivity..."); + _download = this; + // Create somewhere to place the output - we'll check this on 'finish' to make sure we are returning 'something' + OutputData = new Intent(); + + /** + * Both downloading and validation make use of the "download" UI + */ + initializeDownloadUI(); + GameActivity.Log.debug("... UI setup. Checking for files."); + + /** + * Before we do anything, are the files we expect already here and + * delivered (presumably by Market) For free titles, this is probably + * worth doing. (so no Market request is necessary) + */ + if (!expansionFilesDelivered()) { + GameActivity.Log.debug("... Whoops... missing; go go go download system!"); + + try { + + // Make sure we have a key before we try to start the service + if(OBBDownloaderService.getPublicKeyLength() == 0) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + + builder.setCancelable(false) + .setTitle("No Google Play Store Key") + .setMessage("No OBB found and no store key to try to download. Please set one up in Android Project Settings") + .setPositiveButton("Exit", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + OutputData.putExtra(GameActivity.DOWNLOAD_RETURN_NAME, GameActivity.DOWNLOAD_NO_PLAY_KEY); + setResult(RESULT_OK, OutputData); + finish(); + } + }); + + AlertDialog alert = builder.create(); + alert.show(); + } + else + { + + Intent launchIntent = DownloaderActivity.this + .getIntent(); + Intent intentToLaunchThisActivityFromNotification = new Intent( + DownloaderActivity + .this, DownloaderActivity.this.getClass()); + intentToLaunchThisActivityFromNotification.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_CLEAR_TOP); + intentToLaunchThisActivityFromNotification.setAction(launchIntent.getAction()); + + if (launchIntent.getCategories() != null) { + for (String category : launchIntent.getCategories()) { + intentToLaunchThisActivityFromNotification.addCategory(category); + } + } + + // Build PendingIntent used to open this activity from + // Notification + PendingIntent pendingIntent = PendingIntent.getActivity( + DownloaderActivity.this, + 0, intentToLaunchThisActivityFromNotification, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + // Request to start the download + int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this, + pendingIntent, OBBDownloaderService.class); + + if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { + // The DownloaderService has started downloading the files, + // show progress + initializeDownloadUI(); + return; + } // otherwise, download not needed so we fall through to saying all is OK + else + { + OutputData.putExtra(GameActivity.DOWNLOAD_RETURN_NAME, GameActivity.DOWNLOAD_FILES_PRESENT); + setResult(RESULT_OK, OutputData); + finish(); + } + } + + } catch (NameNotFoundException e) { + Log.e(LOG_TAG, "Cannot find own package! MAYDAY!"); + e.printStackTrace(); + } + + } else { + GameActivity.Log.debug("... Can has! Check 'em Dano!"); + if(!onlySingleExpansionFileFound()) { + // Do some UI here to figure out which we want to keep + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + + builder.setCancelable(false) + .setTitle("Select OBB to use") + .setItems(OBBSelectItems, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + DownloaderActivity.RemoveOBBFile(item); + ProcessOBBFiles(); + } + }); + + AlertDialog alert = builder.create(); + alert.show(); + } + else { + ProcessOBBFiles(); + } + } + } + + /** + * Connect the stub to our service on start. + */ + @Override + protected void onStart() { + if (null != mDownloaderClientStub) { + mDownloaderClientStub.connect(this); + } + super.onStart(); + } + + @Override + protected void onPause() { + super.onPause(); + GameActivity.Log.debug("In onPause"); + + if(OutputData.getIntExtra(GameActivity.DOWNLOAD_RETURN_NAME, GameActivity.DOWNLOAD_NO_RETURN_CODE) == GameActivity.DOWNLOAD_NO_RETURN_CODE) + { + GameActivity.Log.debug("onPause returning that user quit the download."); + OutputData.putExtra(GameActivity.DOWNLOAD_RETURN_NAME, GameActivity.DOWNLOAD_USER_QUIT); + setResult(RESULT_OK, OutputData); + } + } + + /** + * Disconnect the stub from our service on stop + */ + @Override + protected void onStop() { + if (null != mDownloaderClientStub) { + mDownloaderClientStub.disconnect(this); + } + super.onStop(); + setResult(RESULT_OK, OutputData); + } + + /** + * Critical implementation detail. In onServiceConnected we create the + * remote service and marshaler. This is how we pass the client information + * back to the service so the client can be properly notified of changes. We + * must do this every time we reconnect to the service. + */ + @Override + public void onServiceConnected(Messenger m) { + mRemoteService = DownloaderServiceMarshaller.CreateProxy(m); + mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger()); + } + + /** + * The download state should trigger changes in the UI --- it may be useful + * to show the state as being indeterminate at times. This sample can be + * considered a guideline. + */ + @Override + public void onDownloadStateChanged(int newState) { + setState(newState); + boolean showDashboard = true; + boolean showCellMessage = false; + boolean paused; + boolean indeterminate; + switch (newState) { + case IDownloaderClient.STATE_IDLE: + // STATE_IDLE means the service is listening, so it's + // safe to start making calls via mRemoteService. + paused = false; + indeterminate = true; + break; + case IDownloaderClient.STATE_CONNECTING: + case IDownloaderClient.STATE_FETCHING_URL: + showDashboard = true; + paused = false; + indeterminate = true; + break; + case IDownloaderClient.STATE_DOWNLOADING: + paused = false; + showDashboard = true; + indeterminate = false; + break; + + case IDownloaderClient.STATE_FAILED_CANCELED: + case IDownloaderClient.STATE_FAILED: + case IDownloaderClient.STATE_FAILED_FETCHING_URL: + case IDownloaderClient.STATE_FAILED_UNLICENSED: + paused = true; + showDashboard = false; + indeterminate = false; + break; + case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION: + case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION: + showDashboard = false; + paused = true; + indeterminate = false; + showCellMessage = true; + break; + + case IDownloaderClient.STATE_PAUSED_BY_REQUEST: + paused = true; + indeterminate = false; + break; + case IDownloaderClient.STATE_PAUSED_ROAMING: + case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE: + paused = true; + indeterminate = false; + break; + case IDownloaderClient.STATE_COMPLETED: + showDashboard = false; + paused = false; + indeterminate = false; + validateXAPKZipFiles(); + return; + default: + paused = true; + indeterminate = true; + showDashboard = true; + } + int newDashboardVisibility = showDashboard ? View.VISIBLE : View.GONE; + if (mDashboard.getVisibility() != newDashboardVisibility) { + mDashboard.setVisibility(newDashboardVisibility); + } + int cellMessageVisibility = showCellMessage ? View.VISIBLE : View.GONE; + if (mCellMessage.getVisibility() != cellMessageVisibility) { + mCellMessage.setVisibility(cellMessageVisibility); + } + + mPB.setIndeterminate(indeterminate); + setButtonPausedState(paused); + } + + /** + * Sets the state of the various controls based on the progressinfo object + * sent from the downloader service. + */ + @Override + public void onDownloadProgress(DownloadProgressInfo progress) { + mAverageSpeed.setText(getString(R.string.kilobytes_per_second, + Helpers.getSpeedString(progress.mCurrentSpeed))); + mTimeRemaining.setText(getString(R.string.time_remaining, + Helpers.getTimeRemaining(progress.mTimeRemaining))); + + progress.mOverallTotal = progress.mOverallTotal; + mPB.setMax((int) (progress.mOverallTotal >> 8)); + mPB.setProgress((int) (progress.mOverallProgress >> 8)); + mProgressPercent.setText(Long.toString(progress.mOverallProgress + * 100 / + progress.mOverallTotal) + "%"); + mProgressFraction.setText(Helpers.getDownloadProgressString + (progress.mOverallProgress, + progress.mOverallTotal)); + } + + @Override + protected void onDestroy() { + this.mCancelValidation = true; + super.onDestroy(); + } + +} diff --git a/Build/Android/src/com/omixlab/MosisUnreal/OBBData.java b/Build/Android/src/com/omixlab/MosisUnreal/OBBData.java new file mode 100644 index 0000000..a3254ab --- /dev/null +++ b/Build/Android/src/com/omixlab/MosisUnreal/OBBData.java @@ -0,0 +1,26 @@ +package com.omixlab.MosisUnreal; + +public class OBBData +{ +public static final String AppType = ""; + +public static class XAPKFile { +public final boolean mIsMain; +public final String mFileVersion; +public final long mFileSize; +XAPKFile(boolean isMain, String fileVersion, long fileSize) { +mIsMain = isMain; +mFileVersion = fileVersion; +mFileSize = fileSize; +} +} + +public static final XAPKFile[] xAPKS = { +new XAPKFile( +true, // true signifies a main file +"1", // the version of the APK that the file was uploaded against +0L // the length of the file in bytes +) +}; +}; + diff --git a/Build/Android/src/com/omixlab/MosisUnreal/OBBDownloaderService.java b/Build/Android/src/com/omixlab/MosisUnreal/OBBDownloaderService.java new file mode 100644 index 0000000..1b933e3 --- /dev/null +++ b/Build/Android/src/com/omixlab/MosisUnreal/OBBDownloaderService.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.omixlab.MosisUnreal; + +import com.google.android.vending.expansion.downloader.impl.DownloaderService; + +/** + * Minimal client implementation of the + * DownloaderService from the Downloader library. + */ +public class OBBDownloaderService extends DownloaderService { + // stuff for LVL -- MODIFY FOR YOUR APPLICATION! + private static final String BASE64_PUBLIC_KEY = ""; + // used by the preference obfuscater + private static final byte[] SALT = new byte[] { + 1, 43, -12, -1, 54, 98, + -100, -12, 43, 2, -8, -4, 9, 5, -106, -108, -33, 45, -1, 84 + }; + + public static int getPublicKeyLength() { + return BASE64_PUBLIC_KEY.length(); + } + + /** + * This public key comes from your Android Market publisher account, and it + * used by the LVL to validate responses from Market on your behalf. + */ + @Override + public String getPublicKey() { + return BASE64_PUBLIC_KEY; + } + + /** + * This is used by the preference obfuscater to make sure that your + * obfuscated preferences are different than the ones used by other + * applications. + */ + @Override + public byte[] getSALT() { + return SALT; + } + + /** + * Fill this in with the class name for your alarm receiver. We do this + * because receivers must be unique across all of Android (it's a good idea + * to make sure that your receiver is in your unique package) + */ + @Override + public String getAlarmReceiverClassName() { + return com.omixlab.MosisUnreal.AlarmReceiver.class.getName(); + } +} diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index e0b2e7f..4939d95 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -40,6 +40,9 @@ r.AntiAliasingMethod=3 bBuildForES31=False ExtraApplicationSettings= bPackageForMetaQuest=True +PackageName=com.omixlab.[PROJECT] +MinSDKVersion=34 +TargetSDKVersion=36 [/Script/WindowsTargetPlatform.WindowsTargetSettings] DefaultGraphicsRHI=DefaultGraphicsRHI_Default diff --git a/Content/VRTemplate/Maps/VRTemplateMap.umap b/Content/VRTemplate/Maps/VRTemplateMap.umap index 081e169..83576b8 100644 --- a/Content/VRTemplate/Maps/VRTemplateMap.umap +++ b/Content/VRTemplate/Maps/VRTemplateMap.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3cb486ece055b324dda622bca8f4d656c6e01eafc82aa929d4d75164275588e6 -size 135023 +oid sha256:dff134f0c0a318bde61cc980c38c03a0fa1c55bb82ec6e8bbbc9f7d6ff65b7c3 +size 169383 diff --git a/Content/VRTemplate/Maps/VRTemplateMap_BuiltData.uasset b/Content/VRTemplate/Maps/VRTemplateMap_BuiltData.uasset index 0e36393..e765a9b 100644 --- a/Content/VRTemplate/Maps/VRTemplateMap_BuiltData.uasset +++ b/Content/VRTemplate/Maps/VRTemplateMap_BuiltData.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b3057baee6b86cfa366d53bcf6ee2bdbae20514bdb0812ee3fcd11f0916df28 -size 13583796 +oid sha256:d90761cd0c2c065c9d5a40a161a02027d9ed1aec2f53b1b555dfaf68ec80ad37 +size 5122165 diff --git a/MosisUnreal.uproject b/MosisUnreal.uproject index 921d941..147a3c4 100644 --- a/MosisUnreal.uproject +++ b/MosisUnreal.uproject @@ -1,6 +1,6 @@ { "FileVersion": 3, - "EngineAssociation": "5.4", + "EngineAssociation": "5.5", "Category": "", "Description": "", "Modules": [ diff --git a/Plugins/MosisSDK/MosisSDK.uplugin b/Plugins/MosisSDK/MosisSDK.uplugin new file mode 100644 index 0000000..fc7a6d1 --- /dev/null +++ b/Plugins/MosisSDK/MosisSDK.uplugin @@ -0,0 +1,24 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "MosisSDK", + "Description": "", + "Category": "Other", + "CreatedBy": "OmixLab LTD", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": true, + "IsBetaVersion": false, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "MosisSDK", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ] +} \ No newline at end of file diff --git a/Plugins/MosisSDK/Resources/Icon128.png b/Plugins/MosisSDK/Resources/Icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..1231d4aad4d0d462fb7b178eb5b30aa61a10df0b GIT binary patch literal 12699 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 a;7#~yJh_ha>A&A^fRdb=Y>l)<=>Gxy=2LS3 literal 0 HcmV?d00001 diff --git a/Plugins/MosisSDK/Source/MosisSDK/AIDL/android/hardware/HardwareBuffer.aidl b/Plugins/MosisSDK/Source/MosisSDK/AIDL/android/hardware/HardwareBuffer.aidl new file mode 100644 index 0000000..ae119e3 --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/AIDL/android/hardware/HardwareBuffer.aidl @@ -0,0 +1,3 @@ +package android.hardware; + +@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable HardwareBuffer ndk_header "android/hardware_buffer_aidl.h"; diff --git a/Plugins/MosisSDK/Source/MosisSDK/AIDL/com/omixlab/mosis/IMosisListener.aidl b/Plugins/MosisSDK/Source/MosisSDK/AIDL/com/omixlab/mosis/IMosisListener.aidl new file mode 100644 index 0000000..4387be4 --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/AIDL/com/omixlab/mosis/IMosisListener.aidl @@ -0,0 +1,10 @@ +package com.omixlab.mosis; + +import android.hardware.HardwareBuffer; + +// oneway = fire-and-forget (asynchronous) +oneway interface IMosisListener { + void onServiceInitialized(boolean success); + void onBufferAvailable(in HardwareBuffer buffer); + void onFrameAvailable(); +} diff --git a/Plugins/MosisSDK/Source/MosisSDK/AIDL/com/omixlab/mosis/IMosisService.aidl b/Plugins/MosisSDK/Source/MosisSDK/AIDL/com/omixlab/mosis/IMosisService.aidl new file mode 100644 index 0000000..8efa74e --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/AIDL/com/omixlab/mosis/IMosisService.aidl @@ -0,0 +1,10 @@ +package com.omixlab.mosis; + +import com.omixlab.mosis.IMosisListener; + +interface IMosisService { + boolean initOS(IMosisListener listener); + void onTouchDown(float x, float y); + void onTouchMove(float x, float y); + void onTouchUp(float x, float y); +} \ No newline at end of file diff --git a/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BnMosisListener.h b/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BnMosisListener.h new file mode 100644 index 0000000..9f6a347 --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BnMosisListener.h @@ -0,0 +1,56 @@ +/* + * This file is auto-generated. DO NOT MODIFY. + * Using: C:\\Users\\omara\\AppData\\Local\\Android\\Sdk\\build-tools/36.1.0/aidl.exe --lang=ndk --min_sdk_version=36 -I D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL -o D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated -h D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL\\com/omixlab/mosis\\IMosisListener.aidl + * + * DO NOT CHECK THIS FILE INTO A CODE TREE (e.g. git, etc..). + * ALWAYS GENERATE THIS FILE FROM UPDATED AIDL COMPILER + * AS A BUILD INTERMEDIATE ONLY. THIS IS NOT SOURCE CODE. + */ +#pragma once + +#include "aidl/com/omixlab/mosis/IMosisListener.h" + +#include +#include + +#ifndef __BIONIC__ +#ifndef __assert2 +#define __assert2(a,b,c,d) ((void)0) +#endif +#endif + +namespace aidl { +namespace com { +namespace omixlab { +namespace mosis { +class BnMosisListener : public ::ndk::BnCInterface { +public: + BnMosisListener(); + virtual ~BnMosisListener(); +protected: + ::ndk::SpAIBinder createBinder() override; +private: +}; +class IMosisListenerDelegator : public BnMosisListener { +public: + explicit IMosisListenerDelegator(const std::shared_ptr &impl) : _impl(impl) { + } + + ::ndk::ScopedAStatus onServiceInitialized(bool in_success) override { + return _impl->onServiceInitialized(in_success); + } + ::ndk::ScopedAStatus onBufferAvailable(const ::aidl::android::hardware::HardwareBuffer& in_buffer) override { + return _impl->onBufferAvailable(in_buffer); + } + ::ndk::ScopedAStatus onFrameAvailable() override { + return _impl->onFrameAvailable(); + } +protected: +private: + std::shared_ptr _impl; +}; + +} // namespace mosis +} // namespace omixlab +} // namespace com +} // namespace aidl diff --git a/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BnMosisService.h b/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BnMosisService.h new file mode 100644 index 0000000..d58581e --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BnMosisService.h @@ -0,0 +1,59 @@ +/* + * This file is auto-generated. DO NOT MODIFY. + * Using: C:\\Users\\omara\\AppData\\Local\\Android\\Sdk\\build-tools/36.1.0/aidl.exe --lang=ndk --min_sdk_version=36 -I D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL -o D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated -h D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL\\com/omixlab/mosis\\IMosisService.aidl + * + * DO NOT CHECK THIS FILE INTO A CODE TREE (e.g. git, etc..). + * ALWAYS GENERATE THIS FILE FROM UPDATED AIDL COMPILER + * AS A BUILD INTERMEDIATE ONLY. THIS IS NOT SOURCE CODE. + */ +#pragma once + +#include "aidl/com/omixlab/mosis/IMosisService.h" + +#include +#include + +#ifndef __BIONIC__ +#ifndef __assert2 +#define __assert2(a,b,c,d) ((void)0) +#endif +#endif + +namespace aidl { +namespace com { +namespace omixlab { +namespace mosis { +class BnMosisService : public ::ndk::BnCInterface { +public: + BnMosisService(); + virtual ~BnMosisService(); +protected: + ::ndk::SpAIBinder createBinder() override; +private: +}; +class IMosisServiceDelegator : public BnMosisService { +public: + explicit IMosisServiceDelegator(const std::shared_ptr &impl) : _impl(impl) { + } + + ::ndk::ScopedAStatus initOS(const std::shared_ptr<::aidl::com::omixlab::mosis::IMosisListener>& in_listener, bool* _aidl_return) override { + return _impl->initOS(in_listener, _aidl_return); + } + ::ndk::ScopedAStatus onTouchDown(float in_x, float in_y) override { + return _impl->onTouchDown(in_x, in_y); + } + ::ndk::ScopedAStatus onTouchMove(float in_x, float in_y) override { + return _impl->onTouchMove(in_x, in_y); + } + ::ndk::ScopedAStatus onTouchUp(float in_x, float in_y) override { + return _impl->onTouchUp(in_x, in_y); + } +protected: +private: + std::shared_ptr _impl; +}; + +} // namespace mosis +} // namespace omixlab +} // namespace com +} // namespace aidl diff --git a/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BpMosisListener.h b/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BpMosisListener.h new file mode 100644 index 0000000..07b1746 --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BpMosisListener.h @@ -0,0 +1,31 @@ +/* + * This file is auto-generated. DO NOT MODIFY. + * Using: C:\\Users\\omara\\AppData\\Local\\Android\\Sdk\\build-tools/36.1.0/aidl.exe --lang=ndk --min_sdk_version=36 -I D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL -o D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated -h D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL\\com/omixlab/mosis\\IMosisListener.aidl + * + * DO NOT CHECK THIS FILE INTO A CODE TREE (e.g. git, etc..). + * ALWAYS GENERATE THIS FILE FROM UPDATED AIDL COMPILER + * AS A BUILD INTERMEDIATE ONLY. THIS IS NOT SOURCE CODE. + */ +#pragma once + +#include "aidl/com/omixlab/mosis/IMosisListener.h" + +#include + +namespace aidl { +namespace com { +namespace omixlab { +namespace mosis { +class BpMosisListener : public ::ndk::BpCInterface { +public: + explicit BpMosisListener(const ::ndk::SpAIBinder& binder); + virtual ~BpMosisListener(); + + ::ndk::ScopedAStatus onServiceInitialized(bool in_success) override; + ::ndk::ScopedAStatus onBufferAvailable(const ::aidl::android::hardware::HardwareBuffer& in_buffer) override; + ::ndk::ScopedAStatus onFrameAvailable() override; +}; +} // namespace mosis +} // namespace omixlab +} // namespace com +} // namespace aidl diff --git a/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BpMosisService.h b/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BpMosisService.h new file mode 100644 index 0000000..eff64d8 --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/BpMosisService.h @@ -0,0 +1,32 @@ +/* + * This file is auto-generated. DO NOT MODIFY. + * Using: C:\\Users\\omara\\AppData\\Local\\Android\\Sdk\\build-tools/36.1.0/aidl.exe --lang=ndk --min_sdk_version=36 -I D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL -o D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated -h D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL\\com/omixlab/mosis\\IMosisService.aidl + * + * DO NOT CHECK THIS FILE INTO A CODE TREE (e.g. git, etc..). + * ALWAYS GENERATE THIS FILE FROM UPDATED AIDL COMPILER + * AS A BUILD INTERMEDIATE ONLY. THIS IS NOT SOURCE CODE. + */ +#pragma once + +#include "aidl/com/omixlab/mosis/IMosisService.h" + +#include + +namespace aidl { +namespace com { +namespace omixlab { +namespace mosis { +class BpMosisService : public ::ndk::BpCInterface { +public: + explicit BpMosisService(const ::ndk::SpAIBinder& binder); + virtual ~BpMosisService(); + + ::ndk::ScopedAStatus initOS(const std::shared_ptr<::aidl::com::omixlab::mosis::IMosisListener>& in_listener, bool* _aidl_return) override; + ::ndk::ScopedAStatus onTouchDown(float in_x, float in_y) override; + ::ndk::ScopedAStatus onTouchMove(float in_x, float in_y) override; + ::ndk::ScopedAStatus onTouchUp(float in_x, float in_y) override; +}; +} // namespace mosis +} // namespace omixlab +} // namespace com +} // namespace aidl diff --git a/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/IMosisListener.h b/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/IMosisListener.h new file mode 100644 index 0000000..3aeb2b6 --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/IMosisListener.h @@ -0,0 +1,61 @@ +/* + * This file is auto-generated. DO NOT MODIFY. + * Using: C:\\Users\\omara\\AppData\\Local\\Android\\Sdk\\build-tools/36.1.0/aidl.exe --lang=ndk --min_sdk_version=36 -I D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL -o D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated -h D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL\\com/omixlab/mosis\\IMosisListener.aidl + * + * DO NOT CHECK THIS FILE INTO A CODE TREE (e.g. git, etc..). + * ALWAYS GENERATE THIS FILE FROM UPDATED AIDL COMPILER + * AS A BUILD INTERMEDIATE ONLY. THIS IS NOT SOURCE CODE. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#ifdef BINDER_STABILITY_SUPPORT +#include +#endif // BINDER_STABILITY_SUPPORT + +namespace aidl { +namespace com { +namespace omixlab { +namespace mosis { +class IMosisListenerDelegator; + +class IMosisListener : public ::ndk::ICInterface { +public: + typedef IMosisListenerDelegator DefaultDelegator; + static const char* descriptor; + IMosisListener(); + virtual ~IMosisListener(); + + static constexpr uint32_t TRANSACTION_onServiceInitialized = FIRST_CALL_TRANSACTION + 0; + static constexpr uint32_t TRANSACTION_onBufferAvailable = FIRST_CALL_TRANSACTION + 1; + static constexpr uint32_t TRANSACTION_onFrameAvailable = FIRST_CALL_TRANSACTION + 2; + + static std::shared_ptr fromBinder(const ::ndk::SpAIBinder& binder); + static binder_status_t writeToParcel(AParcel* parcel, const std::shared_ptr& instance); + static binder_status_t readFromParcel(const AParcel* parcel, std::shared_ptr* instance); + static bool setDefaultImpl(const std::shared_ptr& impl); + static const std::shared_ptr& getDefaultImpl(); + virtual ::ndk::ScopedAStatus onServiceInitialized(bool in_success) = 0; + virtual ::ndk::ScopedAStatus onBufferAvailable(const ::aidl::android::hardware::HardwareBuffer& in_buffer) = 0; + virtual ::ndk::ScopedAStatus onFrameAvailable() = 0; +private: + static std::shared_ptr default_impl; +}; +class IMosisListenerDefault : public IMosisListener { +public: + ::ndk::ScopedAStatus onServiceInitialized(bool in_success) override; + ::ndk::ScopedAStatus onBufferAvailable(const ::aidl::android::hardware::HardwareBuffer& in_buffer) override; + ::ndk::ScopedAStatus onFrameAvailable() override; + ::ndk::SpAIBinder asBinder() override; + bool isRemote() override; +}; +} // namespace mosis +} // namespace omixlab +} // namespace com +} // namespace aidl diff --git a/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/IMosisService.h b/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/IMosisService.h new file mode 100644 index 0000000..ab3ae59 --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/Generated/aidl/com/omixlab/mosis/IMosisService.h @@ -0,0 +1,67 @@ +/* + * This file is auto-generated. DO NOT MODIFY. + * Using: C:\\Users\\omara\\AppData\\Local\\Android\\Sdk\\build-tools/36.1.0/aidl.exe --lang=ndk --min_sdk_version=36 -I D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL -o D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated -h D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL\\com/omixlab/mosis\\IMosisService.aidl + * + * DO NOT CHECK THIS FILE INTO A CODE TREE (e.g. git, etc..). + * ALWAYS GENERATE THIS FILE FROM UPDATED AIDL COMPILER + * AS A BUILD INTERMEDIATE ONLY. THIS IS NOT SOURCE CODE. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#ifdef BINDER_STABILITY_SUPPORT +#include +#endif // BINDER_STABILITY_SUPPORT + +namespace aidl::com::omixlab::mosis { +class IMosisListener; +} // namespace aidl::com::omixlab::mosis +namespace aidl { +namespace com { +namespace omixlab { +namespace mosis { +class IMosisServiceDelegator; + +class IMosisService : public ::ndk::ICInterface { +public: + typedef IMosisServiceDelegator DefaultDelegator; + static const char* descriptor; + IMosisService(); + virtual ~IMosisService(); + + static constexpr uint32_t TRANSACTION_initOS = FIRST_CALL_TRANSACTION + 0; + static constexpr uint32_t TRANSACTION_onTouchDown = FIRST_CALL_TRANSACTION + 1; + static constexpr uint32_t TRANSACTION_onTouchMove = FIRST_CALL_TRANSACTION + 2; + static constexpr uint32_t TRANSACTION_onTouchUp = FIRST_CALL_TRANSACTION + 3; + + static std::shared_ptr fromBinder(const ::ndk::SpAIBinder& binder); + static binder_status_t writeToParcel(AParcel* parcel, const std::shared_ptr& instance); + static binder_status_t readFromParcel(const AParcel* parcel, std::shared_ptr* instance); + static bool setDefaultImpl(const std::shared_ptr& impl); + static const std::shared_ptr& getDefaultImpl(); + virtual ::ndk::ScopedAStatus initOS(const std::shared_ptr<::aidl::com::omixlab::mosis::IMosisListener>& in_listener, bool* _aidl_return) = 0; + virtual ::ndk::ScopedAStatus onTouchDown(float in_x, float in_y) = 0; + virtual ::ndk::ScopedAStatus onTouchMove(float in_x, float in_y) = 0; + virtual ::ndk::ScopedAStatus onTouchUp(float in_x, float in_y) = 0; +private: + static std::shared_ptr default_impl; +}; +class IMosisServiceDefault : public IMosisService { +public: + ::ndk::ScopedAStatus initOS(const std::shared_ptr<::aidl::com::omixlab::mosis::IMosisListener>& in_listener, bool* _aidl_return) override; + ::ndk::ScopedAStatus onTouchDown(float in_x, float in_y) override; + ::ndk::ScopedAStatus onTouchMove(float in_x, float in_y) override; + ::ndk::ScopedAStatus onTouchUp(float in_x, float in_y) override; + ::ndk::SpAIBinder asBinder() override; + bool isRemote() override; +}; +} // namespace mosis +} // namespace omixlab +} // namespace com +} // namespace aidl diff --git a/Plugins/MosisSDK/Source/MosisSDK/Generated/com/omixlab/mosis/IMosisListener.cpp b/Plugins/MosisSDK/Source/MosisSDK/Generated/com/omixlab/mosis/IMosisListener.cpp new file mode 100644 index 0000000..e574894 --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/Generated/com/omixlab/mosis/IMosisListener.cpp @@ -0,0 +1,242 @@ +/* + * This file is auto-generated. DO NOT MODIFY. + * Using: C:\\Users\\omara\\AppData\\Local\\Android\\Sdk\\build-tools/36.1.0/aidl.exe --lang=ndk --min_sdk_version=36 -I D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL -o D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated -h D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL\\com/omixlab/mosis\\IMosisListener.aidl + * + * DO NOT CHECK THIS FILE INTO A CODE TREE (e.g. git, etc..). + * ALWAYS GENERATE THIS FILE FROM UPDATED AIDL COMPILER + * AS A BUILD INTERMEDIATE ONLY. THIS IS NOT SOURCE CODE. + */ +#include "aidl/com/omixlab/mosis/IMosisListener.h" + +#include +#include +#include +#include +#include +#include + +namespace aidl { +namespace com { +namespace omixlab { +namespace mosis { +static binder_status_t _aidl_com_omixlab_mosis_IMosisListener_onTransact(AIBinder* _aidl_binder, transaction_code_t _aidl_code, const AParcel* _aidl_in, AParcel* _aidl_out) { + (void)_aidl_in; + (void)_aidl_out; + binder_status_t _aidl_ret_status = STATUS_UNKNOWN_TRANSACTION; + std::shared_ptr _aidl_impl = std::static_pointer_cast(::ndk::ICInterface::asInterface(_aidl_binder)); + switch (_aidl_code) { + case (FIRST_CALL_TRANSACTION + 0 /*onServiceInitialized*/): { + bool in_success; + + _aidl_ret_status = ::ndk::AParcel_readData(_aidl_in, &in_success); + if (_aidl_ret_status != STATUS_OK) break; + + ::ndk::ScopedAStatus _aidl_status = _aidl_impl->onServiceInitialized(in_success); + _aidl_ret_status = STATUS_OK; + break; + } + case (FIRST_CALL_TRANSACTION + 1 /*onBufferAvailable*/): { + ::aidl::android::hardware::HardwareBuffer in_buffer; + + _aidl_ret_status = ::ndk::AParcel_readData(_aidl_in, &in_buffer); + if (_aidl_ret_status != STATUS_OK) break; + + ::ndk::ScopedAStatus _aidl_status = _aidl_impl->onBufferAvailable(in_buffer); + _aidl_ret_status = STATUS_OK; + break; + } + case (FIRST_CALL_TRANSACTION + 2 /*onFrameAvailable*/): { + + ::ndk::ScopedAStatus _aidl_status = _aidl_impl->onFrameAvailable(); + _aidl_ret_status = STATUS_OK; + break; + } + } + return _aidl_ret_status; +} + +static AIBinder_Class* _g_aidl_com_omixlab_mosis_IMosisListener_clazz = ::ndk::ICInterface::defineClass(IMosisListener::descriptor, _aidl_com_omixlab_mosis_IMosisListener_onTransact, nullptr, 0); + +BpMosisListener::BpMosisListener(const ::ndk::SpAIBinder& binder) : BpCInterface(binder) {} +BpMosisListener::~BpMosisListener() {} + +::ndk::ScopedAStatus BpMosisListener::onServiceInitialized(bool in_success) { + binder_status_t _aidl_ret_status = STATUS_OK; + ::ndk::ScopedAStatus _aidl_status; + ::ndk::ScopedAParcel _aidl_in; + ::ndk::ScopedAParcel _aidl_out; + + _aidl_ret_status = AIBinder_prepareTransaction(asBinderReference().get(), _aidl_in.getR()); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = ::ndk::AParcel_writeData(_aidl_in.get(), in_success); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = AIBinder_transact( + asBinderReference().get(), + (FIRST_CALL_TRANSACTION + 0 /*onServiceInitialized*/), + _aidl_in.getR(), + _aidl_out.getR(), + FLAG_ONEWAY + #ifdef BINDER_STABILITY_SUPPORT + | static_cast(FLAG_PRIVATE_LOCAL) + #endif // BINDER_STABILITY_SUPPORT + ); + if (_aidl_ret_status == STATUS_UNKNOWN_TRANSACTION && IMosisListener::getDefaultImpl()) { + _aidl_status = IMosisListener::getDefaultImpl()->onServiceInitialized(in_success); + goto _aidl_status_return; + } + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_error: + _aidl_status.set(AStatus_fromStatus(_aidl_ret_status)); + _aidl_status_return: + return _aidl_status; +} +::ndk::ScopedAStatus BpMosisListener::onBufferAvailable(const ::aidl::android::hardware::HardwareBuffer& in_buffer) { + binder_status_t _aidl_ret_status = STATUS_OK; + ::ndk::ScopedAStatus _aidl_status; + ::ndk::ScopedAParcel _aidl_in; + ::ndk::ScopedAParcel _aidl_out; + + _aidl_ret_status = AIBinder_prepareTransaction(asBinderReference().get(), _aidl_in.getR()); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = ::ndk::AParcel_writeData(_aidl_in.get(), in_buffer); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = AIBinder_transact( + asBinderReference().get(), + (FIRST_CALL_TRANSACTION + 1 /*onBufferAvailable*/), + _aidl_in.getR(), + _aidl_out.getR(), + FLAG_ONEWAY + #ifdef BINDER_STABILITY_SUPPORT + | static_cast(FLAG_PRIVATE_LOCAL) + #endif // BINDER_STABILITY_SUPPORT + ); + if (_aidl_ret_status == STATUS_UNKNOWN_TRANSACTION && IMosisListener::getDefaultImpl()) { + _aidl_status = IMosisListener::getDefaultImpl()->onBufferAvailable(in_buffer); + goto _aidl_status_return; + } + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_error: + _aidl_status.set(AStatus_fromStatus(_aidl_ret_status)); + _aidl_status_return: + return _aidl_status; +} +::ndk::ScopedAStatus BpMosisListener::onFrameAvailable() { + binder_status_t _aidl_ret_status = STATUS_OK; + ::ndk::ScopedAStatus _aidl_status; + ::ndk::ScopedAParcel _aidl_in; + ::ndk::ScopedAParcel _aidl_out; + + _aidl_ret_status = AIBinder_prepareTransaction(asBinderReference().get(), _aidl_in.getR()); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = AIBinder_transact( + asBinderReference().get(), + (FIRST_CALL_TRANSACTION + 2 /*onFrameAvailable*/), + _aidl_in.getR(), + _aidl_out.getR(), + FLAG_ONEWAY + #ifdef BINDER_STABILITY_SUPPORT + | static_cast(FLAG_PRIVATE_LOCAL) + #endif // BINDER_STABILITY_SUPPORT + ); + if (_aidl_ret_status == STATUS_UNKNOWN_TRANSACTION && IMosisListener::getDefaultImpl()) { + _aidl_status = IMosisListener::getDefaultImpl()->onFrameAvailable(); + goto _aidl_status_return; + } + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_error: + _aidl_status.set(AStatus_fromStatus(_aidl_ret_status)); + _aidl_status_return: + return _aidl_status; +} +// Source for BnMosisListener +BnMosisListener::BnMosisListener() {} +BnMosisListener::~BnMosisListener() {} +::ndk::SpAIBinder BnMosisListener::createBinder() { + AIBinder* binder = AIBinder_new(_g_aidl_com_omixlab_mosis_IMosisListener_clazz, static_cast(this)); + #ifdef BINDER_STABILITY_SUPPORT + AIBinder_markCompilationUnitStability(binder); + #endif // BINDER_STABILITY_SUPPORT + return ::ndk::SpAIBinder(binder); +} +// Source for IMosisListener +const char* IMosisListener::descriptor = "com.omixlab.mosis.IMosisListener"; +IMosisListener::IMosisListener() {} +IMosisListener::~IMosisListener() {} + + +std::shared_ptr IMosisListener::fromBinder(const ::ndk::SpAIBinder& binder) { + if (!AIBinder_associateClass(binder.get(), _g_aidl_com_omixlab_mosis_IMosisListener_clazz)) { + #if __ANDROID_API__ >= 31 + const AIBinder_Class* originalClass = AIBinder_getClass(binder.get()); + if (originalClass == nullptr) return nullptr; + if (0 == strcmp(AIBinder_Class_getDescriptor(originalClass), descriptor)) { + return ::ndk::SharedRefBase::make(binder); + } + #endif + return nullptr; + } + std::shared_ptr<::ndk::ICInterface> interface = ::ndk::ICInterface::asInterface(binder.get()); + if (interface) { + return std::static_pointer_cast(interface); + } + return ::ndk::SharedRefBase::make(binder); +} + +binder_status_t IMosisListener::writeToParcel(AParcel* parcel, const std::shared_ptr& instance) { + return AParcel_writeStrongBinder(parcel, instance ? instance->asBinder().get() : nullptr); +} +binder_status_t IMosisListener::readFromParcel(const AParcel* parcel, std::shared_ptr* instance) { + ::ndk::SpAIBinder binder; + binder_status_t status = AParcel_readStrongBinder(parcel, binder.getR()); + if (status != STATUS_OK) return status; + *instance = IMosisListener::fromBinder(binder); + return STATUS_OK; +} +bool IMosisListener::setDefaultImpl(const std::shared_ptr& impl) { + // Only one user of this interface can use this function + // at a time. This is a heuristic to detect if two different + // users in the same process use this function. + assert(!IMosisListener::default_impl); + if (impl) { + IMosisListener::default_impl = impl; + return true; + } + return false; +} +const std::shared_ptr& IMosisListener::getDefaultImpl() { + return IMosisListener::default_impl; +} +std::shared_ptr IMosisListener::default_impl = nullptr; +::ndk::ScopedAStatus IMosisListenerDefault::onServiceInitialized(bool /*in_success*/) { + ::ndk::ScopedAStatus _aidl_status; + _aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION)); + return _aidl_status; +} +::ndk::ScopedAStatus IMosisListenerDefault::onBufferAvailable(const ::aidl::android::hardware::HardwareBuffer& /*in_buffer*/) { + ::ndk::ScopedAStatus _aidl_status; + _aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION)); + return _aidl_status; +} +::ndk::ScopedAStatus IMosisListenerDefault::onFrameAvailable() { + ::ndk::ScopedAStatus _aidl_status; + _aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION)); + return _aidl_status; +} +::ndk::SpAIBinder IMosisListenerDefault::asBinder() { + return ::ndk::SpAIBinder(); +} +bool IMosisListenerDefault::isRemote() { + return false; +} +} // namespace mosis +} // namespace omixlab +} // namespace com +} // namespace aidl diff --git a/Plugins/MosisSDK/Source/MosisSDK/Generated/com/omixlab/mosis/IMosisService.cpp b/Plugins/MosisSDK/Source/MosisSDK/Generated/com/omixlab/mosis/IMosisService.cpp new file mode 100644 index 0000000..4266f54 --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/Generated/com/omixlab/mosis/IMosisService.cpp @@ -0,0 +1,360 @@ +/* + * This file is auto-generated. DO NOT MODIFY. + * Using: C:\\Users\\omara\\AppData\\Local\\Android\\Sdk\\build-tools/36.1.0/aidl.exe --lang=ndk --min_sdk_version=36 -I D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL -o D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated -h D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\Generated D:\\Dev\\Mosis\\MosisUnreal\\Plugins\\MosisSDK\\Source\\MosisSDK\\AIDL\\com/omixlab/mosis\\IMosisService.aidl + * + * DO NOT CHECK THIS FILE INTO A CODE TREE (e.g. git, etc..). + * ALWAYS GENERATE THIS FILE FROM UPDATED AIDL COMPILER + * AS A BUILD INTERMEDIATE ONLY. THIS IS NOT SOURCE CODE. + */ +#include "aidl/com/omixlab/mosis/IMosisService.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace aidl { +namespace com { +namespace omixlab { +namespace mosis { +static binder_status_t _aidl_com_omixlab_mosis_IMosisService_onTransact(AIBinder* _aidl_binder, transaction_code_t _aidl_code, const AParcel* _aidl_in, AParcel* _aidl_out) { + (void)_aidl_in; + (void)_aidl_out; + binder_status_t _aidl_ret_status = STATUS_UNKNOWN_TRANSACTION; + std::shared_ptr _aidl_impl = std::static_pointer_cast(::ndk::ICInterface::asInterface(_aidl_binder)); + switch (_aidl_code) { + case (FIRST_CALL_TRANSACTION + 0 /*initOS*/): { + std::shared_ptr<::aidl::com::omixlab::mosis::IMosisListener> in_listener; + bool _aidl_return; + + _aidl_ret_status = ::ndk::AParcel_readData(_aidl_in, &in_listener); + if (_aidl_ret_status != STATUS_OK) break; + + ::ndk::ScopedAStatus _aidl_status = _aidl_impl->initOS(in_listener, &_aidl_return); + _aidl_ret_status = AParcel_writeStatusHeader(_aidl_out, _aidl_status.get()); + if (_aidl_ret_status != STATUS_OK) break; + + if (!AStatus_isOk(_aidl_status.get())) break; + + _aidl_ret_status = ::ndk::AParcel_writeData(_aidl_out, _aidl_return); + if (_aidl_ret_status != STATUS_OK) break; + + break; + } + case (FIRST_CALL_TRANSACTION + 1 /*onTouchDown*/): { + float in_x; + float in_y; + + _aidl_ret_status = ::ndk::AParcel_readData(_aidl_in, &in_x); + if (_aidl_ret_status != STATUS_OK) break; + + _aidl_ret_status = ::ndk::AParcel_readData(_aidl_in, &in_y); + if (_aidl_ret_status != STATUS_OK) break; + + ::ndk::ScopedAStatus _aidl_status = _aidl_impl->onTouchDown(in_x, in_y); + _aidl_ret_status = AParcel_writeStatusHeader(_aidl_out, _aidl_status.get()); + if (_aidl_ret_status != STATUS_OK) break; + + if (!AStatus_isOk(_aidl_status.get())) break; + + break; + } + case (FIRST_CALL_TRANSACTION + 2 /*onTouchMove*/): { + float in_x; + float in_y; + + _aidl_ret_status = ::ndk::AParcel_readData(_aidl_in, &in_x); + if (_aidl_ret_status != STATUS_OK) break; + + _aidl_ret_status = ::ndk::AParcel_readData(_aidl_in, &in_y); + if (_aidl_ret_status != STATUS_OK) break; + + ::ndk::ScopedAStatus _aidl_status = _aidl_impl->onTouchMove(in_x, in_y); + _aidl_ret_status = AParcel_writeStatusHeader(_aidl_out, _aidl_status.get()); + if (_aidl_ret_status != STATUS_OK) break; + + if (!AStatus_isOk(_aidl_status.get())) break; + + break; + } + case (FIRST_CALL_TRANSACTION + 3 /*onTouchUp*/): { + float in_x; + float in_y; + + _aidl_ret_status = ::ndk::AParcel_readData(_aidl_in, &in_x); + if (_aidl_ret_status != STATUS_OK) break; + + _aidl_ret_status = ::ndk::AParcel_readData(_aidl_in, &in_y); + if (_aidl_ret_status != STATUS_OK) break; + + ::ndk::ScopedAStatus _aidl_status = _aidl_impl->onTouchUp(in_x, in_y); + _aidl_ret_status = AParcel_writeStatusHeader(_aidl_out, _aidl_status.get()); + if (_aidl_ret_status != STATUS_OK) break; + + if (!AStatus_isOk(_aidl_status.get())) break; + + break; + } + } + return _aidl_ret_status; +} + +static AIBinder_Class* _g_aidl_com_omixlab_mosis_IMosisService_clazz = ::ndk::ICInterface::defineClass(IMosisService::descriptor, _aidl_com_omixlab_mosis_IMosisService_onTransact, nullptr, 0); + +BpMosisService::BpMosisService(const ::ndk::SpAIBinder& binder) : BpCInterface(binder) {} +BpMosisService::~BpMosisService() {} + +::ndk::ScopedAStatus BpMosisService::initOS(const std::shared_ptr<::aidl::com::omixlab::mosis::IMosisListener>& in_listener, bool* _aidl_return) { + binder_status_t _aidl_ret_status = STATUS_OK; + ::ndk::ScopedAStatus _aidl_status; + ::ndk::ScopedAParcel _aidl_in; + ::ndk::ScopedAParcel _aidl_out; + + _aidl_ret_status = AIBinder_prepareTransaction(asBinderReference().get(), _aidl_in.getR()); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = ::ndk::AParcel_writeData(_aidl_in.get(), in_listener); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = AIBinder_transact( + asBinderReference().get(), + (FIRST_CALL_TRANSACTION + 0 /*initOS*/), + _aidl_in.getR(), + _aidl_out.getR(), + 0 + #ifdef BINDER_STABILITY_SUPPORT + | static_cast(FLAG_PRIVATE_LOCAL) + #endif // BINDER_STABILITY_SUPPORT + ); + if (_aidl_ret_status == STATUS_UNKNOWN_TRANSACTION && IMosisService::getDefaultImpl()) { + _aidl_status = IMosisService::getDefaultImpl()->initOS(in_listener, _aidl_return); + goto _aidl_status_return; + } + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = AParcel_readStatusHeader(_aidl_out.get(), _aidl_status.getR()); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + if (!AStatus_isOk(_aidl_status.get())) goto _aidl_status_return; + _aidl_ret_status = ::ndk::AParcel_readData(_aidl_out.get(), _aidl_return); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_error: + _aidl_status.set(AStatus_fromStatus(_aidl_ret_status)); + _aidl_status_return: + return _aidl_status; +} +::ndk::ScopedAStatus BpMosisService::onTouchDown(float in_x, float in_y) { + binder_status_t _aidl_ret_status = STATUS_OK; + ::ndk::ScopedAStatus _aidl_status; + ::ndk::ScopedAParcel _aidl_in; + ::ndk::ScopedAParcel _aidl_out; + + _aidl_ret_status = AIBinder_prepareTransaction(asBinderReference().get(), _aidl_in.getR()); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = ::ndk::AParcel_writeData(_aidl_in.get(), in_x); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = ::ndk::AParcel_writeData(_aidl_in.get(), in_y); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = AIBinder_transact( + asBinderReference().get(), + (FIRST_CALL_TRANSACTION + 1 /*onTouchDown*/), + _aidl_in.getR(), + _aidl_out.getR(), + 0 + #ifdef BINDER_STABILITY_SUPPORT + | static_cast(FLAG_PRIVATE_LOCAL) + #endif // BINDER_STABILITY_SUPPORT + ); + if (_aidl_ret_status == STATUS_UNKNOWN_TRANSACTION && IMosisService::getDefaultImpl()) { + _aidl_status = IMosisService::getDefaultImpl()->onTouchDown(in_x, in_y); + goto _aidl_status_return; + } + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = AParcel_readStatusHeader(_aidl_out.get(), _aidl_status.getR()); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + if (!AStatus_isOk(_aidl_status.get())) goto _aidl_status_return; + _aidl_error: + _aidl_status.set(AStatus_fromStatus(_aidl_ret_status)); + _aidl_status_return: + return _aidl_status; +} +::ndk::ScopedAStatus BpMosisService::onTouchMove(float in_x, float in_y) { + binder_status_t _aidl_ret_status = STATUS_OK; + ::ndk::ScopedAStatus _aidl_status; + ::ndk::ScopedAParcel _aidl_in; + ::ndk::ScopedAParcel _aidl_out; + + _aidl_ret_status = AIBinder_prepareTransaction(asBinderReference().get(), _aidl_in.getR()); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = ::ndk::AParcel_writeData(_aidl_in.get(), in_x); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = ::ndk::AParcel_writeData(_aidl_in.get(), in_y); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = AIBinder_transact( + asBinderReference().get(), + (FIRST_CALL_TRANSACTION + 2 /*onTouchMove*/), + _aidl_in.getR(), + _aidl_out.getR(), + 0 + #ifdef BINDER_STABILITY_SUPPORT + | static_cast(FLAG_PRIVATE_LOCAL) + #endif // BINDER_STABILITY_SUPPORT + ); + if (_aidl_ret_status == STATUS_UNKNOWN_TRANSACTION && IMosisService::getDefaultImpl()) { + _aidl_status = IMosisService::getDefaultImpl()->onTouchMove(in_x, in_y); + goto _aidl_status_return; + } + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = AParcel_readStatusHeader(_aidl_out.get(), _aidl_status.getR()); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + if (!AStatus_isOk(_aidl_status.get())) goto _aidl_status_return; + _aidl_error: + _aidl_status.set(AStatus_fromStatus(_aidl_ret_status)); + _aidl_status_return: + return _aidl_status; +} +::ndk::ScopedAStatus BpMosisService::onTouchUp(float in_x, float in_y) { + binder_status_t _aidl_ret_status = STATUS_OK; + ::ndk::ScopedAStatus _aidl_status; + ::ndk::ScopedAParcel _aidl_in; + ::ndk::ScopedAParcel _aidl_out; + + _aidl_ret_status = AIBinder_prepareTransaction(asBinderReference().get(), _aidl_in.getR()); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = ::ndk::AParcel_writeData(_aidl_in.get(), in_x); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = ::ndk::AParcel_writeData(_aidl_in.get(), in_y); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = AIBinder_transact( + asBinderReference().get(), + (FIRST_CALL_TRANSACTION + 3 /*onTouchUp*/), + _aidl_in.getR(), + _aidl_out.getR(), + 0 + #ifdef BINDER_STABILITY_SUPPORT + | static_cast(FLAG_PRIVATE_LOCAL) + #endif // BINDER_STABILITY_SUPPORT + ); + if (_aidl_ret_status == STATUS_UNKNOWN_TRANSACTION && IMosisService::getDefaultImpl()) { + _aidl_status = IMosisService::getDefaultImpl()->onTouchUp(in_x, in_y); + goto _aidl_status_return; + } + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + _aidl_ret_status = AParcel_readStatusHeader(_aidl_out.get(), _aidl_status.getR()); + if (_aidl_ret_status != STATUS_OK) goto _aidl_error; + + if (!AStatus_isOk(_aidl_status.get())) goto _aidl_status_return; + _aidl_error: + _aidl_status.set(AStatus_fromStatus(_aidl_ret_status)); + _aidl_status_return: + return _aidl_status; +} +// Source for BnMosisService +BnMosisService::BnMosisService() {} +BnMosisService::~BnMosisService() {} +::ndk::SpAIBinder BnMosisService::createBinder() { + AIBinder* binder = AIBinder_new(_g_aidl_com_omixlab_mosis_IMosisService_clazz, static_cast(this)); + #ifdef BINDER_STABILITY_SUPPORT + AIBinder_markCompilationUnitStability(binder); + #endif // BINDER_STABILITY_SUPPORT + return ::ndk::SpAIBinder(binder); +} +// Source for IMosisService +const char* IMosisService::descriptor = "com.omixlab.mosis.IMosisService"; +IMosisService::IMosisService() {} +IMosisService::~IMosisService() {} + + +std::shared_ptr IMosisService::fromBinder(const ::ndk::SpAIBinder& binder) { + if (!AIBinder_associateClass(binder.get(), _g_aidl_com_omixlab_mosis_IMosisService_clazz)) { + #if __ANDROID_API__ >= 31 + const AIBinder_Class* originalClass = AIBinder_getClass(binder.get()); + if (originalClass == nullptr) return nullptr; + if (0 == strcmp(AIBinder_Class_getDescriptor(originalClass), descriptor)) { + return ::ndk::SharedRefBase::make(binder); + } + #endif + return nullptr; + } + std::shared_ptr<::ndk::ICInterface> interface = ::ndk::ICInterface::asInterface(binder.get()); + if (interface) { + return std::static_pointer_cast(interface); + } + return ::ndk::SharedRefBase::make(binder); +} + +binder_status_t IMosisService::writeToParcel(AParcel* parcel, const std::shared_ptr& instance) { + return AParcel_writeStrongBinder(parcel, instance ? instance->asBinder().get() : nullptr); +} +binder_status_t IMosisService::readFromParcel(const AParcel* parcel, std::shared_ptr* instance) { + ::ndk::SpAIBinder binder; + binder_status_t status = AParcel_readStrongBinder(parcel, binder.getR()); + if (status != STATUS_OK) return status; + *instance = IMosisService::fromBinder(binder); + return STATUS_OK; +} +bool IMosisService::setDefaultImpl(const std::shared_ptr& impl) { + // Only one user of this interface can use this function + // at a time. This is a heuristic to detect if two different + // users in the same process use this function. + assert(!IMosisService::default_impl); + if (impl) { + IMosisService::default_impl = impl; + return true; + } + return false; +} +const std::shared_ptr& IMosisService::getDefaultImpl() { + return IMosisService::default_impl; +} +std::shared_ptr IMosisService::default_impl = nullptr; +::ndk::ScopedAStatus IMosisServiceDefault::initOS(const std::shared_ptr<::aidl::com::omixlab::mosis::IMosisListener>& /*in_listener*/, bool* /*_aidl_return*/) { + ::ndk::ScopedAStatus _aidl_status; + _aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION)); + return _aidl_status; +} +::ndk::ScopedAStatus IMosisServiceDefault::onTouchDown(float /*in_x*/, float /*in_y*/) { + ::ndk::ScopedAStatus _aidl_status; + _aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION)); + return _aidl_status; +} +::ndk::ScopedAStatus IMosisServiceDefault::onTouchMove(float /*in_x*/, float /*in_y*/) { + ::ndk::ScopedAStatus _aidl_status; + _aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION)); + return _aidl_status; +} +::ndk::ScopedAStatus IMosisServiceDefault::onTouchUp(float /*in_x*/, float /*in_y*/) { + ::ndk::ScopedAStatus _aidl_status; + _aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION)); + return _aidl_status; +} +::ndk::SpAIBinder IMosisServiceDefault::asBinder() { + return ::ndk::SpAIBinder(); +} +bool IMosisServiceDefault::isRemote() { + return false; +} +} // namespace mosis +} // namespace omixlab +} // namespace com +} // namespace aidl diff --git a/Plugins/MosisSDK/Source/MosisSDK/Java/com/omixlab/mosis/MyKotlinSource.kt b/Plugins/MosisSDK/Source/MosisSDK/Java/com/omixlab/mosis/MyKotlinSource.kt new file mode 100644 index 0000000..d07c511 --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/Java/com/omixlab/mosis/MyKotlinSource.kt @@ -0,0 +1,45 @@ +package com.omixlab.mosis + +import android.util.Log +import android.content.ComponentName +import android.content.Context.BIND_AUTO_CREATE +import android.content.Intent +import android.content.ServiceConnection +import android.os.IBinder +import androidx.core.content.ContextCompat.startForegroundService +import com.omixlab.mosis.IMosisService +import com.epicgames.unreal.GameActivity + +class MyKotlinPlugin { + companion object { + val instance: MyKotlinPlugin by lazy { MyKotlinPlugin() } + @JvmStatic + fun StartMosisService() { + instance.startRemoteService() + } + } + var remote_service: IMosisService? = null + val connection = object : ServiceConnection { + override fun onServiceConnected(className: ComponentName, service: IBinder) { + Log.d("MosisTest", "Service Connected") + remote_service = IMosisService.Stub.asInterface(service) + serviceConnected(service) + } + override fun onServiceDisconnected(arg0: ComponentName) { + Log.d("MosisTest", "Service Disconnected") + } + } + fun startRemoteService() { + val intent = Intent("com.omixlab.mosis.SERVICE") + intent.setPackage("com.omixlab.mosis") + val currentActivity = GameActivity.Get() + try { + startForegroundService(currentActivity, intent) + val result = currentActivity.bindService(intent, connection, BIND_AUTO_CREATE) + Log.d("MosisTest", "Bind result: $result") + } catch (e: Exception) { + Log.e("MosisTest", "Bind failed", e) + } + } + external fun serviceConnected(binder: IBinder) +} diff --git a/Plugins/MosisSDK/Source/MosisSDK/MosisSDK.Build.cs b/Plugins/MosisSDK/Source/MosisSDK/MosisSDK.Build.cs new file mode 100644 index 0000000..00891c2 --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/MosisSDK.Build.cs @@ -0,0 +1,70 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using System; +using System.Diagnostics; +using System.IO; +using UnrealBuildTool; + +public class MosisSDK : ModuleRules +{ + public MosisSDK(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + "Launch" + // ... add private dependencies that you statically link with here ... + } + ); + + if (Target.Platform == UnrealTargetPlatform.Android) + { + // Register the UPL file + string PluginPath = Utils.MakePathRelativeTo(ModuleDirectory, Target.RelativeEnginePath); + AdditionalPropertiesForReceipt.Add("AndroidPlugin", Path.Combine(PluginPath, "MosisSDK_UPL.xml")); + PublicAdditionalLibraries.Add("binder_ndk"); + + string SDKPath = Environment.GetEnvironmentVariable("ANDROID_HOME"); + string BinderPath = Path.Combine(SDKPath, "platforms/android-36/optional/libbinder_ndk_cpp"); + PublicIncludePaths.Add(BinderPath); + + string AidlPath = Path.Combine(SDKPath, "build-tools/36.1.0/aidl.exe"); + string AidlSourceDir = Path.Combine(ModuleDirectory, "AIDL"); + string OutputHeaderDir = Path.Combine(ModuleDirectory, "Generated"); + string OutputCppDir = OutputHeaderDir; + + string[] Files = Directory.GetFiles(Path.Combine(AidlSourceDir, "com/omixlab/mosis"), "*.aidl", SearchOption.AllDirectories); + + foreach (string FilePath in Files) + { + string Args = $"--lang=ndk --min_sdk_version=36 -I \"{AidlSourceDir}\" -o \"{OutputCppDir}\" -h \"{OutputHeaderDir}\" \"{FilePath}\""; + ProcessStartInfo PSI = new ProcessStartInfo(); + PSI.FileName = AidlPath; + PSI.Arguments = Args; + PSI.RedirectStandardOutput = true; + PSI.RedirectStandardError = true; + PSI.UseShellExecute = false; + PSI.CreateNoWindow = true; + + Console.WriteLine($"Running AIDL : {Args}"); + + using (Process Proc = Process.Start(PSI)) + { + Proc.WaitForExit(); + if (Proc.ExitCode != 0) + { + string Error = Proc.StandardError.ReadToEnd(); + Console.WriteLine($"AIDL Error: {Error}"); + throw new BuildException($"Failed to compile AIDL: {FilePath}"); + } + } + } + } + } +} diff --git a/Plugins/MosisSDK/Source/MosisSDK/MosisSDK_UPL.xml b/Plugins/MosisSDK/Source/MosisSDK/MosisSDK_UPL.xml new file mode 100644 index 0000000..f4ff1e1 --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/MosisSDK_UPL.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + -keep class com.omixlab.mosis.MyKotlinPlugin { *; } + -keep interface com.omixlab.mosis.IMosisListener { *; } + -keep interface com.omixlab.mosis.IMosisService { *; } + + + + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:2.3.0" + } + + + + + apply plugin: 'kotlin-android' + android { + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { + aidl = true + } + sourceSets { + main { + kotlin.srcDirs += ['src/main/kotlin'] + java.srcDirs += ['src/main/aidl_gen'] + } + } + } + dependencies { + implementation 'org.jetbrains.kotlin:kotlin-stdlib:2.3.0' + } + + + + + + + diff --git a/Plugins/MosisSDK/Source/MosisSDK/Private/MosisSDK.cpp b/Plugins/MosisSDK/Source/MosisSDK/Private/MosisSDK.cpp new file mode 100644 index 0000000..6aa1f95 --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/Private/MosisSDK.cpp @@ -0,0 +1,46 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "MosisSDK.h" + +#if PLATFORM_ANDROID + +#include "Android/AndroidApplication.h" +#include "Android/AndroidJNI.h" +#include "Android/AndroidJava.h" +#include + +void FMosisSDKModule::StartupModule() +{ + if (JNIEnv* Env = FAndroidApplication::GetJavaEnv()) + { + jclass ManagerClass = FAndroidApplication::FindJavaClass("com/omixlab/mosis/MyKotlinPlugin"); + if (ManagerClass) + { + jmethodID BindMethod = Env->GetStaticMethodID(ManagerClass, "StartMosisService", "()V"); + Env->CallStaticVoidMethod(ManagerClass, BindMethod); + UE_LOG(LogTemp, Log, TEXT("Requested Bind to Android Service...")); + } + } +} + +void FMosisSDKModule::ShutdownModule() +{ +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_omixlab_mosis_MyKotlinPlugin_serviceConnected(JNIEnv* env, jobject thiz, + jobject binder) +{ + AIBinder* pBinder = AIBinder_fromJavaBinder(env, binder); + //const ndk::SpAIBinder spBinder(pBinder); + //g_service = IMosisService::fromBinder(spBinder); + //Logger::Log("Service Connected"); + //g_context = ndk::SharedRefBase::make(); + //bool result{}; + //g_service->initOS(g_context, &result); + //Logger::Log(std::format("InitOS returned {}", result)); +} + +#endif +IMPLEMENT_MODULE(FMosisSDKModule, MosisSDK) diff --git a/Plugins/MosisSDK/Source/MosisSDK/Public/MosisSDK.h b/Plugins/MosisSDK/Source/MosisSDK/Public/MosisSDK.h new file mode 100644 index 0000000..1bf380b --- /dev/null +++ b/Plugins/MosisSDK/Source/MosisSDK/Public/MosisSDK.h @@ -0,0 +1,15 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +class FMosisSDKModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +};