| // Copyright 2018 The Cobalt Authors. 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. |
| |
| #include "starboard/android/shared/application_android.h" |
| #include "starboard/android/shared/jni_env_ext.h" |
| #include "starboard/android/shared/jni_utils.h" |
| #include "starboard/android/shared/log_internal.h" |
| #include "starboard/common/semaphore.h" |
| #include "starboard/common/string.h" |
| #include "starboard/shared/starboard/command_line.h" |
| #include "starboard/thread.h" |
| |
| namespace starboard { |
| namespace android { |
| namespace shared { |
| namespace { |
| |
| using ::starboard::shared::starboard::CommandLine; |
| typedef ::starboard::android::shared::ApplicationAndroid::AndroidCommand |
| AndroidCommand; |
| |
| SbThread g_starboard_thread = kSbThreadInvalid; |
| |
| // Safeguard to avoid sending AndroidCommands either when there is no instance |
| // of the Starboard application, or after the run loop has exited and the |
| // ALooper receiving the commands is no longer being polled. |
| bool g_app_running = false; |
| |
| std::vector<std::string> GetArgs() { |
| std::vector<std::string> args; |
| // Fake program name as args[0] |
| args.push_back(SbStringDuplicate("android_main")); |
| |
| JniEnvExt* env = JniEnvExt::Get(); |
| |
| ScopedLocalJavaRef<jobjectArray> args_array( |
| env->CallStarboardObjectMethodOrAbort("getArgs", |
| "()[Ljava/lang/String;")); |
| jint argc = !args_array ? 0 : env->GetArrayLength(args_array.Get()); |
| |
| for (jint i = 0; i < argc; i++) { |
| ScopedLocalJavaRef<jstring> element( |
| env->GetObjectArrayElementOrAbort(args_array.Get(), i)); |
| args.push_back(env->GetStringStandardUTFOrAbort(element.Get())); |
| } |
| |
| return args; |
| } |
| |
| std::string GetStartDeepLink() { |
| JniEnvExt* env = JniEnvExt::Get(); |
| std::string start_url; |
| |
| ScopedLocalJavaRef<jstring> j_url(env->CallStarboardObjectMethodOrAbort( |
| "getStartDeepLink", "()Ljava/lang/String;")); |
| if (j_url) { |
| start_url = env->GetStringStandardUTFOrAbort(j_url.Get()); |
| } |
| SB_LOG(INFO) << "GetStartDeepLink: " << start_url; |
| return start_url; |
| } |
| |
| void* ThreadEntryPoint(void* context) { |
| Semaphore* app_created_semaphore = static_cast<Semaphore*>(context); |
| |
| ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); |
| ApplicationAndroid app(looper); |
| |
| CommandLine command_line(GetArgs()); |
| LogInit(command_line); |
| |
| // Mark the app running before signaling app created so there's no race to |
| // allow sending the first AndroidCommand after onCreate() returns. |
| g_app_running = true; |
| |
| // Signal ANativeActivity_onCreate() that it may proceed. |
| app_created_semaphore->Put(); |
| |
| // Enter the Starboard run loop until stopped. |
| int error_level = |
| app.Run(std::move(command_line), GetStartDeepLink().c_str()); |
| |
| // Mark the app not running before informing StarboardBridge that the app is |
| // stopped so that we won't send any more AndroidCommands as a result of |
| // shutting down the Activity. |
| g_app_running = false; |
| |
| // Our launcher.py looks for this to know when the app (test) is done. |
| SB_LOG(INFO) << "***Application Stopped*** " << error_level; |
| |
| // Inform StarboardBridge that the run loop has exited so it can cleanup and |
| // kill the process. |
| JniEnvExt* env = JniEnvExt::Get(); |
| env->CallStarboardVoidMethodOrAbort("afterStopped", "()V"); |
| |
| return NULL; |
| } |
| |
| void OnStart(ANativeActivity* activity) { |
| if (g_app_running) { |
| ApplicationAndroid::Get()->SendAndroidCommand(AndroidCommand::kStart); |
| } |
| } |
| |
| void OnResume(ANativeActivity* activity) { |
| if (g_app_running) { |
| ApplicationAndroid::Get()->SendAndroidCommand(AndroidCommand::kResume); |
| } |
| } |
| |
| void OnPause(ANativeActivity* activity) { |
| if (g_app_running) { |
| ApplicationAndroid::Get()->SendAndroidCommand(AndroidCommand::kPause); |
| } |
| } |
| |
| void OnStop(ANativeActivity* activity) { |
| if (g_app_running) { |
| ApplicationAndroid::Get()->SendAndroidCommand(AndroidCommand::kStop); |
| } |
| } |
| |
| void OnWindowFocusChanged(ANativeActivity* activity, int focused) { |
| if (g_app_running) { |
| ApplicationAndroid::Get()->SendAndroidCommand(focused |
| ? AndroidCommand::kWindowFocusGained |
| : AndroidCommand::kWindowFocusLost); |
| } |
| } |
| |
| void OnNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { |
| if (g_app_running) { |
| ApplicationAndroid::Get()->SendAndroidCommand( |
| AndroidCommand::kNativeWindowCreated, window); |
| } |
| } |
| |
| void OnNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { |
| if (g_app_running) { |
| ApplicationAndroid::Get()->SendAndroidCommand( |
| AndroidCommand::kNativeWindowDestroyed); |
| } |
| } |
| |
| void OnInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { |
| if (g_app_running) { |
| ApplicationAndroid::Get()->SendAndroidCommand( |
| AndroidCommand::kInputQueueChanged, queue); |
| } |
| } |
| |
| void OnInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { |
| if (g_app_running) { |
| ApplicationAndroid::Get()->SendAndroidCommand( |
| AndroidCommand::kInputQueueChanged, NULL); |
| } |
| } |
| |
| extern "C" SB_EXPORT_PLATFORM void ANativeActivity_onCreate( |
| ANativeActivity *activity, void *savedState, size_t savedStateSize) { |
| |
| // Start the Starboard thread the first time an Activity is created. |
| if (!SbThreadIsValid(g_starboard_thread)) { |
| Semaphore semaphore; |
| |
| g_starboard_thread = SbThreadCreate( |
| 0, kSbThreadPriorityNormal, kSbThreadNoAffinity, false, |
| "StarboardMain", &ThreadEntryPoint, &semaphore); |
| |
| // Wait for the ApplicationAndroid to be created. |
| semaphore.Take(); |
| } |
| |
| activity->callbacks->onStart = OnStart; |
| activity->callbacks->onResume = OnResume; |
| activity->callbacks->onPause = OnPause; |
| activity->callbacks->onStop = OnStop; |
| activity->callbacks->onWindowFocusChanged = OnWindowFocusChanged; |
| activity->callbacks->onNativeWindowCreated = OnNativeWindowCreated; |
| activity->callbacks->onNativeWindowDestroyed = OnNativeWindowDestroyed; |
| activity->callbacks->onInputQueueCreated = OnInputQueueCreated; |
| activity->callbacks->onInputQueueDestroyed = OnInputQueueDestroyed; |
| activity->instance = ApplicationAndroid::Get(); |
| } |
| |
| extern "C" SB_EXPORT_PLATFORM |
| jboolean Java_dev_cobalt_coat_StarboardBridge_nativeIsReleaseBuild() { |
| #if defined(COBALT_BUILD_TYPE_GOLD) |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| extern "C" SB_EXPORT_PLATFORM |
| void Java_dev_cobalt_coat_StarboardBridge_nativeInitialize( |
| JniEnvExt* env, jobject starboard_bridge) { |
| JniEnvExt::Initialize(env, starboard_bridge); |
| } |
| |
| } // namespace |
| } // namespace shared |
| } // namespace android |
| } // namespace starboard |