diff --git a/src/Tizen.Applications.Team/CultureInfoHelper.cs b/src/Tizen.Applications.Team/CultureInfoHelper.cs new file mode 100644 index 00000000000..5ca0e624ac1 --- /dev/null +++ b/src/Tizen.Applications.Team/CultureInfoHelper.cs @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Tizen.Applications +{ + /// + /// Resolves culture names from the platform-provided CultureInfo INI file. + /// + internal static class CultureInfoHelper + { + private const string _pathCultureInfoIni = "/usr/share/dotnet.tizen/framework/i18n/CultureInfo.ini"; + private static bool _fileExists = File.Exists(_pathCultureInfoIni); + + public static string GetCultureName(string locale) + { + if (!_fileExists) + { + return string.Empty; + } + + IntPtr dictionary = Interop.LibIniParser.Load(_pathCultureInfoIni); + if (dictionary == IntPtr.Zero) + { + return string.Empty; + } + + string cultureName = string.Empty; +#pragma warning disable CA1308 + string key = "CultureInfo:" + locale.ToLowerInvariant(); +#pragma warning restore CA1308 + IntPtr value = Interop.LibIniParser.GetString(dictionary, key, IntPtr.Zero); + if (value != IntPtr.Zero) + { + cultureName = Marshal.PtrToStringAnsi(value); + } + + Interop.LibIniParser.FreeDict(dictionary); + return cultureName; + } + } +} \ No newline at end of file diff --git a/src/Tizen.Applications.Team/GSourceManager.cs b/src/Tizen.Applications.Team/GSourceManager.cs new file mode 100644 index 00000000000..d7b1358621e --- /dev/null +++ b/src/Tizen.Applications.Team/GSourceManager.cs @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.Collections.Concurrent; + +namespace Tizen.Applications +{ + /// + /// Posts actions onto the GLib main loop used by the Team application. + /// + internal static class GSourceManager + { + private static readonly GSourceContext _tizenContext = new GSourceContext(); + private static readonly GSourceContext _tizenUIContext = new GSourceContext(); + private static readonly Interop.Glib.GSourceFunc _wrapperHandler = new Interop.Glib.GSourceFunc(Handler); + + public static void Post(Action action) + { + IntPtr context = IntPtr.Zero; + var sourceContext = context == IntPtr.Zero ? _tizenContext : _tizenUIContext; + sourceContext.Post(action, context, _wrapperHandler); + } + + private static bool Handler(IntPtr userData) + { + var sourceContext = userData == IntPtr.Zero ? _tizenContext : _tizenUIContext; + return sourceContext.ProcessQueue(); + } + } + + /// + /// Holds the GLib source state and action queue for a single main loop context. + /// + internal class GSourceContext + { + private readonly ConcurrentQueue _actionQueue = new ConcurrentQueue(); + private readonly object _lock = new object(); + private IntPtr _source = IntPtr.Zero; + + public void Post(Action action, IntPtr context, Interop.Glib.GSourceFunc handler) + { + _actionQueue.Enqueue(action); + + lock (_lock) + { + if (_source != IntPtr.Zero) + { + return; + } + + _source = Interop.Glib.IdleSourceNew(); + Interop.Glib.SourceSetCallback(_source, handler, context, IntPtr.Zero); + _ = Interop.Glib.SourceAttach(_source, context); + Interop.Glib.SourceUnref(_source); + } + } + + public bool ProcessQueue() + { + if (_actionQueue.TryDequeue(out var action)) + { + action?.Invoke(); + } + + lock (_lock) + { + if (!_actionQueue.IsEmpty) + { + return true; + } + + _source = IntPtr.Zero; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Tizen.Applications.Team/Interop/Interop.ApplicationManager.cs b/src/Tizen.Applications.Team/Interop/Interop.ApplicationManager.cs new file mode 100755 index 00000000000..24d37b61319 --- /dev/null +++ b/src/Tizen.Applications.Team/Interop/Interop.ApplicationManager.cs @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.Runtime.InteropServices; + +using Tizen.Internals; + +internal static partial class Interop +{ + internal static partial class ApplicationManager + { + internal enum ErrorCode + { + None = Tizen.Internals.Errors.ErrorCode.None, + InvalidParameter = Tizen.Internals.Errors.ErrorCode.InvalidParameter, + OutOfMemory = Tizen.Internals.Errors.ErrorCode.OutOfMemory, + IoError = Tizen.Internals.Errors.ErrorCode.IoError, + NoSuchApp = -0x01110000 | 0x01, + DbFailed = -0x01110000 | 0x03, + InvalidPackage = -0x01110000 | 0x04, + AppNoRunning = -0x01110000 | 0x05, + RequestFailed = -0x01110000 | 0x06, + PermissionDenied = Tizen.Internals.Errors.ErrorCode.PermissionDenied + } + + internal enum AppContextEvent + { + Launched = 0, + Terminated = 1 + } + + internal enum AppManagerEventStatusType + { + All = 0x00, + Enable = 0x01, + Disable = 0x02 + } + + internal enum AppManagerEventType + { + Enable = 0, + Disable = 1 + } + + internal enum AppManagerEventState + { + Started = 0, + Completed = 1, + Failed = 2 + } + + internal enum AppInfoAppComponentType + { + UiApp = 0, + ServiceApp = 1, + WidgetApp = 2, + WatchApp = 3 + } + + internal enum AppLifecycleState + { + Initialized = 0, + Created = 1, + Resumed = 2, + Paused = 3, + Destroyed = 4 + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void AppManagerLifecycleStateChangedCallback(string appId, int pid, AppLifecycleState state, bool hasFocus, IntPtr userData); + //void (*app_manager_lifecycle_state_changed_cb)(const char *app_id, pid_t pid, app_manager_lifecycle_state_e state, bool has_focus, void *user_data) + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void AppManagerEventCallback(string appType, string appId, AppManagerEventType eventType, AppManagerEventState eventState, IntPtr eventHandle, IntPtr userData); + //void(* app_manager_event_cb)(const char *type, const char *app_id, app_manager_event_type_e event_type, app_manager_event_state_e event_state, app_manager_event_h handle, void *user_data) + + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void AppManagerAppContextEventCallback(IntPtr handle, AppContextEvent state, IntPtr userData); + //void(* app_manager_app_context_event_cb)(app_context_h app_context, app_context_event_e event, void *user_data) + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate bool AppManagerAppInfoCallback(IntPtr handle, IntPtr userData); + //bool(* app_manager_app_info_cb )(app_info_h app_info, void *user_data) + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate bool AppManagerAppContextCallback(IntPtr handle, IntPtr userData); + //bool(* app_manager_app_context_cb)(app_context_h app_context, void *user_data) + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate bool AppInfoFilterCallback(IntPtr handle, IntPtr userData); + //bool(* app_info_filter_cb )(app_info_h app_info, void *user_data) + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate bool AppInfoMetadataCallback(string key, string value, IntPtr userData); + //bool(* app_info_metadata_cb )(const char *metadata_key, const char *metadata_value, void *user_data) + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate bool AppInfoCategoryCallback(string category, IntPtr userData); + //bool (*app_info_category_cb) (const char *category, void *user_data) + + internal delegate bool AppInfoResControlCallback(string resType, string minResVersion, string maxResVersion, string autoClose, IntPtr userUdata); + //bool (*app_info_res_control_cb) (const char *res_type, const char *min_res_version, const char *max_res_version, const char *auto_close, void *user_data); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_set_app_context_event_cb")] + internal static extern ErrorCode AppManagerSetAppContextEvent(AppManagerAppContextEventCallback callback, IntPtr userData); + //int app_manager_set_app_context_event_cb( app_manager_app_context_event_cb callback, void * user_data) + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_unset_app_context_event_cb")] + internal static extern void AppManagerUnSetAppContextEvent(); + //void app_manager_unset_app_context_event_cb (void); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_set_lifecycle_state_changed_cb")] + internal static extern ErrorCode AppManagerSetLifecycleStateChangedCb(AppManagerLifecycleStateChangedCallback callback, IntPtr userData); + //int app_manager_set_lifecycle_state_changed_cb(app_manager_lifecycle_state_changed_cb callback, void *user_data) + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_unset_lifecycle_state_changed_cb")] + internal static extern void AppManagerUnsetLifecycleStateChangedCb(); + //void app_manager_unset_lifecycle_state_changed_cb(void) + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_add_lifecycle_state_changed_cb")] + internal static extern ErrorCode AppManagerAddLifecycleStateChangedCb( + AppManagerLifecycleStateChangedCallback callback, IntPtr userData, out IntPtr handle); + // int app_manager_add_lifecycle_state_changed_cb(app_manager_lifecycle_state_changed_cb callback, + // void *user_data, app_manager_lifecycle_noti_h *handle) + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_remove_lifecycle_state_changed_cb")] + internal static extern ErrorCode AppManagerRemoveLifecycleStateChangedCb(IntPtr handle); + // int app_manager_remove_lifecycle_state_changed_cb(app_manager_lifecycle_noti_h handle) + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_foreach_running_app_context")] + internal static extern ErrorCode AppManagerForeachRunningAppContext(AppManagerAppContextCallback callback, IntPtr userData); + //int app_manager_foreach_running_app_context(app_manager_app_context_cb callback, void *user_data) + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_foreach_app_context")] + internal static extern ErrorCode AppManagerForeachAppContext(AppManagerAppContextCallback callback, IntPtr userData); + //int app_manager_foreach_app_context(app_manager_app_context_cb callback, void *user_data) + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_get_app_context")] + internal static extern ErrorCode AppManagerGetAppContext(string applicationId, out IntPtr handle); + //int app_manager_get_app_context(const char* app_id, app_context_h *app_context); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_get_app_id")] + internal static extern ErrorCode AppManagerGetAppId(int processId, out string applicationId); + //int app_manager_get_app_id (pid_t pid, char **appid); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_is_running")] + internal static extern ErrorCode AppManagerIsRunning(string applicationId, out bool running); + //int app_manager_is_running (const char *appid, bool *running); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_resume_app")] + internal static extern ErrorCode AppManagerResumeApp(IntPtr handle); + //int app_manager_resume_app (app_context_h handle); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_request_terminate_bg_app")] + internal static extern ErrorCode AppManagerRequestTerminateBgApp(IntPtr handle); + //int app_manager_request_terminate_bg_app (app_context_h handle); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_foreach_app_info")] + internal static extern ErrorCode AppManagerForeachAppInfo(AppManagerAppInfoCallback callback, IntPtr userData); + //int app_manager_foreach_app_info(app_manager_app_info_cb callback, void *user_data) + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_get_app_info")] + internal static extern ErrorCode AppManagerGetAppInfo(string applicationId, out IntPtr handle); + //int app_manager_get_app_info(const char * app_id, app_info_h * app_info) + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_get_shared_data_path")] + internal static extern ErrorCode AppManagerGetSharedDataPath(string applicationId, out string path); + //int app_manager_get_shared_data_path (const char *appid, char **path); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_get_shared_resource_path")] + internal static extern ErrorCode AppManagerGetSharedResourcePath(string applicationId, out string path); + //int app_manager_get_shared_resource_path (const char *appid, char **path); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_get_shared_trusted_path")] + internal static extern ErrorCode AppManagerGetSharedTrustedPath(string applicationId, out string path); + //int app_manager_get_shared_trusted_path (const char *appid, char **path); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_get_common_shared_data_path")] + internal static extern ErrorCode AppManagerGetCommonSharedDataPath(string applicationId, out string path); + //int app_manager_get_common_shared_data_path (const char *appid, char **path); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_get_common_shared_trusted_path")] + internal static extern ErrorCode AppManagerGetCommonSharedTrustedPath(string applicationId, out string path); + //int app_manager_get_common_shared_trusted_path (const char *appid, char **path); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_get_external_shared_data_path")] + internal static extern ErrorCode AppManagerGetExternalSharedDataPath(string applicationId, out string path); + //int app_manager_get_external_shared_data_path (const char *appid, char **path); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_event_create")] + internal static extern ErrorCode AppManagerEventCreate(out IntPtr handle); + //int app_manager_event_create (app_manager_event_h *handle); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_event_set_status")] + internal static extern ErrorCode AppManagerEventSetStatus(IntPtr handle, AppManagerEventStatusType statusType); + //int app_manager_event_set_status (app_manager_event_h handle, int status_type); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_set_event_cb")] + internal static extern ErrorCode AppManagerSetEventCallback(IntPtr handle, AppManagerEventCallback callback, IntPtr userData); + //int app_manager_set_event_cb (app_manager_event_h handle, app_manager_event_cb callback, void *user_data); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_unset_event_cb")] + internal static extern ErrorCode AppManagerUnSetEventCallback(IntPtr handle); + //int app_manager_unset_event_cb (app_manager_event_h handle); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_event_destroy")] + internal static extern ErrorCode AppManagerEventDestroy(IntPtr handle); + //int app_manager_event_destroy (app_manager_event_h handle); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_terminate_app")] + internal static extern ErrorCode AppManagerTerminateApp(IntPtr handle); + //int app_manager_terminate_app (app_context_h app_context); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_terminate_app_without_restarting")] + internal static extern ErrorCode AppManagerTerminateAppWithoutRestarting(IntPtr handle); + //int app_manager_terminate_app_without_restarting (app_context_h app_context); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_get_app_context_by_instance_id")] + internal static extern ErrorCode AppManagerGetAppContextByInstanceId(string applicationId, string instanceId, out IntPtr handle); + //int app_manager_get_app_context_by_instance_id (const char *app_id, const char *instance_id, app_context_h *app_context); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_attach_window")] + internal static extern ErrorCode AppManagerAttachWindow(string parentAppId, string childAppId); + //int app_manager_attach_window(const char *parent_app_id, const char *child_app_id); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_detach_window")] + internal static extern ErrorCode AppManagerDetachWindow(string applicationId); + //int app_manager_detach_window(const char *app_id); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_attach_window_below")] + internal static extern ErrorCode AppManagerAttachWindowBelow(string parentAppId, string childAppId); + //int app_manager_attach_window_below(const char *parent_app_id, const char *child_app_id); + + [DllImport(Libraries.AppManager, EntryPoint = "app_manager_request_remount_subsession")] + internal static extern ErrorCode AppManagerRequestRemountSubsession(string subsessionId); + //int app_manager_request_remount_subsession(const char *subsession_id); + + [DllImport(Libraries.AppManager, EntryPoint = "app_context_destroy")] + internal static extern ErrorCode AppContextDestroy(IntPtr handle); + //int app_context_destroy(app_context_h app_context) + + [DllImport(Libraries.AppManager, EntryPoint = "app_context_get_app_id")] + internal static extern ErrorCode AppContextGetAppId(IntPtr handle, out string applicationId); + //int app_context_get_app_id(app_context_h app_context, char **app_id) + + [DllImport(Libraries.AppManager, EntryPoint = "app_context_get_package_id")] + internal static extern ErrorCode AppContextGetPackageId(IntPtr handle, out string packageId); + //int app_context_get_package_id(app_context_h app_context, char **package_id) + + [DllImport(Libraries.AppManager, EntryPoint = "app_context_get_pid")] + internal static extern ErrorCode AppContextGetPid(IntPtr handle, out int processId); + //int app_context_get_pid (app_context_h app_context, pid_t *pid) + + [DllImport(Libraries.AppManager, EntryPoint = "app_context_get_app_state")] + internal static extern ErrorCode AppContextGetAppState(IntPtr handle, out int state); + //int app_context_get_app_state (app_context_h app_context, app_state_e *state) + + [DllImport(Libraries.AppManager, EntryPoint = "app_context_is_terminated")] + internal static extern ErrorCode AppContextIsTerminated(IntPtr handle, out bool terminated); + //int app_context_is_terminated (app_context_h app_context, bool *terminated); + + [DllImport(Libraries.AppManager, EntryPoint = "app_context_is_equal")] + internal static extern ErrorCode AppContextIsEqual(IntPtr first, IntPtr second, out bool equal); + //int app_context_is_equal (app_context_h lhs, app_context_h rhs, bool *equal); + + [DllImport(Libraries.AppManager, EntryPoint = "app_context_is_sub_app")] + internal static extern ErrorCode AppContextIsSubApp(IntPtr handle, out bool is_sub_app); + //int app_context_is_sub_app (app_context_h app_context, bool *is_sub_app); + + [DllImport(Libraries.AppManager, EntryPoint = "app_context_clone")] + internal static extern ErrorCode AppContextClone(out IntPtr destination, IntPtr source); + //int app_context_clone (app_context_h *clone, app_context_h app_context); + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_create")] + internal static extern ErrorCode AppInfoCreate(string applicationId, out IntPtr handle); + //int app_info_create (const char *app_id, app_info_h *app_info); + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_destroy")] + internal static extern ErrorCode AppInfoDestroy(IntPtr handle); + //int app_info_destroy (app_info_h app_info); + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_get_app_id")] + internal static extern ErrorCode AppInfoGetAppId(IntPtr handle, out string applicationId); + //int app_info_get_app_id (app_info_h app_info, char **app_id); + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_get_exec")] + internal static extern ErrorCode AppInfoGetExec(IntPtr handle, out string exec); + //int app_info_get_exec (app_info_h app_info, char **exec); + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_get_label")] + internal static extern ErrorCode AppInfoGetLabel(IntPtr handle, out string label); + //int app_info_get_label (app_info_h app_info, char **label); + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_get_localed_label")] + internal static extern ErrorCode AppInfoGetLocaledLabel(string applicationId, string locale, out string label); + //int app_info_get_localed_label (const char *app_id, const char *locale, char **label); + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_get_icon")] + internal static extern ErrorCode AppInfoGetIcon(IntPtr handle, out string path); + //int app_info_get_icon (app_info_h app_info, char **path) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_get_package")] + internal static extern ErrorCode AppInfoGetPackage(IntPtr handle, out string package); + //int app_info_get_package (app_info_h app_info, char **package) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_get_type")] + internal static extern ErrorCode AppInfoGetType(IntPtr handle, out string type); + //int app_info_get_type (app_info_h app_info, char **type) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_get_app_component_type")] + internal static extern ErrorCode AppInfoGetAppComponentType(IntPtr handle, out AppInfoAppComponentType type); + //int app_info_get_app_component_type(app_info_h app_info, app_info_app_component_type_e *type) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_foreach_metadata")] + internal static extern ErrorCode AppInfoForeachMetadata(IntPtr handle, AppInfoMetadataCallback callback, IntPtr userData); + //int app_info_foreach_metadata(app_info_h app_info, app_info_metadata_cb callback, void *user_data) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_is_nodisplay")] + internal static extern ErrorCode AppInfoIsNodisplay(IntPtr handle, out bool noDisplay); + //int app_info_is_nodisplay (app_info_h app_info, bool *nodisplay) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_is_equal")] + internal static extern ErrorCode AppInfoIsEqual(IntPtr first, IntPtr second, out bool equal); + //int app_info_is_equal (app_info_h lhs, app_info_h rhs, bool *equal) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_is_enabled")] + internal static extern ErrorCode AppInfoIsEnabled(IntPtr handle, out bool enabled); + //int app_info_is_enabled (app_info_h app_info, bool *enabled) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_is_onboot")] + internal static extern ErrorCode AppInfoIsOnBoot(IntPtr handle, out bool onBoot); + //int app_info_is_onboot (app_info_h app_info, bool *onboot) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_is_preload")] + internal static extern ErrorCode AppInfoIsPreLoad(IntPtr handle, out bool preLoaded); + //int app_info_is_preload (app_info_h app_info, bool *preload) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_clone")] + internal static extern ErrorCode AppInfoClone(out IntPtr destination, IntPtr source); + //int app_info_clone(app_info_h * clone, app_info_h app_info) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_foreach_category")] + internal static extern ErrorCode AppInfoForeachCategory(IntPtr handle, AppInfoCategoryCallback callback, IntPtr userData); + //int app_info_foreach_category(app_info_h app_info, app_info_category_cb callback, void *user_data) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_filter_create")] + internal static extern ErrorCode AppInfoFilterCreate(out IntPtr handle); + //int app_info_filter_create(app_info_filter_h * handle) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_filter_destroy")] + internal static extern ErrorCode AppInfoFilterDestroy(IntPtr handle); + //int app_info_filter_destroy(app_info_filter_h handle) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_filter_add_bool")] + internal static extern ErrorCode AppInfoFilterAddBool(IntPtr handle, string property, bool value); + //int app_info_filter_add_bool(app_info_filter_h handle, const char *property, const bool value) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_filter_add_string")] + internal static extern ErrorCode AppInfoFilterAddString(IntPtr handle, string property, string value); + //int app_info_filter_add_string(app_info_filter_h handle, const char *property, const char *value) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_filter_count_appinfo")] + internal static extern ErrorCode AppInfoFilterCountAppinfo(IntPtr handle, out int count); + //int app_info_filter_count_appinfo(app_info_filter_h handle, int *count) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_filter_foreach_appinfo")] + internal static extern ErrorCode AppInfoFilterForeachAppinfo(IntPtr handle, AppInfoFilterCallback callback, IntPtr userData); + //int app_info_filter_foreach_appinfo(app_info_filter_h handle, app_info_filter_cb callback, void * user_data) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_metadata_filter_create")] + internal static extern ErrorCode AppInfoMetadataFilterCreate(out IntPtr handle); + //int app_info_metadata_filter_create (app_info_metadata_filter_h *handle) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_metadata_filter_destroy")] + internal static extern ErrorCode AppInfoMetadataFilterDestroy(IntPtr handle); + //int app_info_metadata_filter_destroy (app_info_metadata_filter_h handle) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_metadata_filter_add")] + internal static extern ErrorCode AppInfoMetadataFilterAdd(IntPtr handle, string key, string value); + //int app_info_metadata_filter_add (app_info_metadata_filter_h handle, const char *key, const char *value) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_metadata_filter_foreach")] + internal static extern ErrorCode AppInfoMetadataFilterForeach(IntPtr handle, AppInfoFilterCallback callback, IntPtr userData); + //int app_info_metadata_filter_foreach (app_info_metadata_filter_h handle, app_info_filter_cb callback, void *user_data) + + [DllImport(Libraries.AppManager, EntryPoint = "app_info_foreach_res_control")] + internal static extern ErrorCode AppInfoForeachResControl(IntPtr handle, AppInfoResControlCallback callback, IntPtr userData); + } +} diff --git a/src/Tizen.Applications.Team/Interop/Interop.BaseUtilsi18n.cs b/src/Tizen.Applications.Team/Interop/Interop.BaseUtilsi18n.cs new file mode 100644 index 00000000000..1bf2e6b9c32 --- /dev/null +++ b/src/Tizen.Applications.Team/Interop/Interop.BaseUtilsi18n.cs @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +internal static partial class Interop +{ + internal static partial class BaseUtilsi18n + { + [DllImport(Libraries.BaseUtilsi18n, EntryPoint = "i18n_ulocale_canonicalize")] + internal static extern Int32 Canonicalize(string localeID, [Out] StringBuilder name, Int32 nameCapacity); + // int32_t i18n_ulocale_canonicalize(const char *locale_id, char *name, int32_t name_capacity); + + [DllImport(Libraries.BaseUtilsi18n, EntryPoint = "i18n_ulocale_get_language")] + internal static extern int GetLanguage(string localeID, [Out] StringBuilder language, Int32 languageCapacity, out int bufSizeLanguage); + // int i18n_ulocale_get_language(const char *locale_id, char *language, int32_t language_capacity, int32_t *buf_size_language); + + [DllImport(Libraries.BaseUtilsi18n, EntryPoint = "i18n_ulocale_get_script")] + internal static extern Int32 GetScript(string localeID, [Out] StringBuilder script, Int32 scriptCapacity); + // int32_t i18n_ulocale_get_script(const char *locale_id, char *script, int32_t script_capacity); + + [DllImport(Libraries.BaseUtilsi18n, EntryPoint = "i18n_ulocale_get_country")] + internal static extern Int32 GetCountry(string localeID, [Out] StringBuilder country, Int32 countryCapacity, out int err); + // int32_t i18n_ulocale_get_country(const char *locale_id, char *country, int32_t country_capacity, int *error); + + [DllImport(Libraries.BaseUtilsi18n, EntryPoint = "i18n_ulocale_get_variant")] + internal static extern Int32 GetVariant(string localeID, [Out] StringBuilder variant, Int32 variantCapacity); + // int32_t i18n_ulocale_get_variant(const char *locale_id, char *variant, int32_t variant_capacity); + + [DllImport(Libraries.BaseUtilsi18n, EntryPoint = "i18n_ulocale_get_lcid")] + internal static extern UInt32 GetLCID(string localeID); + // uint32_t i18n_ulocale_get_lcid(const char *locale_id); + + [DllImport(Libraries.BaseUtilsi18n, EntryPoint = "i18n_ulocale_get_default")] + internal static extern int GetDefault(out IntPtr localeID); + // int i18n_ulocale_get_default(const char **locale); + } +} \ No newline at end of file diff --git a/src/Tizen.Applications.Team/Interop/Interop.Glib.cs b/src/Tizen.Applications.Team/Interop/Interop.Glib.cs new file mode 100644 index 00000000000..79e4cbc98cf --- /dev/null +++ b/src/Tizen.Applications.Team/Interop/Interop.Glib.cs @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Glib + { + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate bool GSourceFunc(IntPtr userData); + + [DllImport(Libraries.Glib, EntryPoint = "g_idle_source_new", CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr IdleSourceNew(); + + [DllImport(Libraries.Glib, EntryPoint = "g_source_set_callback", CallingConvention = CallingConvention.Cdecl)] + internal static extern void SourceSetCallback(IntPtr source, GSourceFunc func, IntPtr data, IntPtr notify); + + [DllImport(Libraries.Glib, EntryPoint = "g_source_attach", CallingConvention = CallingConvention.Cdecl)] + internal static extern uint SourceAttach(IntPtr source, IntPtr context); + + [DllImport(Libraries.Glib, EntryPoint = "g_source_unref", CallingConvention = CallingConvention.Cdecl)] + internal static extern void SourceUnref(IntPtr source); + } +} \ No newline at end of file diff --git a/src/Tizen.Applications.Team/Interop/Interop.IniParser.cs b/src/Tizen.Applications.Team/Interop/Interop.IniParser.cs new file mode 100644 index 00000000000..7173a2ee185 --- /dev/null +++ b/src/Tizen.Applications.Team/Interop/Interop.IniParser.cs @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class LibIniParser + { + [DllImport(Libraries.IniParser, EntryPoint = "iniparser_getstring", CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr GetString(IntPtr d, string key, IntPtr def); + + [DllImport(Libraries.IniParser, EntryPoint = "iniparser_load", CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr Load(string iniName); + + [DllImport(Libraries.IniParser, EntryPoint = "iniparser_freedict", CallingConvention = CallingConvention.Cdecl)] + internal static extern void FreeDict(IntPtr d); + } +} \ No newline at end of file diff --git a/src/Tizen.Applications.Team/Interop/Interop.Libc.cs b/src/Tizen.Applications.Team/Interop/Interop.Libc.cs new file mode 100644 index 00000000000..e7609f1f25d --- /dev/null +++ b/src/Tizen.Applications.Team/Interop/Interop.Libc.cs @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Libc + { + [DllImport(Libraries.Libc, EntryPoint = "getenv")] + internal static extern IntPtr GetEnvironmentVariable(string name); + } +} \ No newline at end of file diff --git a/src/Tizen.Applications.Team/Interop/Interop.Libraries.cs b/src/Tizen.Applications.Team/Interop/Interop.Libraries.cs new file mode 100755 index 00000000000..c049ed10861 --- /dev/null +++ b/src/Tizen.Applications.Team/Interop/Interop.Libraries.cs @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +internal static partial class Interop +{ + internal static partial class Libraries + { + public const string TeamLib = "libteam-application.so.1"; + public const string Glib = "libglib-2.0.so.0"; + public const string Libc = "libc.so.6"; + public const string BaseUtilsi18n = "libbase-utils-i18n.so.0"; + public const string IniParser = "libiniparser.so.1"; + public const string AppManager = "libcapi-appfw-app-manager.so.0"; + } +} diff --git a/src/Tizen.Applications.Team/Interop/Interop.TeamLoop.cs b/src/Tizen.Applications.Team/Interop/Interop.TeamLoop.cs new file mode 100644 index 00000000000..6a1119295a6 --- /dev/null +++ b/src/Tizen.Applications.Team/Interop/Interop.TeamLoop.cs @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.Runtime.InteropServices; +using Tizen.Internals; + +internal static partial class Interop +{ + internal static partial class TeamLoop + { + internal delegate IntPtr TeamLoopOpsLoad([MarshalAs(UnmanagedType.LPStr)] string path); + + internal delegate void TeamLoopOpsUnload(IntPtr loadObj); + + internal delegate IntPtr TeamLoopOpsCreateArgs(IntPtr loadObj); + + internal delegate void TeamLoopOpsCreateLibPath([MarshalAs(UnmanagedType.LPStr)] string path, ref IntPtr output); + + internal delegate void TeamLoopOpsOnLoopCreate(); + + internal delegate void TeamLoopOpsOnLoopTerminate(); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_main")] + internal static extern int Main(int argc, string[] argv, TeamLoopOperations ops); + + [NativeStruct("team_launcher_operation_s", Include="team_common.h", PkgConfig="team-application")] + [StructLayout(LayoutKind.Sequential)] + internal struct TeamLoopOperations + { + [MarshalAs(UnmanagedType.FunctionPtr)] + public TeamLoopOpsLoad Load; + + [MarshalAs(UnmanagedType.FunctionPtr)] + public TeamLoopOpsUnload Unload; + + [MarshalAs(UnmanagedType.FunctionPtr)] + public TeamLoopOpsCreateArgs CreateArgs; + + [MarshalAs(UnmanagedType.FunctionPtr)] + public TeamLoopOpsCreateLibPath CreateLibPath; + + [MarshalAs(UnmanagedType.FunctionPtr)] + public TeamLoopOpsOnLoopCreate OnLoopCreate; + + [MarshalAs(UnmanagedType.FunctionPtr)] + public TeamLoopOpsOnLoopTerminate OnLoopTerminate; + } + } +} \ No newline at end of file diff --git a/src/Tizen.Applications.Team/Interop/Interop.TeamManager.cs b/src/Tizen.Applications.Team/Interop/Interop.TeamManager.cs new file mode 100644 index 00000000000..a4663a1da20 --- /dev/null +++ b/src/Tizen.Applications.Team/Interop/Interop.TeamManager.cs @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.Runtime.InteropServices; +using Tizen.Internals; +using Tizen.Internals.Errors; + +internal static partial class Interop +{ + internal static partial class TeamManager + { + internal enum TeamAppErrorCode + { + None = ErrorCode.None, + InvalidParameter = ErrorCode.InvalidParameter, + OutOfMemory = ErrorCode.OutOfMemory, + InvalidContext = -0x01100000 | 0x01, + AlreadyExist = ErrorCode.FileExists, + AlreadyRunning = ErrorCode.AlreadyInProgress, + NoSuchMember = ErrorCode.NoSuchFile, + IOError = ErrorCode.IoError, + NotSupported = ErrorCode.NotSupported, + } + + [DllImport(Libraries.TeamLib, EntryPoint = "create_wl2_window_by_id")] + internal static extern IntPtr CreateWl2WindowById(int wl2WinId); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_data_path")] + internal static extern TeamAppErrorCode TeamAppGetDataPath(IntPtr memberHandle, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_cache_path")] + internal static extern TeamAppErrorCode TeamAppGetCachePath(IntPtr memberHandle, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_resource_path")] + internal static extern TeamAppErrorCode TeamAppGetResourcePath(IntPtr memberHandle, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_tep_resource_path")] + internal static extern TeamAppErrorCode TeamAppGetTepResourcePath(IntPtr memberHandle, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_shared_data_path")] + internal static extern TeamAppErrorCode TeamAppGetSharedDataPath(IntPtr memberHandle, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_shared_resource_path")] + internal static extern TeamAppErrorCode TeamAppGetSharedResourcePath(IntPtr memberHandle, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_shared_trusted_path")] + internal static extern TeamAppErrorCode TeamAppGetSharedTrustedPath(IntPtr memberHandle, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_res_control_allowed_path")] + internal static extern TeamAppErrorCode TeamAppGetResControlAllowedPath(IntPtr memberHandle, string resType, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_res_control_global_path")] + internal static extern TeamAppErrorCode TeamAppGetResControlGlobalPath(IntPtr memberHandle, string resType, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_common_data_path")] + internal static extern TeamAppErrorCode TeamAppGetCommonDataPath(IntPtr memberHandle, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_common_cache_path")] + internal static extern TeamAppErrorCode TeamAppGetCommonCachePath(IntPtr memberHandle, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_common_shared_data_path")] + internal static extern TeamAppErrorCode TeamAppGetCommonSharedDataPath(IntPtr memberHandle, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_common_shared_trusted_path")] + internal static extern TeamAppErrorCode TeamAppGetCommonSharedTrustedPath(IntPtr memberHandle, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_app_id")] + internal static extern TeamAppErrorCode TeamAppGetAppId(IntPtr memberHandle, out string appId); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_package_id")] + internal static extern TeamAppErrorCode TeamAppGetPackageId(IntPtr memberHandle, out string packageId); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_instance_id")] + internal static extern TeamAppErrorCode TeamAppGetInstanceId(IntPtr memberHandle, out string instId); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_app_instance_id")] + internal static extern TeamAppErrorCode TeamAppGetAppInstanceId(IntPtr memberHandle, out string appInstId); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_name")] + internal static extern TeamAppErrorCode TeamAppGetName(IntPtr memberHandle, out string name); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_version")] + internal static extern TeamAppErrorCode TeamAppGetVersion(IntPtr memberHandle, out string version); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_extern_data_path")] + internal static extern TeamAppErrorCode TeamAppGetExternDataPath(IntPtr memberHandle, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_extern_cache_path")] + internal static extern TeamAppErrorCode TeamAppGetExternCachePath(IntPtr memberHandle, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_app_get_extern_shared_data_path")] + internal static extern TeamAppErrorCode TeamAppGetExternSharedDataPath(IntPtr memberHandle, out string path); + + [DllImport(Libraries.TeamLib, EntryPoint = "invoke_view_visibility_event")] + internal static extern void InvokeViewVisibilityEvent(int viewId, bool visible); + + [DllImport(Libraries.TeamLib, EntryPoint = "create_view_by_view_id")] + internal static extern IntPtr CreateViewByViewId(int viewId); + + [DllImport(Libraries.TeamLib, EntryPoint = "destroy_view_by_view_id")] + internal static extern void DestroyViewByViewId(int viewId); + + } +} diff --git a/src/Tizen.Applications.Team/Interop/Interop.TeamMember.cs b/src/Tizen.Applications.Team/Interop/Interop.TeamMember.cs new file mode 100644 index 00000000000..d99752fa8ab --- /dev/null +++ b/src/Tizen.Applications.Team/Interop/Interop.TeamMember.cs @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.Runtime.InteropServices; +using Tizen.Internals; + +internal static partial class Interop +{ + internal static partial class TeamMember + { + internal delegate void AppTerminateCallback(IntPtr context, IntPtr userdata); + internal delegate void AppControlCallback(IntPtr context, IntPtr appControl, IntPtr userdata); + internal delegate void AppResumeCallback(IntPtr context, IntPtr userdata); + internal delegate void AppPauseCallback(IntPtr context, IntPtr userdata); + internal delegate void AppLowMemoryCallback(IntPtr context, int status, IntPtr userdata); + internal delegate void AppLowBatteryCallback(IntPtr context, int status, IntPtr userdata); + internal delegate void AppLanguageChangedCallback(IntPtr context, + [MarshalAs(UnmanagedType.LPStr)] string language, IntPtr userdata); + internal delegate void AppDeviceOrientationChangedCallback(IntPtr context, int status, IntPtr userdata); + internal delegate void AppRegionFormatChangedCallback(IntPtr context, + [MarshalAs(UnmanagedType.LPStr)] string region, IntPtr userdata); + internal delegate void AppSuspendStateChangedCallback(IntPtr context, int status, IntPtr userdata); + internal delegate void AppTimeZoneChangedCallback(IntPtr context, + [MarshalAs(UnmanagedType.LPStr)] string timeZone, + [MarshalAs(UnmanagedType.LPStr)] string timeZoneId, + IntPtr userdata); + internal delegate IntPtr UIAppCreateCallback(IntPtr context, IntPtr userdata); + internal delegate IntPtr ViewAppCreateCallback(IntPtr context, IntPtr userdata); + internal delegate bool ServiceAppCreateCallback(IntPtr context, IntPtr userdata); + [StructLayout(LayoutKind.Sequential)] + internal struct UIMemberLifecycleCallbacks + { + public UIAppCreateCallback Create; + public AppTerminateCallback Terminate; + public AppControlCallback Control; + public AppResumeCallback Resume; + public AppPauseCallback Pause; + public AppLowMemoryCallback LowMemory; + public AppLowBatteryCallback LowBattery; + public AppLanguageChangedCallback LanguageChanged; + public AppDeviceOrientationChangedCallback DeviceOrientationChanged; + public AppRegionFormatChangedCallback RegionFormatChanged; + public AppSuspendStateChangedCallback SuspendStateChanged; + public AppTimeZoneChangedCallback TimezoneChanged; + } + [StructLayout(LayoutKind.Sequential)] + internal struct ServiceMemberLifecycleCallbacks + { + public ServiceAppCreateCallback Create; + public AppTerminateCallback Terminate; + public AppControlCallback Control; + public AppLowMemoryCallback LowMemory; + public AppLowBatteryCallback LowBattery; + public AppLanguageChangedCallback LanguageChanged; + public AppDeviceOrientationChangedCallback DeviceOrientationChanged; + public AppRegionFormatChangedCallback RegionFormatChanged; + public AppSuspendStateChangedCallback SuspendStateChanged; + public AppTimeZoneChangedCallback TimezoneChanged; + } + + [DllImport(Libraries.TeamLib, EntryPoint = "team_ui_app_teamup")] + internal static extern IntPtr UIMemberTeamup(UIMemberLifecycleCallbacks callbacks, IntPtr userdata); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_ui_app_quit")] + internal static extern void UIMemberQuit(IntPtr member); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_service_app_teamup")] + internal static extern IntPtr ServiceMemberTeamup(ServiceMemberLifecycleCallbacks callbacks, IntPtr userdata); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_service_app_quit")] + internal static extern void ServiceMemberQuit(IntPtr member); + + [StructLayout(LayoutKind.Sequential)] + internal struct ViewMemberLifecycleCallbacks + { + public ViewAppCreateCallback Create; + public AppTerminateCallback Terminate; + public AppControlCallback Control; + public AppResumeCallback Resume; + public AppPauseCallback Pause; + public AppLowMemoryCallback LowMemory; + public AppLowBatteryCallback LowBattery; + public AppLanguageChangedCallback LanguageChanged; + public AppDeviceOrientationChangedCallback DeviceOrientationChanged; + public AppRegionFormatChangedCallback RegionFormatChanged; + public AppSuspendStateChangedCallback SuspendStateChanged; + public AppTimeZoneChangedCallback TimezoneChanged; + } + + [DllImport(Libraries.TeamLib, EntryPoint = "team_view_app_teamup")] + internal static extern IntPtr ViewMemberTeamup(ViewMemberLifecycleCallbacks callbacks, IntPtr userdata); + + [DllImport(Libraries.TeamLib, EntryPoint = "team_view_app_quit")] + internal static extern void ViewMemberQuit(IntPtr member); + } +} diff --git a/src/Tizen.Applications.Team/ResourceControl.cs b/src/Tizen.Applications.Team/ResourceControl.cs new file mode 100644 index 00000000000..070070d02a7 --- /dev/null +++ b/src/Tizen.Applications.Team/ResourceControl.cs @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace Tizen.Applications +{ + /// + /// Represents the resource control information for a Team application. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public class ResourceControl + { + internal ResourceControl(string resourceType, string minResourceVersion, string maxResourceVersion, bool isAutoClose) + { + ResourceType = resourceType; + MinResourceVersion = minResourceVersion ?? null; + MaxResourceVersion = maxResourceVersion ?? null; + IsAutoClose = isAutoClose; + } + + /// + /// Gets the resource type. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string ResourceType { get; } + + /// + /// Gets the minimum version of the required resource package. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string MinResourceVersion { get; } + + /// + /// Gets the maximum version of the required resource package. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string MaxResourceVersion { get; } + + /// + /// Gets a value indicating whether the resource is auto-closed. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsAutoClose { get; } + } +} diff --git a/src/Tizen.Applications.Team/SystemLocaleConverter.cs b/src/Tizen.Applications.Team/SystemLocaleConverter.cs new file mode 100644 index 00000000000..72d710622cc --- /dev/null +++ b/src/Tizen.Applications.Team/SystemLocaleConverter.cs @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Tizen.Applications +{ + /// + /// Converts Tizen system locale strings into values with ICU fallback. + /// + internal class SystemLocaleConverter + { + private static readonly string LogTag = "DN_TAM"; + public SystemLocaleConverter() + { + } + + public CultureInfo Convert(string locale) + { + ULocale pLocale = new ULocale(locale); + string cultureName = CultureInfoHelper.GetCultureName(pLocale.Locale.Replace("_", "-")); + + if (!string.IsNullOrEmpty(cultureName)) + { + try + { + return new CultureInfo(cultureName); + } + catch (CultureNotFoundException) + { + Log.Error(LogTag, "CultureNotFoundException occurs. CultureName: " + cultureName); + } + } + + try + { + return new CultureInfo(pLocale.LCID); + } + catch (ArgumentOutOfRangeException) + { + return GetFallback(pLocale); + } + catch (CultureNotFoundException) + { + return GetFallback(pLocale); + } + } + + public string Convert(CultureInfo cultureInfo) + { + if (cultureInfo == null || string.IsNullOrEmpty(cultureInfo.Name)) + { + return string.Empty; + } + return $"{cultureInfo.Name.Replace("-", "_")}.UTF-8"; + } + + private CultureInfo GetFallback(ULocale uLocale) + { + CultureInfo fallbackCultureInfo = null; + string locale = string.Empty; + + if (uLocale.Locale != null) + { + locale = uLocale.Locale.Replace("_", "-"); + fallbackCultureInfo = GetCultureInfo(locale); + } + + if (fallbackCultureInfo == null && uLocale.Language != null && uLocale.Script != null && uLocale.Country != null) + { + locale = uLocale.Language + "-" + uLocale.Script + "-" + uLocale.Country; + fallbackCultureInfo = GetCultureInfo(locale); + } + + if (fallbackCultureInfo == null && uLocale.Language != null && uLocale.Script != null) + { + locale = uLocale.Language + "-" + uLocale.Script; + fallbackCultureInfo = GetCultureInfo(locale); + } + + if (fallbackCultureInfo == null && uLocale.Language != null && uLocale.Country != null) + { + locale = uLocale.Language + "-" + uLocale.Country; + fallbackCultureInfo = GetCultureInfo(locale); + } + + if (fallbackCultureInfo == null && uLocale.Language != null) + { + locale = uLocale.Language; + fallbackCultureInfo = GetCultureInfo(locale); + } + + if (fallbackCultureInfo == null) + { + try + { + fallbackCultureInfo = new CultureInfo("en"); + } + catch (CultureNotFoundException e) + { + Log.Error(LogTag, "Failed to create CultureInfo. err = " + e.Message); + } + } + + return fallbackCultureInfo; + } + + private CultureInfo GetCultureInfo(string locale) + { + if (!Exist(locale)) + { + return null; + } + + try + { + return new CultureInfo(locale); + } + catch (CultureNotFoundException) + { + return null; + } + } + + private bool Exist(string locale) + { + foreach (var cultureInfo in CultureInfo.GetCultures(CultureTypes.AllCultures)) + { + if (cultureInfo.Name == locale) + { + return true; + } + } + return false; + } + + + /// + /// Wraps ICU uloc_* interop for parsing and canonicalizing locale strings. + /// + internal class ULocale + { + private const int ULOC_FULLNAME_CAPACITY = 157; + private const int ULOC_LANG_CAPACITY = 12; + private const int ULOC_SCRIPT_CAPACITY = 6; + private const int ULOC_COUNTRY_CAPACITY = 4; + private const int ULOC_VARIANT_CAPACITY = ULOC_FULLNAME_CAPACITY; + private string locale; + private string _locale; + private string _language; + private string _script; + private string _country; + private string _variant; + private int _lcid; + + internal ULocale(string locale) + { + this.locale = locale; + _locale = _language = _script = _country = _variant = null; + _lcid = -1; + } + + internal string Locale + { + get + { + if (string.IsNullOrEmpty(_locale)) + { + _locale = Canonicalize(locale); + } + return _locale; + } + private set + { + _locale = value; + } + } + internal string Language { + get + { + if (string.IsNullOrEmpty(_language)) + { + _language = GetLanguage(Locale); + } + return _language; + } + private set + { + _language = value; + } + } + internal string Script + { + get + { + if (string.IsNullOrEmpty(_script)) + { + _script = GetScript(Locale); + } + return _script; + } + private set + { + _script = value; + } + } + internal string Country { + get + { + if (string.IsNullOrEmpty(_country)) + { + _country = GetCountry(Locale); + } + return _country; + } + private set + { + _country = value; + } + } + internal string Variant + { + get + { + if (string.IsNullOrEmpty(_variant)) + { + _variant = GetVariant(Locale); + } + return _variant; + } + private set + { + _variant = value; + } + } + internal int LCID { + get + { + if (_lcid == -1) { + _lcid = GetLCID(Locale); + } + return _lcid; + } + private set { + _lcid = value; + } + } + + private string Canonicalize(string localeName) + { + // Get the locale name from ICU + StringBuilder sb = new StringBuilder(ULOC_FULLNAME_CAPACITY); + if (Interop.BaseUtilsi18n.Canonicalize(localeName, sb, sb.Capacity) <= 0) + { + return null; + } + + return sb.ToString(); + } + + private string GetLanguage(string locale) + { + // Get the language name from ICU + StringBuilder sb = new StringBuilder(ULOC_LANG_CAPACITY); + if (Interop.BaseUtilsi18n.GetLanguage(locale, sb, sb.Capacity, out int bufSizeLanguage) != 0) + { + return null; + } + + return sb.ToString(); + } + + private string GetScript(string locale) + { + // Get the script name from ICU + StringBuilder sb = new StringBuilder(ULOC_SCRIPT_CAPACITY); + if (Interop.BaseUtilsi18n.GetScript(locale, sb, sb.Capacity) <= 0) + { + return null; + } + + return sb.ToString(); + } + + private string GetCountry(string locale) + { + int err = 0; + + // Get the country name from ICU + StringBuilder sb = new StringBuilder(ULOC_COUNTRY_CAPACITY); + if (Interop.BaseUtilsi18n.GetCountry(locale, sb, sb.Capacity, out err) <= 0) + { + return null; + } + + return sb.ToString(); + } + + private string GetVariant(string locale) + { + // Get the variant name from ICU + StringBuilder sb = new StringBuilder(ULOC_VARIANT_CAPACITY); + if (Interop.BaseUtilsi18n.GetVariant(locale, sb, sb.Capacity) <= 0) + { + return null; + } + + return sb.ToString(); + } + + private int GetLCID(string locale) + { + // Get the LCID from ICU + uint lcid = Interop.BaseUtilsi18n.GetLCID(locale); + return (int)lcid; + } + + internal static string GetDefaultLocale() + { + IntPtr stringPtr = Interop.Libc.GetEnvironmentVariable("LANG"); + if (stringPtr == IntPtr.Zero) + { + return string.Empty; + } + + return Marshal.PtrToStringAnsi(stringPtr); + } + } + } +} \ No newline at end of file diff --git a/src/Tizen.Applications.Team/TeamApplication.cs b/src/Tizen.Applications.Team/TeamApplication.cs new file mode 100644 index 00000000000..90b251f63fc --- /dev/null +++ b/src/Tizen.Applications.Team/TeamApplication.cs @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.ComponentModel; +using Tizen.Applications.CoreBackend; + +namespace Tizen.Applications +{ + /// + /// Represents the abstract base class of a Team application instance. + /// + /// + /// A Team application is a member instance managed by the Team host process. Each instance + /// owns its own lifecycle, directory information, and application information. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public abstract class TeamApplication : IDisposable + { + internal const string LogTag = "DN_TAM"; + + private object _lock = new object(); + + private TeamDirectoryInfo _directoryInfo; + private TeamApplicationInfo _applicationInfo; + + /// + /// The backend that drives this Team application instance. + /// + protected readonly TeamCoreBackend _backend; + + /// + /// Gets the backend associated with this Team application instance. + /// + protected ICoreBackend Backend => _backend; + + /// + /// Initializes the class with the given backend. + /// + /// The backend used to drive this Team application instance. + /// Thrown when is null. + protected TeamApplication(TeamCoreBackend backend) + { + Log.Info(LogTag, "TeamApplication constructor called"); + _backend = backend ?? throw new ArgumentNullException(nameof(backend)); + } + + /// + /// Gets the directory information of the current Team application instance. + /// + /// A instance, or null if the member handle is not yet available. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public TeamDirectoryInfo DirectoryInfo + { + get + { + lock (_lock) + { + if (_directoryInfo == null) + { + Log.Info(LogTag, "DirectoryInfo getter called - creating new TeamDirectoryInfo"); + var memberHandle = _backend.MemberHandle; + if (memberHandle != IntPtr.Zero) + { + _directoryInfo = new TeamDirectoryInfo(memberHandle); + } + } + } + return _directoryInfo; + } + } + + /// + /// Gets the application information of the current Team application instance. + /// + /// A instance, or null if the member handle or application id cannot be resolved. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public TeamApplicationInfo ApplicationInfo + { + get + { + lock (_lock) + { + if (_applicationInfo == null) + { + Log.Info(LogTag, "ApplicationInfo getter called - creating new TeamApplicationInfo"); + var memberHandle = _backend.MemberHandle; + if (memberHandle != IntPtr.Zero) + { + var err = Interop.TeamManager.TeamAppGetAppId(memberHandle, out string appId); + if (err == 0 && !string.IsNullOrEmpty(appId)) + { + _applicationInfo = new TeamApplicationInfo(memberHandle, appId); + } + else + { + Log.Warn(LogTag, $"Failed to get AppId from memberHandle. err = {err}"); + } + } + else + { + Log.Warn(LogTag, "MemberHandle is zero, cannot get ApplicationInfo"); + } + } + } + return _applicationInfo; + } + } + + /// + /// Gets the current Team application instance. + /// + /// The current instance. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public TeamApplication Current { get { return this; } } + + /// + /// Gets the name of the current Team application instance. + /// + /// The name string, or if it cannot be retrieved. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string Name + { + get + { + var memberHandle = _backend.MemberHandle; + if (memberHandle == IntPtr.Zero) + { + Log.Warn(LogTag, "MemberHandle is zero, cannot get Name"); + return string.Empty; + } + + var err = Interop.TeamManager.TeamAppGetName(memberHandle, out string name); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + { + Log.Warn(LogTag, $"Failed to get Name. err = {err}"); + return string.Empty; + } + + return name ?? string.Empty; + } + } + + /// + /// Gets the version of the current Team application instance. + /// + /// The version string, or if it cannot be retrieved. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string Version + { + get + { + var memberHandle = _backend.MemberHandle; + if (memberHandle == IntPtr.Zero) + { + Log.Warn(LogTag, "MemberHandle is zero, cannot get Version"); + return string.Empty; + } + + var err = Interop.TeamManager.TeamAppGetVersion(memberHandle, out string version); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + { + Log.Warn(LogTag, $"Failed to get Version. err = {err}"); + return string.Empty; + } + + return version ?? string.Empty; + } + } + + /// + /// Runs the Team application's main loop and registers this instance with the Team manager. + /// + /// Arguments from commandline. + /// Thrown when is null. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void Run(string[] args) + { + if (args == null) + { + throw new ArgumentNullException(nameof(args)); + } + TeamManager.RegisterRunningTeamApp(this); + } + + /// + /// Exits the main loop of this Team application instance and unregisters it from the Team manager. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public virtual void Exit() + { + TeamManager.UnRegisterRunningTeamApp(this); + _backend.Exit(); + } + + private bool _disposedValue = false; + + /// + /// Releases the resources used by this Team application instance. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + if (_applicationInfo != null) + { + _applicationInfo.Dispose(); + } + Exit(); + } + + _disposedValue = true; + } + } + + /// + /// Finalizes the instance. + /// + ~TeamApplication() + { + Dispose(false); + } + + /// + /// Releases all resources used by this Team application instance. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public void Dispose() + { + Log.Info(LogTag, "TeamApplication.Dispose() called"); + Dispose(true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Tizen.Applications.Team/TeamApplicationInfo.cs b/src/Tizen.Applications.Team/TeamApplicationInfo.cs new file mode 100644 index 00000000000..9d704ff1ff5 --- /dev/null +++ b/src/Tizen.Applications.Team/TeamApplicationInfo.cs @@ -0,0 +1,599 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using Tizen.Internals; +using Tizen.Internals.Errors; + +namespace Tizen.Applications +{ + /// + /// Represents the installed application information of a Team application instance. + /// + /// + /// The information is obtained lazily from the native application manager and cached for subsequent access. + /// Call to release the underlying native handle. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public class TeamApplicationInfo : IDisposable + { + private const string LogTag = "DN_TAMS"; + private bool _disposed = false; + private IntPtr _infoHandle = IntPtr.Zero; + private IntPtr _memberHandle = IntPtr.Zero; + private string _applicationId = string.Empty; + private Interop.ApplicationManager.ErrorCode err = Interop.ApplicationManager.ErrorCode.None; + + // Cached path fields + private string _sharedDataPath = null; + private string _sharedResourcePath = null; + private string _sharedTrustedPath = null; + private string _externalSharedDataPath = null; + private string _commonSharedDataPath = null; + private string _commonSharedTrustedPath = null; + + internal TeamApplicationInfo(IntPtr memberHandle, string applicationId) + { + _memberHandle = memberHandle; + _applicationId = applicationId; + } + + /// + /// Finalizes the instance. + /// + ~TeamApplicationInfo() + { + Dispose(false); + } + + /// + /// Gets the application id. + /// + /// The application id string. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string ApplicationId + { + get + { + return _applicationId; + } + } + + /// + /// Gets the package id of the application. + /// + /// The package id string, or if it cannot be retrieved. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string PackageId + { + get + { + IntPtr infoHandle = GetInfoHandle(); + string packageid = string.Empty; + if (infoHandle != IntPtr.Zero) + { + err = Interop.ApplicationManager.AppInfoGetPackage(infoHandle, out packageid); + if (err != Interop.ApplicationManager.ErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the package id of " + _applicationId + ". err = " + err); + } + } + return packageid; + } + } + + /// + /// Gets the label of the application. + /// + /// The application label, or if it cannot be retrieved. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string Label + { + get + { + IntPtr infoHandle = GetInfoHandle(); + string label = string.Empty; + if (infoHandle != IntPtr.Zero) + { + err = Interop.ApplicationManager.AppInfoGetLabel(infoHandle, out label); + if (err != Interop.ApplicationManager.ErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the app label of " + _applicationId + ". err = " + err); + } + } + return label; + } + } + + /// + /// Gets the absolute path of the executable file of the application. + /// + /// The executable file path, or if it cannot be retrieved. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string ExecutablePath + { + get + { + IntPtr infoHandle = GetInfoHandle(); + string exec = string.Empty; + if (infoHandle != IntPtr.Zero) + { + err = Interop.ApplicationManager.AppInfoGetExec(infoHandle, out exec); + if (err != Interop.ApplicationManager.ErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the executable file path of " + _applicationId + ". err = " + err); + } + } + return exec; + } + } + + /// + /// Gets the absolute path of the icon image of the application. + /// + /// The icon image path, or if it cannot be retrieved. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string IconPath + { + get + { + IntPtr infoHandle = GetInfoHandle(); + string path = string.Empty; + if (infoHandle != IntPtr.Zero) + { + err = Interop.ApplicationManager.AppInfoGetIcon(infoHandle, out path); + if (err != Interop.ApplicationManager.ErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the app icon path of " + _applicationId + ". err = " + err); + } + } + return path; + } + } + + /// + /// Gets the application type. + /// + /// The application type string, or if it cannot be retrieved. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string ApplicationType + { + get + { + IntPtr infoHandle = GetInfoHandle(); + string type = string.Empty; + if (infoHandle != IntPtr.Zero) + { + err = Interop.ApplicationManager.AppInfoGetType(infoHandle, out type); + if (err != Interop.ApplicationManager.ErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the application type of " + _applicationId + ". err = " + err); + } + } + return type; + } + } + + /// + /// Gets the application component type. + /// + /// The of this application. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public ApplicationComponentType ComponentType + { + get + { + IntPtr infoHandle = GetInfoHandle(); + Interop.ApplicationManager.AppInfoAppComponentType componentType = 0; + if (infoHandle != IntPtr.Zero) + { + err = Interop.ApplicationManager.AppInfoGetAppComponentType(infoHandle, out componentType); + if (err != Interop.ApplicationManager.ErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the application component type of " + _applicationId + ". err = " + err); + } + } + return (ApplicationComponentType)componentType; + } + } + + /// + /// Gets the metadata key-value pairs declared by the application. + /// + /// A dictionary containing the metadata entries. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public IDictionary Metadata + { + get + { + IDictionary metadata = new Dictionary(); + + Interop.ApplicationManager.AppInfoMetadataCallback cb = (string key, string value, IntPtr userData) => + { + if (key.Length != 0) + { + if (!metadata.ContainsKey(key)) + metadata.Add(key, value); + } + return true; + }; + + IntPtr infoHandle = GetInfoHandle(); + if (infoHandle != IntPtr.Zero) + { + err = Interop.ApplicationManager.AppInfoForeachMetadata(infoHandle, cb, IntPtr.Zero); + if (err != Interop.ApplicationManager.ErrorCode.None) + { + Log.Warn(LogTag, "Failed to get application metadata of " + _applicationId + ". err = " + err); + } + } + + GC.KeepAlive(cb); + + return metadata; + } + } + + /// + /// Gets a value indicating whether the application is not displayed on the launcher. + /// + /// true if the application is hidden from the launcher; otherwise, false. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsNoDisplay + { + get + { + IntPtr infoHandle = GetInfoHandle(); + bool nodisplay = false; + if (infoHandle != IntPtr.Zero) + { + err = Interop.ApplicationManager.AppInfoIsNodisplay(infoHandle, out nodisplay); + if (err != Interop.ApplicationManager.ErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the IsNoDisplay value of " + _applicationId + ". err = " + err); + + } + } + return nodisplay; + } + } + + /// + /// Gets a value indicating whether the application is launched automatically on system boot. + /// + /// true if the application is launched on boot; otherwise, false. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsOnBoot + { + get + { + IntPtr infoHandle = GetInfoHandle(); + bool onboot = false; + if (infoHandle != IntPtr.Zero) + { + err = Interop.ApplicationManager.AppInfoIsOnBoot(infoHandle, out onboot); + if (err != Interop.ApplicationManager.ErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the IsOnBoot value of " + _applicationId + ". err = " + err); + } + } + return onboot; + } + } + + /// + /// Gets a value indicating whether the application is preloaded on the device. + /// + /// true if the application is preloaded; otherwise, false. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsPreload + { + get + { + IntPtr infoHandle = GetInfoHandle(); + bool preloaded = false; + if (infoHandle != IntPtr.Zero) + { + err = Interop.ApplicationManager.AppInfoIsPreLoad(infoHandle, out preloaded); + if (err != Interop.ApplicationManager.ErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the IsPreload value of " + _applicationId + ". err = " + err); + } + } + return preloaded; + } + } + + /// + /// Gets the categories the application belongs to. + /// + /// An enumerable collection of category names. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public IEnumerable Categories + { + get + { + List categories = new List(); + + Interop.ApplicationManager.AppInfoCategoryCallback cb = (string category, IntPtr userData) => + { + categories.Add(category); + return true; + }; + + IntPtr infoHandle = GetInfoHandle(); + if (infoHandle != IntPtr.Zero) + { + err = Interop.ApplicationManager.AppInfoForeachCategory(infoHandle, cb, IntPtr.Zero); + if (err != Interop.ApplicationManager.ErrorCode.None) + { + Log.Warn(LogTag, "Failed to get application category of " + _applicationId + ". err = " + err); + } + } + + GC.KeepAlive(cb); + + return categories; + } + } + + /// + /// Gets the absolute path of the directory shared with other applications for this Team member. + /// + /// The shared data directory path. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string SharedDataPath + { + get + { + if (_sharedDataPath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetSharedDataPath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the SharedDataPath of " + _applicationId + ". err = " + err); + } + _sharedDataPath = path; + } + return _sharedDataPath; + } + } + + /// + /// Gets the absolute path of the read-only resource directory shared with other applications for this Team member. + /// + /// The shared resource directory path. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string SharedResourcePath + { + get + { + if (_sharedResourcePath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetSharedResourcePath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the SharedResourcePath of " + _applicationId + ". err = " + err); + } + _sharedResourcePath = path; + } + return _sharedResourcePath; + } + } + + /// + /// Gets the absolute path of the directory shared only with trusted applications for this Team member. + /// + /// The shared trusted directory path. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string SharedTrustedPath + { + get + { + if (_sharedTrustedPath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetSharedTrustedPath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the SharedTrustedPath of " + _applicationId + ". err = " + err); + } + _sharedTrustedPath = path; + } + return _sharedTrustedPath; + } + } + + /// + /// Gets the absolute path of the shared directory on the external storage for this Team member. + /// + /// The external shared data directory path. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string ExternalSharedDataPath + { + get + { + if (_externalSharedDataPath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetExternSharedDataPath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the ExternalSharedDataPath of " + _applicationId + ". err = " + err); + } + _externalSharedDataPath = path; + } + return _externalSharedDataPath; + } + } + + /// + /// Gets the resource control entries declared by the application. + /// + /// An enumerable collection of entries. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public IEnumerable ResourceControls + { + get + { + List resourceControls = new List(); + Interop.ApplicationManager.AppInfoResControlCallback cb = (string resType, string minResourceVersion, string maxResourceVersion, string isAutoClose, IntPtr userData) => + { + resourceControls.Add(new ResourceControl(resType, minResourceVersion, maxResourceVersion, isAutoClose == "true")); + return true; + }; + + IntPtr infoHandle = GetInfoHandle(); + if (infoHandle != IntPtr.Zero) + { + err = Interop.ApplicationManager.AppInfoForeachResControl(infoHandle, cb, IntPtr.Zero); + if (err != Interop.ApplicationManager.ErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the resource controls of " + _applicationId + ". err = " + err); + } + } + + GC.KeepAlive(cb); + + return resourceControls; + } + } + + /// + /// Gets the localized label of the application for the given locale. + /// + /// The locale in the form of language and country code (for example, "en-US"). + /// The localized label; falls back to if no localized value is available. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string GetLocalizedLabel(string locale) + { + string label = string.Empty; + err = Interop.ApplicationManager.AppInfoGetLocaledLabel(ApplicationId, locale, out label); + if (err != Interop.ApplicationManager.ErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the GetLocalizedLabel of " + _applicationId + ". err = " + err); + label = Label; + } + return label; + } + + /// + /// Gets the absolute path of the common shared data directory for this Team member. + /// + /// The common shared data directory path. + [EditorBrowsable(EditorBrowsableState.Never)] + public string CommonSharedDataPath + { + get + { + if (_commonSharedDataPath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetCommonSharedDataPath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the CommonSharedDataPath of " + _applicationId + ". err = " + err); + } + _commonSharedDataPath = path; + } + return _commonSharedDataPath; + } + } + + /// + /// Gets the absolute path of the common shared trusted directory for this Team member. + /// + /// The common shared trusted directory path. + [EditorBrowsable(EditorBrowsableState.Never)] + public string CommonSharedTrustedPath + { + get + { + if (_commonSharedTrustedPath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetCommonSharedTrustedPath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the CommonSharedTrustedPath of " + _applicationId + ". err = " + err); + } + _commonSharedTrustedPath = path; + } + return _commonSharedTrustedPath; + } + } + + private IntPtr GetInfoHandle() + { + if (_infoHandle == IntPtr.Zero) + { + IntPtr infoHandle = IntPtr.Zero; + err = Interop.ApplicationManager.AppManagerGetAppInfo(_applicationId, out infoHandle); + if (err != Interop.ApplicationManager.ErrorCode.None) + { + Log.Warn(LogTag, "Failed to get the handle of the ApplicationInfo. err = " + err); + } + _infoHandle = infoHandle; + } + return _infoHandle; + } + + /// + /// Releases all resources used by this . + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + +#pragma warning disable CA1063 + private void Dispose(bool disposing) +#pragma warning restore CA1063 + { + if (_disposed) + return; + if (disposing) + { + } + if (_infoHandle != IntPtr.Zero) + { + Interop.ApplicationManager.AppInfoDestroy(_infoHandle); + _infoHandle = IntPtr.Zero; + } + _disposed = true; + } + } +} diff --git a/src/Tizen.Applications.Team/TeamCoreApplication.cs b/src/Tizen.Applications.Team/TeamCoreApplication.cs new file mode 100644 index 00000000000..c3c7460c8d0 --- /dev/null +++ b/src/Tizen.Applications.Team/TeamCoreApplication.cs @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Threading.Tasks; +using Tizen.Applications.CoreBackend; + +namespace Tizen.Applications +{ + /// + /// Represents the core Team application with common lifecycle and system events. + /// + /// + /// Subclasses typically add UI- or service-specific behavior on top of the events exposed here. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public class TeamCoreApplication : TeamApplication + { + /// + /// Initializes the class with the given backend. + /// + /// The backend used to drive this Team application instance. + /// Thrown when is null. + protected TeamCoreApplication(TeamCoreBackend backend) : base(backend) + { + Log.Info(LogTag, "TeamCoreApplication constructor called"); + } + + /// + /// Occurs whenever the application is launched. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler Created; + + /// + /// Occurs whenever the application is about to shut down. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler Terminated; + + /// + /// Occurs whenever the application receives an application control request. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler AppControlReceived; + + /// + /// Occurs when a low memory condition is reported by the system. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler LowMemory; + + /// + /// Occurs when a low battery condition is reported by the system. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler LowBattery; + + /// + /// Occurs when the system language is changed. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler LocaleChanged; + + /// + /// Occurs when the region format of the system is changed. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler RegionFormatChanged; + + /// + /// Occurs when the device orientation is changed. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler DeviceOrientationChanged; + + /// + /// Occurs when the system time zone is changed. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler TimeZoneChanged; + + /// + /// Runs the Team application's main loop and subscribes to lifecycle and system events. + /// + /// Arguments from commandline. + /// Thrown when is null. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public override void Run(string[] args) + { + base.Run(args); + + Backend.AddEventHandler(EventType.Created, OnCreate); + Backend.AddEventHandler(EventType.Terminated, OnTerminate); + Backend.AddEventHandler(EventType.AppControlReceived, OnAppControlReceived); + Backend.AddEventHandler(EventType.LowMemory, OnLowMemory); + Backend.AddEventHandler(EventType.LowBattery, OnLowBattery); + Backend.AddEventHandler(EventType.LocaleChanged, OnLocaleChanged); + Backend.AddEventHandler(EventType.RegionFormatChanged, OnRegionFormatChanged); + Backend.AddEventHandler(EventType.DeviceOrientationChanged, OnDeviceOrientationChanged); + Backend.AddEventHandler(EventType.TimeZoneChanged, OnTimeZoneChanged); + + _backend.Run(args); + } + + /// + /// Invoked when the application is created. Raises the event. + /// + protected virtual void OnCreate() + { + if (!GlobalizationMode.Invariant) + { + string locale = SystemLocaleConverter.ULocale.GetDefaultLocale(); + + TeamLocaleManager.SetCurrentUICultureInfo(locale); + TeamLocaleManager.SetCurrentCultureInfo(locale); + } + else + { + Log.Warn(LogTag, "Run in invariant mode"); + } + + Created?.Invoke(this, EventArgs.Empty); + } + + /// + /// Invoked when the application is about to terminate. Raises the event. + /// + protected virtual void OnTerminate() + { + Log.Info(LogTag, "TeamCoreApplication.OnTerminate() called"); + Terminated?.Invoke(this, EventArgs.Empty); + } + + /// + /// Invoked when the application receives an application control request. Raises the event. + /// + /// The event data containing the received application control. + protected virtual void OnAppControlReceived(AppControlReceivedEventArgs e) + { + Log.Info(LogTag, "TeamCoreApplication.OnAppControlReceived() called"); + if (e == null) + { + Log.Error(LogTag, "e is null"); + return; + } + + AppControlReceived?.Invoke(this, e); + } + + /// + /// Invoked when a low memory event is reported by the system. Raises the event + /// and triggers on soft/hard warnings. + /// + /// The event data describing the low memory status. + protected virtual void OnLowMemory(LowMemoryEventArgs e) + { + Log.Info(LogTag, $"TeamCoreApplication.OnLowMemory() called - Status: {e?.LowMemoryStatus}"); + if (e == null) + { + Log.Error(LogTag, "e is null"); + return; + } + + LowMemory?.Invoke(this, e); + if (e.LowMemoryStatus == LowMemoryStatus.SoftWarning || e.LowMemoryStatus == LowMemoryStatus.HardWarning) + { + GC.Collect(); + } + } + + /// + /// Invoked when a low battery event is reported by the system. Raises the event. + /// + /// The event data describing the low battery status. + protected virtual void OnLowBattery(LowBatteryEventArgs e) + { + Log.Info(LogTag, $"TeamCoreApplication.OnLowBattery() called - Status: {e?.LowBatteryStatus}"); + if (e == null) + { + Log.Error(LogTag, "e is null"); + return; + } + + LowBattery?.Invoke(this, e); + } + + /// + /// Invoked when the system language is changed. Updates the current UI culture and raises the event. + /// + /// The event data describing the new locale. + protected virtual void OnLocaleChanged(LocaleChangedEventArgs e) + { + Log.Info(LogTag, $"TeamCoreApplication.OnLocaleChanged() called - Locale: {e?.Locale}"); + if (e == null) + { + Log.Error(LogTag, "e is null"); + return; + } + + if (!GlobalizationMode.Invariant) + { + TeamLocaleManager.SetCurrentUICultureInfo(e.Locale); + } + + LocaleChanged?.Invoke(this, e); + } + + /// + /// Invoked when the region format is changed. Updates the current culture and raises the event. + /// + /// The event data describing the new region. + protected virtual void OnRegionFormatChanged(RegionFormatChangedEventArgs e) + { + Log.Info(LogTag, $"TeamCoreApplication.OnRegionFormatChanged() called - Region: {e?.Region}"); + if (e == null) + { + Log.Error(LogTag, "e is null"); + return; + } + + if (!GlobalizationMode.Invariant) + { + TeamLocaleManager.SetCurrentCultureInfo(e.Region); + } + + RegionFormatChanged?.Invoke(this, e); + } + + /// + /// Invoked when the device orientation is changed. Raises the event. + /// + /// The event data describing the new orientation. + protected virtual void OnDeviceOrientationChanged(DeviceOrientationEventArgs e) + { + Log.Info(LogTag, $"TeamCoreApplication.OnDeviceOrientationChanged() called - Orientation: {e?.DeviceOrientation}"); + DeviceOrientationChanged?.Invoke(this, e); + } + + /// + /// Invoked when the system time zone is changed. Clears cached culture data and raises the event. + /// + /// The event data describing the new time zone. + protected virtual void OnTimeZoneChanged(TimeZoneChangedEventArgs e) + { + Log.Info(LogTag, $"TeamCoreApplication.OnTimeZoneChanged() called - TimeZone: {e?.TimeZone}, ID: {e?.TimeZoneId}"); + CultureInfo.CurrentCulture.ClearCachedData(); + TimeZoneChanged?.Invoke(this, e); + } + + /// + /// Posts an action to be executed on the main loop of the current Team application. + /// + /// The action to be executed on the main loop. + /// Thrown when is null. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public static void Post(Action runner) + { + Log.Info(LogTag, "TeamCoreApplication.Post(Action) called"); + if (runner == null) + { + throw new ArgumentNullException(nameof(runner)); + } + + GSourceManager.Post(runner); + } + + /// + /// Posts a function to be executed on the main loop of the current Team application and awaits its result. + /// + /// The type of the result returned by . + /// The function to be executed on the main loop. + /// A task that completes with the result of . + /// Thrown when is null. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public static async Task Post(Func runner) + { + Log.Info(LogTag, $"TeamCoreApplication.Post() called - Type: {typeof(T).Name}"); + if (runner == null) + { + throw new ArgumentNullException(nameof(runner)); + } + + var task = new TaskCompletionSource(); + GSourceManager.Post(() => { task.SetResult(runner()); } ); + return await task.Task.ConfigureAwait(false); + } + } + + internal static class GlobalizationMode + { + private static int _invariant = -1; + + internal static bool Invariant + { + get + { + if (_invariant == -1) + { + string value = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT"); + _invariant = value != null ? (value.Equals("1") ? 1 : 0) : 0; + } + + return _invariant != 0; + } + } + } + + internal static class TeamLocaleManager + { + private static readonly string LogTag = "DN_TAM"; + + internal static void SetCurrentCultureInfo(string locale) + { + var converter = new SystemLocaleConverter(); + CultureInfo cultureInfo = converter.Convert(locale); + if (cultureInfo != null) + { + CultureInfo.CurrentCulture = cultureInfo; + } + else + { + Log.Error(LogTag, $"CultureInfo is null. locale: {locale}"); + } + } + + internal static void SetCurrentUICultureInfo(string locale) + { + var converter = new SystemLocaleConverter(); + CultureInfo cultureInfo = converter.Convert(locale); + if (cultureInfo != null) + { + CultureInfo.CurrentUICulture = cultureInfo; + } + else + { + Log.Error(LogTag, $"CultureInfo is null. locale: {locale}"); + } + } + } +} diff --git a/src/Tizen.Applications.Team/TeamCoreUiApplication.cs b/src/Tizen.Applications.Team/TeamCoreUiApplication.cs new file mode 100644 index 00000000000..bcd068b4879 --- /dev/null +++ b/src/Tizen.Applications.Team/TeamCoreUiApplication.cs @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + + +using System; +using System.ComponentModel; +using Tizen.Applications.CoreBackend; +using Tizen.NUI; + +namespace Tizen.Applications +{ + /// + /// Represents a base class for Team UI applications that own a NUI and expose + /// and lifecycle events. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public class TeamCoreUiApplication : TeamCoreApplication + { + /// + /// Initializes the class. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public TeamCoreUiApplication() : base(new TeamUICoreBackend()) + { + } + + /// + /// Gets the default window of this application. + /// + /// The default associated with this application. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public Window GetDefaultWindow() + { + return ((TeamUICoreBackend)Backend).GetDefaultWindow(); + } + + /// + /// Occurs whenever the application is resumed. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler Resumed; + + /// + /// Occurs whenever the application is paused. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler Paused; + + /// + /// Runs the Team UI application's main loop. + /// + /// Arguments from commandline. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public override void Run(string[] args) + { + Backend.AddEventHandler(EventType.Resumed, OnResume); + Backend.AddEventHandler(EventType.Paused, OnPause); + + base.Run(args); + } + + /// + /// Invoked when the application is created. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + protected override void OnCreate() + { + base.OnCreate(); + } + + /// + /// Invoked when the application is resumed. Raises the event. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void OnResume() + { + Resumed?.Invoke(this, EventArgs.Empty); + } + + /// + /// Invoked when the application is paused. Raises the event. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void OnPause() + { + Paused?.Invoke(this, EventArgs.Empty); + } + } +} diff --git a/src/Tizen.Applications.Team/TeamDirectoryInfo.cs b/src/Tizen.Applications.Team/TeamDirectoryInfo.cs new file mode 100644 index 00000000000..8f2541f0be9 --- /dev/null +++ b/src/Tizen.Applications.Team/TeamDirectoryInfo.cs @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.ComponentModel; +using System.IO; +using Tizen.Internals; +using Tizen.Internals.Errors; + +namespace Tizen.Applications +{ + /// + /// Represents the directory paths available to a Team application instance. + /// + /// + /// Each path is resolved lazily on first access and cached. Any failure in the native call is translated + /// into a .NET exception; see each property for the specific exceptions that can be thrown. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public class TeamDirectoryInfo + { + private IntPtr _memberHandle; + private string _dataPath; + private string _cachePath; + private string _resourcePath; + private string _sharedDataPath; + private string _sharedResourcePath; + private string _sharedTrustedPath; + private string _externalDataPath; + private string _externalCachePath; + private string _externalSharedDataPath; + private string _expansionPackageResourcePath; + private string _commonDataPath; + private string _commonCachePath; + private string _commonSharedDataPath; + private string _commonSharedTrustedPath; + + internal TeamDirectoryInfo(IntPtr memberHandle) + { + _memberHandle = memberHandle; + } + + /// + /// Gets the absolute path to the private data directory of this Team application. + /// + /// The private data directory path. + /// Thrown when the member handle is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string Data + { + get + { + if (_dataPath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetDataPath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError("Data", err); + _dataPath = path; + } + return _dataPath; + } + } + + /// + /// Gets the absolute path to the private cache directory of this Team application. + /// + /// The private cache directory path. + /// Thrown when the member handle is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string Cache + { + get + { + if (_cachePath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetCachePath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError("Cache", err); + _cachePath = path; + } + return _cachePath; + } + } + + /// + /// Gets the absolute path to the read-only resource directory of this Team application. + /// + /// The read-only resource directory path. + /// Thrown when the member handle is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string Resource + { + get + { + if (_resourcePath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetResourcePath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError("Resource", err); + _resourcePath = path; + } + return _resourcePath; + } + } + + /// + /// Gets the absolute path to the directory shared with other applications. + /// + /// The shared data directory path. + /// Thrown when the member handle is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string SharedData + { + get + { + if (_sharedDataPath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetSharedDataPath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError("SharedData", err); + _sharedDataPath = path; + } + return _sharedDataPath; + } + } + + /// + /// Gets the absolute path to the read-only resource directory shared with other applications. + /// + /// The shared resource directory path. + /// Thrown when the member handle is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string SharedResource + { + get + { + if (_sharedResourcePath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetSharedResourcePath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError("SharedResource", err); + _sharedResourcePath = path; + } + return _sharedResourcePath; + } + } + + /// + /// Gets the absolute path to the directory shared only with trusted applications. + /// + /// The shared trusted directory path. + /// Thrown when the member handle is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string SharedTrusted + { + get + { + if (_sharedTrustedPath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetSharedTrustedPath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError("SharedTrusted", err); + _sharedTrustedPath = path; + } + return _sharedTrustedPath; + } + } + + /// + /// Gets the absolute path to the private data directory on the external storage. + /// + /// The external data directory path. + /// Thrown when the member handle is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string ExternalData + { + get + { + if (_externalDataPath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetExternDataPath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError("ExternalData", err); + _externalDataPath = path; + } + return _externalDataPath; + } + } + + /// + /// Gets the absolute path to the private cache directory on the external storage. + /// + /// The external cache directory path. + /// Thrown when the member handle is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string ExternalCache + { + get + { + if (_externalCachePath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetExternCachePath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError("ExternalCache", err); + _externalCachePath = path; + } + return _externalCachePath; + } + } + + /// + /// Gets the absolute path to the shared directory on the external storage. + /// + /// The external shared data directory path. + /// Thrown when the member handle is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string ExternalSharedData + { + get + { + if (_externalSharedDataPath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetExternSharedDataPath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError("ExternalSharedData", err); + _externalSharedDataPath = path; + } + return _externalSharedDataPath; + } + } + + /// + /// Gets the absolute path to the resource directory of the Tizen Expansion Package. + /// + /// The expansion package resource directory path. + /// Thrown when the member handle is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string ExpansionPackageResource + { + get + { + if (_expansionPackageResourcePath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetTepResourcePath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError("ExpansionPackageResource", err); + _expansionPackageResourcePath = path; + } + return _expansionPackageResourcePath; + } + } + + /// + /// Gets the absolute path to the common data directory shared among users. + /// + /// The common data directory path. + /// Thrown when the member handle is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + [EditorBrowsable(EditorBrowsableState.Never)] + public string CommonData + { + get + { + if (_commonDataPath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetCommonDataPath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError("CommonData", err); + _commonDataPath = path; + } + return _commonDataPath; + } + } + + /// + /// Gets the absolute path to the common cache directory shared among users. + /// + /// The common cache directory path. + /// Thrown when the member handle is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + [EditorBrowsable(EditorBrowsableState.Never)] + public string CommonCache + { + get + { + if (_commonCachePath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetCommonCachePath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError("CommonCache", err); + _commonCachePath = path; + } + return _commonCachePath; + } + } + + /// + /// Gets the absolute path to the common shared data directory shared among users. + /// + /// The common shared data directory path. + /// Thrown when the member handle is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + [EditorBrowsable(EditorBrowsableState.Never)] + public string CommonSharedData + { + get + { + if (_commonSharedDataPath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetCommonSharedDataPath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError("CommonSharedData", err); + _commonSharedDataPath = path; + } + return _commonSharedDataPath; + } + } + + /// + /// Gets the absolute path to the common shared trusted directory shared among users. + /// + /// The common shared trusted directory path. + /// Thrown when the member handle is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + [EditorBrowsable(EditorBrowsableState.Never)] + public string CommonSharedTrusted + { + get + { + if (_commonSharedTrustedPath == null) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetCommonSharedTrustedPath(_memberHandle, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError("CommonSharedTrusted", err); + _commonSharedTrustedPath = path; + } + return _commonSharedTrustedPath; + } + } + + /// + /// Gets the absolute path to the resource directory allowed by resource control for the given resource type. + /// + /// The resource type. + /// The path to the allowed resource directory. + /// Thrown when the member handle or is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string GetResourceControlAllowedResource(string resType) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetResControlAllowedPath(_memberHandle, resType, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError($"GetResourceControlAllowedResource({resType})", err); + return path; + } + + /// + /// Gets the absolute path to the global resource directory by resource control for the given resource type. + /// + /// The resource type. + /// The path to the global resource directory. + /// Thrown when the member handle or is invalid. + /// Thrown when the system runs out of memory. + /// Thrown when the team member context is invalid or the path cannot be retrieved. + /// Thrown when the Team member is not found. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public string GetResourceControlGlobalResource(string resType) + { + Interop.TeamManager.TeamAppErrorCode err = Interop.TeamManager.TeamAppGetResControlGlobalPath(_memberHandle, resType, out string path); + if (err != Interop.TeamManager.TeamAppErrorCode.None) + throw GetExceptionFromError($"GetResourceControlGlobalResource({resType})", err); + return path; + } + + private Exception GetExceptionFromError(string operationName, Interop.TeamManager.TeamAppErrorCode err) + { + switch (err) + { + case Interop.TeamManager.TeamAppErrorCode.InvalidParameter: + return new ArgumentException($"Invalid Arguments for {operationName}"); + case Interop.TeamManager.TeamAppErrorCode.OutOfMemory: + return new OutOfMemoryException($"Out of memory for {operationName}"); + case Interop.TeamManager.TeamAppErrorCode.InvalidContext: + return new InvalidOperationException($"Invalid team member context for {operationName}"); + case Interop.TeamManager.TeamAppErrorCode.NoSuchMember: + return new DirectoryNotFoundException($"{operationName} not found"); + default: + return new InvalidOperationException($"Failed to get {operationName}"); + } + } + } +} diff --git a/src/Tizen.Applications.Team/TeamLoop.cs b/src/Tizen.Applications.Team/TeamLoop.cs new file mode 100644 index 00000000000..09b62f6d4c0 --- /dev/null +++ b/src/Tizen.Applications.Team/TeamLoop.cs @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + + +using System; +using System.ComponentModel; +using System.Threading; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Loader; +using System.Text; +using Tizen.NUI; + +namespace Tizen.Applications +{ + /// + /// Provides the entry point to the native Team main loop and the dynamic assembly load/unload callbacks. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public static class TeamLoop + { + private const string LogTag = "DN_TAM"; + private static Interop.TeamLoop.TeamLoopOperations _ops; + private static bool _isRunning = false; + private static string[] _systemArgs = null; + + static TeamLoop() + { + Log.Info(LogTag, "TeamLoop static constructor called"); + _ops.Load = new Interop.TeamLoop.TeamLoopOpsLoad(DoLoad); + _ops.Unload = new Interop.TeamLoop.TeamLoopOpsUnload(DoUnload); + _ops.CreateArgs = new Interop.TeamLoop.TeamLoopOpsCreateArgs(DoCreateArgs); + _ops.CreateLibPath = new Interop.TeamLoop.TeamLoopOpsCreateLibPath(DoCreateLibPath); + _ops.OnLoopCreate = new Interop.TeamLoop.TeamLoopOpsOnLoopCreate(DoOnLoopCreate); + _ops.OnLoopTerminate = new Interop.TeamLoop.TeamLoopOpsOnLoopTerminate(DoOnLoopTerminate); + } + /// + /// Starts the Team main loop. Subsequent calls while the loop is running are no-ops. + /// + /// Arguments passed to the native Team loop. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public static void Run(string[] args) + { + Log.Info(LogTag, $"Run() called - Already Running: {_isRunning}"); + if(_isRunning) + { + return; + } + + _isRunning = true; + _systemArgs = args; + + Log.Info("NUI", "[NUI] NUIApplicationInitializer: StaticInitialize"); + Registry.Instance.SavedApplicationThread = Thread.CurrentThread; + PropertyBridge.RegisterStringGetter(); + Log.Info("NUI", "[NUI] NUIApplicationInitializer: StaticInitialize done"); + + var err = Interop.TeamLoop.Main(args.Length, args, _ops); + if (err != 0) + { + Log.Error(LogTag, "Failed to Initialize TeamLoop"); + } + else + { + Log.Info(LogTag, "TeamLoop Run"); + } + } + + /// + /// Gets a value indicating whether the Team main loop is currently running. + /// + /// true if the main loop is running; otherwise, false. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public static bool IsRunning() + { + Log.Info(LogTag, $"IsRunning() called - Result: {_isRunning}"); + return _isRunning; + } + + internal static IntPtr DoLoad(string path) + { + if (string.IsNullOrEmpty(path)) + { + Log.Error(LogTag, "Invalid path: null or empty"); + return IntPtr.Zero; + } + + try + { + Log.Info(LogTag, $"Loading assembly: {path}"); + + TeamAssemblyLoadContext context = new TeamAssemblyLoadContext(); + WeakReference contextRef = new WeakReference(context); + + Assembly assembly = context.LoadFromAssemblyPath(path); + + var info = new AssemblyInfo(assembly, path, contextRef); + IntPtr id = TeamManager.RegisterAssemblyInfo(info); + + Log.Info(LogTag, $"Assembly loaded successfully: {path}, ID: {id}"); + return id; + } + catch (Exception e) + { + Log.Error(LogTag, $"Failed to load assembly: {path}, Error: {e.Message}"); + return IntPtr.Zero; + } + } + + internal static void DoUnload(IntPtr loadObj) + { + if (loadObj == IntPtr.Zero) + { + Log.Error(LogTag, "Invalid loadObj: IntPtr.Zero"); + return; + } + + var assemblyInfo = TeamManager.GetAssembly(loadObj); + if (assemblyInfo == null) + { + Log.Error(LogTag, $"Assembly not found for id: {loadObj}"); + return; + } + + Log.Info(LogTag, $"Unloading assembly - ID: {loadObj}, Path: {assemblyInfo.Path}"); + + if (assemblyInfo.LoadContextRef.IsAlive) + { + var context = assemblyInfo.LoadContextRef.Target as TeamAssemblyLoadContext; + context.Unload(); + Log.Info(LogTag, $"AssemblyLoadContext unloaded - ID: {loadObj}"); + } + else + { + Log.Warn(LogTag, $"AssemblyLoadContext already collected - ID: {loadObj}"); + } + + TeamManager.UnregisterAssembly(loadObj); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + Log.Info(LogTag, $"Assembly unloaded - ID: {loadObj}, Path: {assemblyInfo.Path}"); + } + + internal static IntPtr DoCreateArgs(IntPtr loadObj) + { + if (loadObj == IntPtr.Zero) + { + Log.Error(LogTag, "Invalid loadObj id: IntPtr.Zero"); + return IntPtr.Zero; + } + + var assemblyInfo = TeamManager.GetAssembly(loadObj); + if (assemblyInfo == null) + { + Log.Error(LogTag, $"Assembly not found for id: {loadObj}"); + return IntPtr.Zero; + } + + try + { + var mainMethod = assemblyInfo.Assembly.EntryPoint; + Log.Info(LogTag, $"Main method {mainMethod.Name}"); + + if (mainMethod == null) + { + Log.Error(LogTag, $"Entry point not found in assembly: {assemblyInfo.Path}"); + return IntPtr.Zero; + } + + string[] args = (string[])GetSystemArgs().Clone(); + args[0] = TeamManager.GetAssembly(loadObj).Path; + Log.Info(LogTag, $"args count: {args.Length}"); + for (int i = 0; i < args.Length; i++) + { + Log.Info(LogTag, $" args[{i}] = {args[i]}"); + } + + mainMethod.Invoke(null, new object[] { args }); + + var argHandle = TeamManager.GetArgHandle(loadObj); + Log.Info(LogTag, $"Main method invoked successfully - ID: {loadObj}, Result: {argHandle}"); + return argHandle; + } + catch (Exception e) + { + Log.Error(LogTag, $"Failed to invoke Main method - ID: {loadObj}, Error: {e.Message}"); + return IntPtr.Zero; + } + } + + internal static void DoCreateLibPath(string path, ref IntPtr output) + { + if (string.IsNullOrEmpty(path)) + { + Log.Error(LogTag, "Invalid path: null or empty"); + output = IntPtr.Zero; + return; + } + + int lastDotIndex = path.LastIndexOf('.'); + if (lastDotIndex < 0) + { + Log.Error(LogTag, $"Invalid path format: {path}. Expected format: org.appfw.csteam.{{member_id}}"); + output = IntPtr.Zero; + return; + } + + string memberId = path.Substring(lastDotIndex + 1); + if (string.IsNullOrEmpty(memberId)) + { + Log.Error(LogTag, $"Empty member_id extracted from: {path}"); + output = IntPtr.Zero; + return; + } + + string libPath = $"/usr/share/csteam/dll/{memberId}.dll"; + Log.Info(LogTag, $"Created lib path: {libPath} from {path}"); + + output = Marshal.StringToHGlobalAnsi(libPath); + + if (output == IntPtr.Zero) { + Log.Error(LogTag, "Failed to allocate memory for lib path"); + } + } + + /// + /// Gets the command line arguments that were passed to . + /// + /// The arguments array, or null if has not been invoked. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public static string[] GetSystemArgs() + { + Log.Info(LogTag, $"GetSystemArgs() called - Count: {_systemArgs?.Length ?? 0}"); + return _systemArgs; + } + + internal static void DoOnLoopCreate() + { + Log.Info(LogTag, "DoOnLoopCreate() called"); + + Log.Info("NUI", "[NUI] NUIApplicationInitializer: ProcessorController Initialize"); + Tracer.Begin("[NUI] NUIApplicationInitializer: ProcessorController Initialize"); + ProcessorController.Instance.Initialize(); + Tracer.End(); + + // Initialize DisposeQueue Singleton class. This is also required to create DisposeQueue on main thread. + Log.Info("NUI", "[NUI] NUIApplicationInitializer: DisposeQueue Initialize"); + Tracer.Begin("[NUI] NUIApplicationInitializer: DisposeQueue Initialize"); + DisposeQueue.Instance.Initialize(); + Tracer.End(); + + // Empty implementation for C# launcher + } + + internal static void DoOnLoopTerminate() + { + Log.Info(LogTag, "DoOnLoopTerminate() called"); + // Empty implementation for C# launcher + } + + } +} diff --git a/src/Tizen.Applications.Team/TeamManager.cs b/src/Tizen.Applications.Team/TeamManager.cs new file mode 100644 index 00000000000..76ae8d60082 --- /dev/null +++ b/src/Tizen.Applications.Team/TeamManager.cs @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; +using System.Runtime.Loader; +using Tizen.NUI.BaseComponents; + +namespace Tizen.Applications +{ + internal class TeamAssemblyLoadContext : AssemblyLoadContext + { + public TeamAssemblyLoadContext() : base(isCollectible: true) { } + + protected override Assembly Load(AssemblyName name) => null; + } + + internal class AssemblyInfo + { + public Assembly Assembly { get; } + public string Path { get; } + public WeakReference LoadContextRef { get; } + + public AssemblyInfo(Assembly assembly, string path, WeakReference contextRef) + { + Assembly = assembly; + Path = path; + LoadContextRef = contextRef; + } + } + + internal class ViewInfo + { + public View View { get; set; } + public string Appid { get; set; } + public bool Owned { get; set; } + } + + /// + /// Provides registration and management of Team application instances, loaded assemblies, and view ownership. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public static class TeamManager + { + private const string LogTag = "DN_TAM"; + private static readonly Dictionary _assemblies = new Dictionary(); + private static readonly Dictionary _assembliesByPath = new Dictionary(); + private static readonly Dictionary _argHandles = new Dictionary(); + private static readonly HashSet _runningApps = new HashSet(); + private static readonly object _lock = new object(); + private static int _assemblyId = 1; + + private static readonly Dictionary _viewInfos = new Dictionary(); + private static readonly Dictionary _viewByAppId = new Dictionary(); + private static int _viewIdCounter = 1; + + internal static IntPtr RegisterAssemblyInfo(AssemblyInfo info) + { + lock (_lock) + { + IntPtr id = new IntPtr(_assemblyId); + _assemblies[id] = info; + _assembliesByPath[info.Path] = id; + + Log.Info(LogTag, $"Assembly registered - ID: {_assemblyId}, Path: {info.Path}"); + + _assemblyId++; + return id; + } + } + + internal static void UnregisterAssembly(IntPtr id) + { + lock (_lock) + { + if (_assemblies.TryGetValue(id, out var info)) + { + _assembliesByPath.Remove(info.Path); + } + _assemblies.Remove(id); + } + } + + internal static AssemblyInfo GetAssembly(IntPtr id) + { + lock (_lock) + { + if (_assemblies.TryGetValue(id, out var info)) + { + return info; + } + } + return null; + } + + /// + /// Initializes the Team application runtime and starts the Team main loop. + /// + /// Arguments from commandline. May be null. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public static void Init(string[] args) + { + TeamLoop.Run(args); + } + + /// + /// Gets a value indicating whether the Team main loop has been initialized. + /// + /// true if the Team main loop is running; otherwise, false. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public static bool IsInit() + { + return TeamLoop.IsRunning(); + } + + internal static IntPtr GetAssemblyIdByPath(string path) + { + lock (_lock) + { + if (_assembliesByPath.TryGetValue(path, out var id)) + { + return id; + } + } + return IntPtr.Zero; + } + + internal static void RegisterArgHandle(IntPtr id, IntPtr argHandle) + { + lock (_lock) + { + _argHandles[id] = argHandle; + Log.Info(LogTag, $"ArgHandle registered - ID: {id}, ArgHandle: {argHandle}"); + } + } + + internal static void UnregisterArgHandle(IntPtr id) + { + lock (_lock) + { + _argHandles.Remove(id); + Log.Info(LogTag, $"ArgHandle unregistered - ID: {id}"); + } + } + + internal static IntPtr GetArgHandle(IntPtr id) + { + lock (_lock) + { + if (_argHandles.TryGetValue(id, out var argHandle)) + { + return argHandle; + } + } + return IntPtr.Zero; + } + + internal static void RegisterRunningTeamApp(TeamApplication app) + { + lock (_lock) + { + if (_runningApps.Add(app)) + { + Log.Info(LogTag, $"TeamApplication registered"); + } + } + } + + internal static void UnRegisterRunningTeamApp(TeamApplication app) + { + lock (_lock) + { + if (_runningApps.Remove(app)) + { + Log.Info(LogTag, $"TeamApplication unregistered"); + } + } + } + + /// + /// Registers a with the Team manager under the given application id. + /// + /// The view to register. + /// The application id that owns the view. + /// A unique identifier assigned to the registered view. + /// Thrown when or is null or empty. + /// Thrown when has already been registered. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public static int AddView(View view, string appid) + { + if (view == null) + throw new ArgumentNullException(nameof(view)); + if (string.IsNullOrEmpty(appid)) + throw new ArgumentNullException(nameof(appid)); + + lock (_lock) + { + if (_viewByAppId.ContainsKey(appid)) + { + throw new ArgumentException($"AppId '{appid}' already exists.", nameof(appid)); + } + + int id = _viewIdCounter++; + _viewInfos[id] = new ViewInfo { View = view, Appid = appid, Owned = false }; + _viewByAppId[appid] = id; + + Log.Info(LogTag, $"View added - ID: {id}, AppId: {appid}"); + return id; + } + } + + /// + /// Removes a view previously registered via . + /// + /// The unique identifier returned from . + /// true if the view was removed; false if no matching id was found. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public static bool RemoveView(int id) + { + lock (_lock) + { + if (_viewInfos.TryGetValue(id, out var info)) + { + _viewInfos.Remove(id); + _viewByAppId.Remove(info.Appid); + Log.Info(LogTag, $"View removed - ID: {id}, Appid: {info.Appid}"); + return true; + } + return false; + } + } + + /// + /// Claims ownership of the view registered under the given application id. + /// + /// The application id that owns the view. + /// The owned , or null if the view is already owned or not found. + /// Thrown when is null or empty. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public static View OwnViewApp(string appid) + { + if (string.IsNullOrEmpty(appid)) + throw new ArgumentNullException(nameof(appid)); + + lock (_lock) + { + if (_viewByAppId.TryGetValue(appid, out var id)) + { + if (_viewInfos.TryGetValue(id, out var info)) + { + if (info.Owned) + { + Log.Error(LogTag, $"View already owned - AppId: {appid}"); + return null; + } + info.Owned = true; + Log.Info(LogTag, $"View owned - AppId: {appid}"); + return info.View; + } + } + Log.Warn(LogTag, $"View not found for own - AppId: {appid}"); + return null; + } + } + + /// + /// Releases ownership of the view registered under the given application id. + /// + /// The application id that owns the view. + /// Thrown when is null or empty. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public static void DisownViewApp(string appid) + { + if (string.IsNullOrEmpty(appid)) + throw new ArgumentNullException(nameof(appid)); + + lock (_lock) + { + if (_viewByAppId.TryGetValue(appid, out var id)) + { + if (_viewInfos.TryGetValue(id, out var info)) + { + if (!info.Owned) + { + Log.Error(LogTag, $"View already disowned - AppId: {appid}"); + return; + } + info.Owned = false; + Log.Info(LogTag, $"View disowned - AppId: {appid}"); + } + } + else + { + Log.Warn(LogTag, $"View not found for disown - AppId: {appid}"); + } + } + } + + } +} diff --git a/src/Tizen.Applications.Team/TeamServiceApplication.cs b/src/Tizen.Applications.Team/TeamServiceApplication.cs new file mode 100644 index 00000000000..510a63170fb --- /dev/null +++ b/src/Tizen.Applications.Team/TeamServiceApplication.cs @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using Tizen.Applications.CoreBackend; + +namespace Tizen.Applications +{ + /// + /// Represents a Team application that runs without a graphical user interface. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public class TeamServiceApplication : TeamCoreApplication + { + /// + /// Initializes the class. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] +#pragma warning disable CA2000 + public TeamServiceApplication() : base(new TeamServiceCoreBackend()) +#pragma warning restore CA2000 + { + } + + /// + /// Runs the Team service application's main loop. + /// + /// Arguments from commandline. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public override void Run(string[] args) + { + base.Run(args); + } + } +} diff --git a/src/Tizen.Applications.Team/TeamUiApplication.cs b/src/Tizen.Applications.Team/TeamUiApplication.cs new file mode 100644 index 00000000000..4ae9daaaad1 --- /dev/null +++ b/src/Tizen.Applications.Team/TeamUiApplication.cs @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.ComponentModel; +using Tizen.NUI; + +namespace Tizen.Applications +{ + /// + /// Represents a Team application that provides a default NUI window. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public class TeamUiApplication : TeamCoreUiApplication + { + /// + /// Initializes the class. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public TeamUiApplication() + { + } + + /// + /// Runs the Team UI application's main loop. + /// + /// Arguments from commandline. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public override void Run(string[] args) + { + base.Run(args); + } + } +} diff --git a/src/Tizen.Applications.Team/TeamViewApplication.cs b/src/Tizen.Applications.Team/TeamViewApplication.cs new file mode 100644 index 00000000000..b61d0096015 --- /dev/null +++ b/src/Tizen.Applications.Team/TeamViewApplication.cs @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + + +using System; +using System.ComponentModel; +using Tizen.Applications.CoreBackend; +using Tizen.NUI; +using Tizen.NUI.BaseComponents; + +namespace Tizen.Applications +{ + /// + /// Represents a Team application that provides a default NUI . + /// + /// + /// A view application renders into a shared window supplied by the Team host rather than owning its own window. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public class TeamViewApplication : TeamCoreApplication + { + /// + /// Initializes the class. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public TeamViewApplication() : base(new TeamViewCoreBackend()) + { + } + + /// + /// Gets the default view attached to this application. + /// + /// The default associated with this application. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public View GetDefaultView() + { + return ((TeamViewCoreBackend)Backend).GetDefaultView(); + } + + /// + /// Occurs whenever the application is resumed. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler Resumed; + + /// + /// Occurs whenever the application is paused. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public event EventHandler Paused; + + /// + /// Runs the Team view application's main loop. + /// + /// Arguments from commandline. + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public override void Run(string[] args) + { + Backend.AddEventHandler(EventType.Resumed, OnResume); + Backend.AddEventHandler(EventType.Paused, OnPause); + + base.Run(args); + } + + /// + /// Invoked when the application is resumed. Raises the event. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void OnResume() + { + Resumed?.Invoke(this, EventArgs.Empty); + } + + /// + /// Invoked when the application is paused. Raises the event. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual void OnPause() + { + Paused?.Invoke(this, EventArgs.Empty); + } + } +} diff --git a/src/Tizen.Applications.Team/Tizen.Applications.CoreBackend/TeamCoreBackend.cs b/src/Tizen.Applications.Team/Tizen.Applications.CoreBackend/TeamCoreBackend.cs new file mode 100644 index 00000000000..473bad48ea3 --- /dev/null +++ b/src/Tizen.Applications.Team/Tizen.Applications.CoreBackend/TeamCoreBackend.cs @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using System.ComponentModel; + +namespace Tizen.Applications.CoreBackend +{ + /// + /// Represents the abstract base backend that drives a Team application instance. + /// + /// This will be public opened in next tizen after ACR done. (Before ACR, need to be hidden as inhouse API) + [EditorBrowsable(EditorBrowsableState.Never)] + public abstract class TeamCoreBackend : DefaultCoreBackend + { + /// + /// The native handle that identifies this Team member instance. + /// + protected IntPtr _memberHandle = IntPtr.Zero; + + /// + /// The identifier of the loaded assembly associated with this Team member instance. + /// + protected IntPtr _loadObjId = IntPtr.Zero; + + /// + /// The native handle to the arguments passed to this Team member instance. + /// + protected IntPtr _argHandle = IntPtr.Zero; + + internal abstract IntPtr MemberHandle { get; } + internal abstract IntPtr LoadObjId { get; } + internal abstract IntPtr ArgHandle { get; } + } +} diff --git a/src/Tizen.Applications.Team/Tizen.Applications.CoreBackend/TeamServiceCoreBackend.cs b/src/Tizen.Applications.Team/Tizen.Applications.CoreBackend/TeamServiceCoreBackend.cs new file mode 100644 index 00000000000..4f688e29708 --- /dev/null +++ b/src/Tizen.Applications.Team/Tizen.Applications.CoreBackend/TeamServiceCoreBackend.cs @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using Tizen.Internals; + +namespace Tizen.Applications.CoreBackend +{ + /// + /// Backend implementation for Team service applications that run without a graphical UI. + /// + internal class TeamServiceCoreBackend : TeamCoreBackend + { + internal new static string LogTag = "DN_TAM"; + private Interop.TeamMember.ServiceMemberLifecycleCallbacks _callbacks; + private bool _disposedValue = false; + internal override IntPtr MemberHandle => _memberHandle; + internal override IntPtr LoadObjId => _loadObjId; + internal override IntPtr ArgHandle => _argHandle; + + private Interop.TeamMember.ServiceAppCreateCallback _onCreateNative; + private Interop.TeamMember.AppTerminateCallback _onTerminateNative; + private Interop.TeamMember.AppControlCallback _onAppControlNative; + private Interop.TeamMember.AppLowMemoryCallback _onLowMemoryNative; + private Interop.TeamMember.AppLowBatteryCallback _onLowBatteryNative; + private Interop.TeamMember.AppLanguageChangedCallback _onLanguageChangedNative; + private Interop.TeamMember.AppDeviceOrientationChangedCallback _onDeviceOrientationChangedNative; + private Interop.TeamMember.AppRegionFormatChangedCallback _onRegionFormatChangedNative; + private Interop.TeamMember.AppSuspendStateChangedCallback _onSuspendStateChangedNative; + private Interop.TeamMember.AppTimeZoneChangedCallback _onTimeZoneChangedNative; + + public TeamServiceCoreBackend() + { + _onCreateNative = new Interop.TeamMember.ServiceAppCreateCallback(OnCreateNative); + _onTerminateNative = new Interop.TeamMember.AppTerminateCallback(OnTerminateNative); + _onAppControlNative = new Interop.TeamMember.AppControlCallback(OnAppControlNative); + _onLowMemoryNative = new Interop.TeamMember.AppLowMemoryCallback(OnLowMemoryNative); + _onLowBatteryNative = new Interop.TeamMember.AppLowBatteryCallback(OnLowBatteryNative); + _onLanguageChangedNative = new Interop.TeamMember.AppLanguageChangedCallback(OnLanguageChangedNative); + _onDeviceOrientationChangedNative = new Interop.TeamMember.AppDeviceOrientationChangedCallback(OnDeviceOrientationChangedNative); + _onRegionFormatChangedNative = new Interop.TeamMember.AppRegionFormatChangedCallback(OnRegionFormatChangedNative); + _onSuspendStateChangedNative = new Interop.TeamMember.AppSuspendStateChangedCallback(OnSuspendStateChangedNative); + _onTimeZoneChangedNative = new Interop.TeamMember.AppTimeZoneChangedCallback(OnTimeZoneChangedNative); + + _callbacks.Create = _onCreateNative; + _callbacks.Terminate = _onTerminateNative; + _callbacks.Control = _onAppControlNative; + _callbacks.LowMemory = _onLowMemoryNative; + _callbacks.LowBattery = _onLowBatteryNative; + _callbacks.LanguageChanged = _onLanguageChangedNative; + _callbacks.DeviceOrientationChanged = _onDeviceOrientationChangedNative; + _callbacks.RegionFormatChanged = _onRegionFormatChangedNative; + _callbacks.SuspendStateChanged = _onSuspendStateChangedNative; + _callbacks.TimezoneChanged = _onTimeZoneChangedNative; + } + + public override void Exit() + { + if (_memberHandle != IntPtr.Zero) + { + Interop.TeamMember.ServiceMemberQuit(_memberHandle); + _memberHandle = IntPtr.Zero; + } + } + + public override void Run(string[] args) + { + // base.Run() is not required. + if (!TeamManager.IsInit()) + { + string[] argsClone = new string[args == null ? 1 : args.Length + 1]; + if (args != null && args.Length > 1) + { + args.CopyTo(argsClone, 1); + } + argsClone[0] = "Tizen.Applications.Team.dll"; + + TeamManager.Init(argsClone); + Log.Info("DN_TAM", $"Launching Team Loop."); + return; + } + + if (args == null || args.Length == 0) + { + Log.Error(LogTag, "args is null or empty"); + return; + } + + _loadObjId = TeamManager.GetAssemblyIdByPath(args[0]); + _argHandle = Interop.TeamMember.ServiceMemberTeamup(_callbacks, IntPtr.Zero); + TeamManager.RegisterArgHandle(_loadObjId, _argHandle); + Log.Info(LogTag, $"path: {args[0]}, id: {_loadObjId}, arg_h: {_argHandle}"); + } + + protected override void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + // Release disposable objects + } + + if (_memberHandle != IntPtr.Zero) + { + Interop.TeamMember.ServiceMemberQuit(_memberHandle); + _memberHandle = IntPtr.Zero; + } + + _disposedValue = true; + } + } + + private bool OnCreateNative(IntPtr context, IntPtr userdata) + { + if (_memberHandle != IntPtr.Zero) + { + Log.Warn(LogTag, "OnCreate called twice!"); + } + _memberHandle = context; + + if (Handlers.ContainsKey(EventType.Created)) + { + var handler = Handlers[EventType.Created] as Action; + if (handler != null) + { + try + { + handler.Invoke(); + Log.Warn(LogTag, $"Return True"); + return true; + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in Created handler: {ex.Message}"); + return false; + } + } + } + + Log.Error(LogTag, "No OnCreate Callback"); + return false; + } + + private void OnTerminateNative(IntPtr context, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.Terminated)) + { + var handler = Handlers[EventType.Terminated] as Action; + try + { + handler?.Invoke(); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in Terminated handler: {ex.Message}"); + } + } + } + + private void OnAppControlNative(IntPtr context, IntPtr appControl, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.AppControlReceived)) + { + using (SafeAppControlHandle safeHandle = new SafeAppControlHandle(appControl, false)) + { + var handler = Handlers[EventType.AppControlReceived] as Action; + try + { + handler?.Invoke(new AppControlReceivedEventArgs(new ReceivedAppControl(safeHandle))); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in AppControlReceived handler: {ex.Message}"); + } + } + } + } + + private void OnLowMemoryNative(IntPtr context, int status, IntPtr userdata) + { + LowMemoryStatus lowMemoryStatus = (LowMemoryStatus)status; + if (Handlers.ContainsKey(EventType.LowMemory)) + { + var handler = Handlers[EventType.LowMemory] as Action; + try + { + handler?.Invoke(new LowMemoryEventArgs(lowMemoryStatus)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in LowMemory handler: {ex.Message}"); + } + } + } + + private void OnLowBatteryNative(IntPtr context, int status, IntPtr userdata) + { + LowBatteryStatus lowBatteryStatus = (LowBatteryStatus)status; + if (Handlers.ContainsKey(EventType.LowBattery)) + { + var handler = Handlers[EventType.LowBattery] as Action; + try + { + handler?.Invoke(new LowBatteryEventArgs(lowBatteryStatus)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in LowBattery handler: {ex.Message}"); + } + } + } + + private void OnLanguageChangedNative(IntPtr context, string language, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.LocaleChanged)) + { + var handler = Handlers[EventType.LocaleChanged] as Action; + try + { + handler?.Invoke(new LocaleChangedEventArgs(language)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in LocaleChanged handler: {ex.Message}"); + } + } + } + + private void OnDeviceOrientationChangedNative(IntPtr context, int status, IntPtr userdata) + { + DeviceOrientation orientation = (DeviceOrientation)status; + if (Handlers.ContainsKey(EventType.DeviceOrientationChanged)) + { + var handler = Handlers[EventType.DeviceOrientationChanged] as Action; + try + { + handler?.Invoke(new DeviceOrientationEventArgs(orientation)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in DeviceOrientationChanged handler: {ex.Message}"); + } + } + } + + private void OnRegionFormatChangedNative(IntPtr context, string region, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.RegionFormatChanged)) + { + var handler = Handlers[EventType.RegionFormatChanged] as Action; + try + { + handler?.Invoke(new RegionFormatChangedEventArgs(region)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in RegionFormatChanged handler: {ex.Message}"); + } + } + } + + private void OnSuspendStateChangedNative(IntPtr context, int status, IntPtr userdata) + { + SuspendedState state = (SuspendedState)status; + if (Handlers.ContainsKey(EventType.SuspendedStateChanged)) + { + var handler = Handlers[EventType.SuspendedStateChanged] as Action; + try + { + handler?.Invoke(new SuspendedStateEventArgs(state)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in SuspendedStateChanged handler: {ex.Message}"); + } + } + } + + private void OnTimeZoneChangedNative(IntPtr context, string timeZone, string timeZoneId, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.TimeZoneChanged)) + { + var handler = Handlers[EventType.TimeZoneChanged] as Action; + try + { + handler?.Invoke(new TimeZoneChangedEventArgs(timeZone, timeZoneId)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in TimeZoneChanged handler: {ex.Message}"); + } + } + } + } +} \ No newline at end of file diff --git a/src/Tizen.Applications.Team/Tizen.Applications.CoreBackend/TeamUICoreBackend.cs b/src/Tizen.Applications.Team/Tizen.Applications.CoreBackend/TeamUICoreBackend.cs new file mode 100644 index 00000000000..45e06f4f7d7 --- /dev/null +++ b/src/Tizen.Applications.Team/Tizen.Applications.CoreBackend/TeamUICoreBackend.cs @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using Tizen.Internals; +using Tizen.NUI; + +namespace Tizen.Applications.CoreBackend +{ + /// + /// Backend implementation for Team UI applications that own a default . + /// + internal class TeamUICoreBackend : TeamCoreBackend + { + internal new static string LogTag = "DN_TAM"; + private Interop.TeamMember.UIMemberLifecycleCallbacks _callbacks; + private bool _disposedValue = false; + private int DefaultWindowId = 0; + internal Window defaultWindow; + internal override IntPtr MemberHandle => _memberHandle; + internal override IntPtr LoadObjId => _loadObjId; + internal override IntPtr ArgHandle => _argHandle; + + private Interop.TeamMember.UIAppCreateCallback _onCreateNative; + private Interop.TeamMember.AppTerminateCallback _onTerminateNative; + private Interop.TeamMember.AppControlCallback _onAppControlNative; + private Interop.TeamMember.AppResumeCallback _onResumeNative; + private Interop.TeamMember.AppPauseCallback _onPauseNative; + private Interop.TeamMember.AppLowMemoryCallback _onLowMemoryNative; + private Interop.TeamMember.AppLowBatteryCallback _onLowBatteryNative; + private Interop.TeamMember.AppLanguageChangedCallback _onLanguageChangedNative; + private Interop.TeamMember.AppDeviceOrientationChangedCallback _onDeviceOrientationChangedNative; + private Interop.TeamMember.AppRegionFormatChangedCallback _onRegionFormatChangedNative; + private Interop.TeamMember.AppSuspendStateChangedCallback _onSuspendStateChangedNative; + private Interop.TeamMember.AppTimeZoneChangedCallback _onTimeZoneChangedNative; + + public TeamUICoreBackend() + { + _onCreateNative = new Interop.TeamMember.UIAppCreateCallback(OnCreateNative); + _onTerminateNative = new Interop.TeamMember.AppTerminateCallback(OnTerminateNative); + _onAppControlNative = new Interop.TeamMember.AppControlCallback(OnAppControlNative); + _onResumeNative = new Interop.TeamMember.AppResumeCallback(OnResumeNative); + _onPauseNative = new Interop.TeamMember.AppPauseCallback(OnPauseNative); + _onLowMemoryNative = new Interop.TeamMember.AppLowMemoryCallback(OnLowMemoryNative); + _onLowBatteryNative = new Interop.TeamMember.AppLowBatteryCallback(OnLowBatteryNative); + _onLanguageChangedNative = new Interop.TeamMember.AppLanguageChangedCallback(OnLanguageChangedNative); + _onDeviceOrientationChangedNative = new Interop.TeamMember.AppDeviceOrientationChangedCallback(OnDeviceOrientationChangedNative); + _onRegionFormatChangedNative = new Interop.TeamMember.AppRegionFormatChangedCallback(OnRegionFormatChangedNative); + _onSuspendStateChangedNative = new Interop.TeamMember.AppSuspendStateChangedCallback(OnSuspendStateChangedNative); + _onTimeZoneChangedNative = new Interop.TeamMember.AppTimeZoneChangedCallback(OnTimeZoneChangedNative); + + _callbacks.Create = _onCreateNative; + _callbacks.Terminate = _onTerminateNative; + _callbacks.Control = _onAppControlNative; + _callbacks.Resume = _onResumeNative; + _callbacks.Pause = _onPauseNative; + _callbacks.LowMemory = _onLowMemoryNative; + _callbacks.LowBattery = _onLowBatteryNative; + _callbacks.LanguageChanged = _onLanguageChangedNative; + _callbacks.DeviceOrientationChanged = _onDeviceOrientationChangedNative; + _callbacks.RegionFormatChanged = _onRegionFormatChangedNative; + _callbacks.SuspendStateChanged = _onSuspendStateChangedNative; + _callbacks.TimezoneChanged = _onTimeZoneChangedNative; + } + + internal int GetDefaultWindowId() + { + return DefaultWindowId; + } + internal void SetDefaultWindowId(int windowId) + { + Log.Error(LogTag, $"SetDefaultWindowId: {windowId}"); + DefaultWindowId = windowId; + } + public Window GetDefaultWindow() + { + return defaultWindow; + } + internal void SetDefaultWindow(Window window) + { + defaultWindow = window; + SetDefaultWindowId(window.GetNativeId()); + } + public override void Exit() + { + if (_memberHandle != IntPtr.Zero) + { + Interop.TeamMember.UIMemberQuit(_memberHandle); + _memberHandle = IntPtr.Zero; + } + } + + public override void Run(string[] args) + { + // base.Run() is not required. + if (!TeamManager.IsInit()) + { + string[] argsClone = new string[args == null ? 1 : args.Length + 1]; + if (args != null && args.Length > 1) + { + args.CopyTo(argsClone, 1); + } + argsClone[0] = "Tizen.Applications.Team.dll"; + + TeamManager.Init(argsClone); + Log.Info("DN_TAM", $"Launching Team Loop."); + return; + } + + if (args == null || args.Length == 0) + { + Log.Error(LogTag, "args is null or empty"); + return; + } + + _loadObjId = TeamManager.GetAssemblyIdByPath(args[0]); + _argHandle = Interop.TeamMember.UIMemberTeamup(_callbacks, IntPtr.Zero); + TeamManager.RegisterArgHandle(_loadObjId, _argHandle); + Log.Info(LogTag, $"path: {args[0]}, id: {_loadObjId}, arg_h: {_argHandle}"); + } + + protected override void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + // Release disposable objects + } + + if (_memberHandle != IntPtr.Zero) + { + Interop.TeamMember.UIMemberQuit(_memberHandle); + _memberHandle = IntPtr.Zero; + } + + _disposedValue = true; + } + } + + private IntPtr OnCreateNative(IntPtr context, IntPtr userdata) + { + if (_memberHandle != IntPtr.Zero) + { + Log.Warn(LogTag, "OnCreate called twice!"); + } + _memberHandle = context; + + if (Handlers.ContainsKey(EventType.Created)) + { + var handler = Handlers[EventType.Created] as Action; + if (handler != null) + { + // This function will set default window + try + { + var window = new Window(); + SetDefaultWindow(window); + window.Hide(); + + try { + handler?.Invoke(); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in User Created handler: {ex.Message}"); + } + + IntPtr window_h = Interop.TeamManager.CreateWl2WindowById(GetDefaultWindowId()); + return window_h; + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in Internal Created handler: {ex.Message}"); + return IntPtr.Zero; + } + } + else + { + Log.Error(LogTag, "Invalid OnCreate Callback type"); + return IntPtr.Zero; + } + } + + Log.Error(LogTag, "No OnCreate Callback"); + return IntPtr.Zero; + } + + private void OnTerminateNative(IntPtr context, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.Terminated)) + { + var handler = Handlers[EventType.Terminated] as Action; + try + { + handler?.Invoke(); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in Terminated handler: {ex.Message}"); + } + + defaultWindow?.Hide(); + DefaultWindowId = -1; + defaultWindow = null; + } + } + + private void OnAppControlNative(IntPtr context, IntPtr appControl, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.AppControlReceived)) + { + using (SafeAppControlHandle safeHandle = new SafeAppControlHandle(appControl, false)) + { + var handler = Handlers[EventType.AppControlReceived] as Action; + try + { + handler?.Invoke(new AppControlReceivedEventArgs(new ReceivedAppControl(safeHandle))); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in AppControlReceived handler: {ex.Message}"); + } + } + } + } + + private void OnResumeNative(IntPtr context, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.Resumed)) + { + var handler = Handlers[EventType.Resumed] as Action; + try + { + handler?.Invoke(); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in Resumed handler: {ex.Message}"); + } + } + } + + private void OnPauseNative(IntPtr context, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.Paused)) + { + var handler = Handlers[EventType.Paused] as Action; + try + { + handler?.Invoke(); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in Paused handler: {ex.Message}"); + } + } + } + + private void OnLowMemoryNative(IntPtr context, int status, IntPtr userdata) + { + LowMemoryStatus lowMemoryStatus = (LowMemoryStatus)status; + if (Handlers.ContainsKey(EventType.LowMemory)) + { + var handler = Handlers[EventType.LowMemory] as Action; + try + { + handler?.Invoke(new LowMemoryEventArgs(lowMemoryStatus)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in LowMemory handler: {ex.Message}"); + } + } + } + + private void OnLowBatteryNative(IntPtr context, int status, IntPtr userdata) + { + LowBatteryStatus lowBatteryStatus = (LowBatteryStatus)status; + if (Handlers.ContainsKey(EventType.LowBattery)) + { + var handler = Handlers[EventType.LowBattery] as Action; + try + { + handler?.Invoke(new LowBatteryEventArgs(lowBatteryStatus)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in LowBattery handler: {ex.Message}"); + } + } + } + + private void OnLanguageChangedNative(IntPtr context, string language, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.LocaleChanged)) + { + var handler = Handlers[EventType.LocaleChanged] as Action; + try + { + handler?.Invoke(new LocaleChangedEventArgs(language)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in LocaleChanged handler: {ex.Message}"); + } + } + } + + private void OnDeviceOrientationChangedNative(IntPtr context, int status, IntPtr userdata) + { + DeviceOrientation orientation = (DeviceOrientation)status; + if (Handlers.ContainsKey(EventType.DeviceOrientationChanged)) + { + var handler = Handlers[EventType.DeviceOrientationChanged] as Action; + try + { + handler?.Invoke(new DeviceOrientationEventArgs(orientation)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in DeviceOrientationChanged handler: {ex.Message}"); + } + } + } + + private void OnRegionFormatChangedNative(IntPtr context, string region, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.RegionFormatChanged)) + { + var handler = Handlers[EventType.RegionFormatChanged] as Action; + try + { + handler?.Invoke(new RegionFormatChangedEventArgs(region)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in RegionFormatChanged handler: {ex.Message}"); + } + } + } + + private void OnSuspendStateChangedNative(IntPtr context, int status, IntPtr userdata) + { + SuspendedState state = (SuspendedState)status; + if (Handlers.ContainsKey(EventType.SuspendedStateChanged)) + { + var handler = Handlers[EventType.SuspendedStateChanged] as Action; + try + { + handler?.Invoke(new SuspendedStateEventArgs(state)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in SuspendedStateChanged handler: {ex.Message}"); + } + } + } + + private void OnTimeZoneChangedNative(IntPtr context, string timeZone, string timeZoneId, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.TimeZoneChanged)) + { + var handler = Handlers[EventType.TimeZoneChanged] as Action; + try + { + handler?.Invoke(new TimeZoneChangedEventArgs(timeZone, timeZoneId)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in TimeZoneChanged handler: {ex.Message}"); + } + } + } + } +} diff --git a/src/Tizen.Applications.Team/Tizen.Applications.CoreBackend/TeamViewCoreBackend.cs b/src/Tizen.Applications.Team/Tizen.Applications.CoreBackend/TeamViewCoreBackend.cs new file mode 100644 index 00000000000..dbffc71a4bb --- /dev/null +++ b/src/Tizen.Applications.Team/Tizen.Applications.CoreBackend/TeamViewCoreBackend.cs @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2026 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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. + */ + +using System; +using Tizen.Internals; +using Tizen.NUI; +using Tizen.NUI.BaseComponents; + +namespace Tizen.Applications.CoreBackend +{ + /// + /// Backend implementation for Team view applications that render into a shared host-provided . + /// + internal class TeamViewCoreBackend : TeamCoreBackend + { + internal new static string LogTag = "DN_TAM"; + private Interop.TeamMember.ViewMemberLifecycleCallbacks _callbacks; + private bool _disposedValue = false; + private int DefaultViewId = 0; + internal View DefaultView; + internal override IntPtr MemberHandle => _memberHandle; + internal override IntPtr LoadObjId => _loadObjId; + internal override IntPtr ArgHandle => _argHandle; + + private Interop.TeamMember.ViewAppCreateCallback _onCreateNative; + private Interop.TeamMember.AppTerminateCallback _onTerminateNative; + private Interop.TeamMember.AppControlCallback _onAppControlNative; + private Interop.TeamMember.AppResumeCallback _onResumeNative; + private Interop.TeamMember.AppPauseCallback _onPauseNative; + private Interop.TeamMember.AppLowMemoryCallback _onLowMemoryNative; + private Interop.TeamMember.AppLowBatteryCallback _onLowBatteryNative; + private Interop.TeamMember.AppLanguageChangedCallback _onLanguageChangedNative; + private Interop.TeamMember.AppDeviceOrientationChangedCallback _onDeviceOrientationChangedNative; + private Interop.TeamMember.AppRegionFormatChangedCallback _onRegionFormatChangedNative; + private Interop.TeamMember.AppSuspendStateChangedCallback _onSuspendStateChangedNative; + private Interop.TeamMember.AppTimeZoneChangedCallback _onTimeZoneChangedNative; + + public TeamViewCoreBackend() + { + _onCreateNative = new Interop.TeamMember.ViewAppCreateCallback(OnCreateNative); + _onTerminateNative = new Interop.TeamMember.AppTerminateCallback(OnTerminateNative); + _onAppControlNative = new Interop.TeamMember.AppControlCallback(OnAppControlNative); + _onResumeNative = new Interop.TeamMember.AppResumeCallback(OnResumeNative); + _onPauseNative = new Interop.TeamMember.AppPauseCallback(OnPauseNative); + _onLowMemoryNative = new Interop.TeamMember.AppLowMemoryCallback(OnLowMemoryNative); + _onLowBatteryNative = new Interop.TeamMember.AppLowBatteryCallback(OnLowBatteryNative); + _onLanguageChangedNative = new Interop.TeamMember.AppLanguageChangedCallback(OnLanguageChangedNative); + _onDeviceOrientationChangedNative = new Interop.TeamMember.AppDeviceOrientationChangedCallback(OnDeviceOrientationChangedNative); + _onRegionFormatChangedNative = new Interop.TeamMember.AppRegionFormatChangedCallback(OnRegionFormatChangedNative); + _onSuspendStateChangedNative = new Interop.TeamMember.AppSuspendStateChangedCallback(OnSuspendStateChangedNative); + _onTimeZoneChangedNative = new Interop.TeamMember.AppTimeZoneChangedCallback(OnTimeZoneChangedNative); + + _callbacks.Create = _onCreateNative; + _callbacks.Terminate = _onTerminateNative; + _callbacks.Control = _onAppControlNative; + _callbacks.Resume = _onResumeNative; + _callbacks.Pause = _onPauseNative; + _callbacks.LowMemory = _onLowMemoryNative; + _callbacks.LowBattery = _onLowBatteryNative; + _callbacks.LanguageChanged = _onLanguageChangedNative; + _callbacks.DeviceOrientationChanged = _onDeviceOrientationChangedNative; + _callbacks.RegionFormatChanged = _onRegionFormatChangedNative; + _callbacks.SuspendStateChanged = _onSuspendStateChangedNative; + _callbacks.TimezoneChanged = _onTimeZoneChangedNative; + } + public View GetDefaultView() + { + return DefaultView; + } + + public int GetDefaultViewId() + { + return DefaultViewId; + } + internal void SetDefaultView(View view, string memberInstId) + { + DefaultView = view; + DefaultViewId = TeamManager.AddView(view, memberInstId); + } + internal void UnsetDefaultView() + { + TeamManager.RemoveView(DefaultViewId); + } + public override void Exit() + { + if (_memberHandle != IntPtr.Zero) + { + Interop.TeamMember.ViewMemberQuit(_memberHandle); + _memberHandle = IntPtr.Zero; + } + } + + public override void Run(string[] args) + { + // base.Run() is not required. + if (!TeamManager.IsInit()) + { + string[] argsClone = new string[args == null ? 1 : args.Length + 1]; + if (args != null && args.Length > 1) + { + args.CopyTo(argsClone, 1); + } + argsClone[0] = "Tizen.Applications.Team.dll"; + + TeamManager.Init(argsClone); + Log.Info("DN_TAM", $"Launching Team Loop."); + return; + } + + if (args == null || args.Length == 0) + { + Log.Error(LogTag, "args is null or empty"); + return; + } + + _loadObjId = TeamManager.GetAssemblyIdByPath(args[0]); + _argHandle = Interop.TeamMember.ViewMemberTeamup(_callbacks, IntPtr.Zero); + TeamManager.RegisterArgHandle(_loadObjId, _argHandle); + Log.Info(LogTag, $"path: {args[0]}, id: {_loadObjId}, arg_h: {_argHandle}"); + } + + protected override void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + // Release disposable objects + } + + if (_memberHandle != IntPtr.Zero) + { + Interop.TeamMember.ViewMemberQuit(_memberHandle); + _memberHandle = IntPtr.Zero; + } + + _disposedValue = true; + } + } + + private IntPtr OnCreateNative(IntPtr context, IntPtr userdata) + { + if (_memberHandle != IntPtr.Zero) + { + Log.Warn(LogTag, "OnCreate called twice!"); + } + _memberHandle = context; + + if (Handlers.ContainsKey(EventType.Created)) + { + var handler = Handlers[EventType.Created] as Action; + if (handler != null) + { + // This function will set default window + try + { + var view = new View(); + Interop.TeamManager.TeamAppGetAppInstanceId(_memberHandle, out string appInstId); + SetDefaultView(view, appInstId); + + try { + handler?.Invoke(); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in User Created handler: {ex.Message}"); + } + + IntPtr view_h = Interop.TeamManager.CreateViewByViewId(GetDefaultViewId()); + + if (view_h != IntPtr.Zero) + { + view.AggregatedVisibilityChanged += (sender, e) => + { + Log.Info(LogTag, $"View Visibility Changed: {e.Visibility}"); + Interop.TeamManager.InvokeViewVisibilityEvent(DefaultViewId, e.Visibility); + }; + } + + return view_h; + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in Internal Created handler: {ex.Message}"); + return IntPtr.Zero; + } + } + else + { + Log.Error(LogTag, "Invalid OnCreate Callback type"); + return IntPtr.Zero; + } + } + + Log.Error(LogTag, "No OnCreate Callback"); + return IntPtr.Zero; + } + + private void OnTerminateNative(IntPtr context, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.Terminated)) + { + var handler = Handlers[EventType.Terminated] as Action; + try + { + handler?.Invoke(); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in Terminated handler: {ex.Message}"); + } + } + + UnsetDefaultView(); + Interop.TeamManager.DestroyViewByViewId(GetDefaultViewId()); + } + + private void OnAppControlNative(IntPtr context, IntPtr appControl, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.AppControlReceived)) + { + using (SafeAppControlHandle safeHandle = new SafeAppControlHandle(appControl, false)) + { + var handler = Handlers[EventType.AppControlReceived] as Action; + try + { + handler?.Invoke(new AppControlReceivedEventArgs(new ReceivedAppControl(safeHandle))); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in AppControlReceived handler: {ex.Message}"); + } + } + } + } + + private void OnResumeNative(IntPtr context, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.Resumed)) + { + var handler = Handlers[EventType.Resumed] as Action; + try + { + handler?.Invoke(); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in Resumed handler: {ex.Message}"); + } + } + } + + private void OnPauseNative(IntPtr context, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.Paused)) + { + var handler = Handlers[EventType.Paused] as Action; + try + { + handler?.Invoke(); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in Paused handler: {ex.Message}"); + } + } + } + + private void OnLowMemoryNative(IntPtr context, int status, IntPtr userdata) + { + LowMemoryStatus lowMemoryStatus = (LowMemoryStatus)status; + if (Handlers.ContainsKey(EventType.LowMemory)) + { + var handler = Handlers[EventType.LowMemory] as Action; + try + { + handler?.Invoke(new LowMemoryEventArgs(lowMemoryStatus)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in LowMemory handler: {ex.Message}"); + } + } + } + + private void OnLowBatteryNative(IntPtr context, int status, IntPtr userdata) + { + LowBatteryStatus lowBatteryStatus = (LowBatteryStatus)status; + if (Handlers.ContainsKey(EventType.LowBattery)) + { + var handler = Handlers[EventType.LowBattery] as Action; + try + { + handler?.Invoke(new LowBatteryEventArgs(lowBatteryStatus)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in LowBattery handler: {ex.Message}"); + } + } + } + + private void OnLanguageChangedNative(IntPtr context, string language, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.LocaleChanged)) + { + var handler = Handlers[EventType.LocaleChanged] as Action; + try + { + handler?.Invoke(new LocaleChangedEventArgs(language)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in LocaleChanged handler: {ex.Message}"); + } + } + } + + private void OnDeviceOrientationChangedNative(IntPtr context, int status, IntPtr userdata) + { + DeviceOrientation orientation = (DeviceOrientation)status; + if (Handlers.ContainsKey(EventType.DeviceOrientationChanged)) + { + var handler = Handlers[EventType.DeviceOrientationChanged] as Action; + try + { + handler?.Invoke(new DeviceOrientationEventArgs(orientation)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in DeviceOrientationChanged handler: {ex.Message}"); + } + } + } + + private void OnRegionFormatChangedNative(IntPtr context, string region, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.RegionFormatChanged)) + { + var handler = Handlers[EventType.RegionFormatChanged] as Action; + try + { + handler?.Invoke(new RegionFormatChangedEventArgs(region)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in RegionFormatChanged handler: {ex.Message}"); + } + } + } + + private void OnSuspendStateChangedNative(IntPtr context, int status, IntPtr userdata) + { + SuspendedState state = (SuspendedState)status; + if (Handlers.ContainsKey(EventType.SuspendedStateChanged)) + { + var handler = Handlers[EventType.SuspendedStateChanged] as Action; + try + { + handler?.Invoke(new SuspendedStateEventArgs(state)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in SuspendedStateChanged handler: {ex.Message}"); + } + } + } + + private void OnTimeZoneChangedNative(IntPtr context, string timeZone, string timeZoneId, IntPtr userdata) + { + if (Handlers.ContainsKey(EventType.TimeZoneChanged)) + { + var handler = Handlers[EventType.TimeZoneChanged] as Action; + try + { + handler?.Invoke(new TimeZoneChangedEventArgs(timeZone, timeZoneId)); + } + catch (Exception ex) + { + Log.Error(LogTag, $"Error in TimeZoneChanged handler: {ex.Message}"); + } + } + } + } +} \ No newline at end of file diff --git a/src/Tizen.Applications.Team/Tizen.Applications.Team.csproj b/src/Tizen.Applications.Team/Tizen.Applications.Team.csproj new file mode 100755 index 00000000000..5809df03c80 --- /dev/null +++ b/src/Tizen.Applications.Team/Tizen.Applications.Team.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + + + + + + + + + + + + diff --git a/src/Tizen.NUI/src/internal/Common/FriendAssembly.cs b/src/Tizen.NUI/src/internal/Common/FriendAssembly.cs index 01bf6d0dc70..38ba0ea29fc 100755 --- a/src/Tizen.NUI/src/internal/Common/FriendAssembly.cs +++ b/src/Tizen.NUI/src/internal/Common/FriendAssembly.cs @@ -22,6 +22,7 @@ using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("Tizen.Applications.Team, " + Tizen.NUI.PublicKey.TizenFX)] [assembly: InternalsVisibleTo("Tizen.Multimedia, " + Tizen.NUI.PublicKey.TizenFX)] [assembly: InternalsVisibleTo("Tizen.NUI.Wearable, " + Tizen.NUI.PublicKey.TizenFX)] [assembly: InternalsVisibleTo("Tizen.NUI.Components, " + Tizen.NUI.PublicKey.TizenFX)]