提交 a299d69f 编写于 作者: A Adam Barth

Rename PlatformViewAndroid to io.flutter.view.FlutterView (#2586)

This patch cleans up the public Java interface to the Flutter engine. The code
that we intend to be re-usable is now in the io.flutter.view package. The two
public classes are:

 * FlutterMain, which controls initialization of the system, and
 * FlutterView, which is an Android view that hosts a Flutter app.

We'll need to interate on these interface over time, but at least these names
are more reasonable.
上级 cd30c9ff
......@@ -139,8 +139,8 @@ if (is_android) {
generate_jni("jni_headers") {
sources = [
"platform/android/org/domokit/sky/shell/PlatformViewAndroid.java",
"platform/android/org/domokit/sky/shell/SkyMain.java",
"platform/android/io/flutter/view/FlutterView.java",
"platform/android/io/flutter/view/FlutterMain.java",
"platform/android/org/domokit/sky/shell/TracingController.java",
]
jni_package = "sky/shell"
......@@ -148,11 +148,11 @@ if (is_android) {
shared_library("sky_shell") {
sources = [
"platform/android/flutter_main.cc",
"platform/android/flutter_main.h",
"platform/android/library_loader.cc",
"platform/android/platform_view_android.cc",
"platform/android/platform_view_android.h",
"platform/android/sky_main.cc",
"platform/android/sky_main.h",
"platform/android/tracing_controller.cc",
"platform/android/tracing_controller.h",
]
......@@ -175,16 +175,16 @@ if (is_android) {
android_library("java") {
java_files = [
"platform/android/org/domokit/sky/shell/FlutterSemanticsToAndroidAccessibilityBridge.java",
"platform/android/org/domokit/sky/shell/PlatformViewAndroid.java",
"platform/android/org/domokit/sky/shell/ResourceCleaner.java",
"platform/android/org/domokit/sky/shell/ResourceExtractor.java",
"platform/android/org/domokit/sky/shell/ServiceFactory.java",
"platform/android/org/domokit/sky/shell/ServiceProviderImpl.java",
"platform/android/org/domokit/sky/shell/ServiceRegistry.java",
"platform/android/io/flutter/view/AccessibilityBridge.java",
"platform/android/io/flutter/view/FlutterMain.java",
"platform/android/io/flutter/view/FlutterView.java",
"platform/android/io/flutter/view/ResourceCleaner.java",
"platform/android/io/flutter/view/ResourceExtractor.java",
"platform/android/io/flutter/view/ServiceFactory.java",
"platform/android/io/flutter/view/ServiceProviderImpl.java",
"platform/android/io/flutter/view/ServiceRegistry.java",
"platform/android/org/domokit/sky/shell/SkyActivity.java",
"platform/android/org/domokit/sky/shell/SkyApplication.java",
"platform/android/org/domokit/sky/shell/SkyMain.java",
"platform/android/org/domokit/sky/shell/TracingController.java",
]
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sky/shell/platform/android/sky_main.h"
#include "sky/shell/platform/android/flutter_main.h"
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
......@@ -18,7 +18,7 @@
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/threading/simple_thread.h"
#include "jni/SkyMain_jni.h"
#include "jni/FlutterMain_jni.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/embedder/simple_platform_support.h"
#include "sky/shell/shell.h"
......@@ -84,7 +84,7 @@ static void Init(JNIEnv* env,
InitializeTracing();
}
bool RegisterSkyMain(JNIEnv* env) {
bool RegisterFlutterMain(JNIEnv* env) {
return RegisterNativesImpl(env);
}
......
......@@ -2,17 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SKY_SHELL_SKY_MAIN_H_
#define SKY_SHELL_SKY_MAIN_H_
#ifndef SKY_SHELL_PLATFORM_ANDROID_FLUTTER_MAIN_H_
#define SKY_SHELL_PLATFORM_ANDROID_FLUTTER_MAIN_H_
#include <jni.h>
namespace sky {
namespace shell {
bool RegisterSkyMain(JNIEnv* env);
bool RegisterFlutterMain(JNIEnv* env);
} // namespace shell
} // namespace sky
#endif // SKY_SHELL_SKY_MAIN_H_
#endif // SKY_SHELL_PLATFORM_ANDROID_FLUTTER_MAIN_H_
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.domokit.sky.shell;
package io.flutter.view;
import android.graphics.Rect;
import android.opengl.Matrix;
......@@ -24,16 +24,15 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FlutterSemanticsToAndroidAccessibilityBridge extends AccessibilityNodeProvider
implements SemanticsListener {
class AccessibilityBridge extends AccessibilityNodeProvider implements SemanticsListener {
private Map<Integer, PersistentAccessibilityNode> mTreeNodes;
private PlatformViewAndroid mOwner;
private FlutterView mOwner;
private SemanticsServer.Proxy mSemanticsServer;
private boolean mAccessilibilyEnabled = true;
private PersistentAccessibilityNode mFocusedNode;
private PersistentAccessibilityNode mHoveredNode;
FlutterSemanticsToAndroidAccessibilityBridge(PlatformViewAndroid owner, SemanticsServer.Proxy semanticsServer) {
AccessibilityBridge(FlutterView owner, SemanticsServer.Proxy semanticsServer) {
assert owner != null;
assert semanticsServer != null;
mOwner = owner;
......@@ -42,7 +41,7 @@ public class FlutterSemanticsToAndroidAccessibilityBridge extends AccessibilityN
mSemanticsServer.addSemanticsListener(this);
}
public void setAccessibilityEnabled(boolean accessibilityEnabled) {
void setAccessibilityEnabled(boolean accessibilityEnabled) {
mAccessilibilyEnabled = accessibilityEnabled;
}
......@@ -189,14 +188,14 @@ public class FlutterSemanticsToAndroidAccessibilityBridge extends AccessibilityN
// TODO(ianh): implement findAccessibilityNodeInfosByText()
// TODO(ianh): implement findFocus()
public void handleTouchExplorationExit() {
void handleTouchExplorationExit() {
if (mHoveredNode != null) {
sendAccessibilityEvent(mHoveredNode.id, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
mHoveredNode = null;
}
}
public void handleTouchExploration(float x, float y) {
void handleTouchExploration(float x, float y) {
if (mTreeNodes.isEmpty())
return;
assert mTreeNodes.containsKey(0);
......@@ -247,7 +246,7 @@ public class FlutterSemanticsToAndroidAccessibilityBridge extends AccessibilityN
return persistentNode;
}
public void removePersistentNode(PersistentAccessibilityNode node) {
void removePersistentNode(PersistentAccessibilityNode node) {
assert mTreeNodes.containsKey(node.id);
assert mTreeNodes.get(node.id).parent == null;
mTreeNodes.remove(node.id);
......@@ -263,7 +262,7 @@ public class FlutterSemanticsToAndroidAccessibilityBridge extends AccessibilityN
}
}
public void reset(SemanticsServer.Proxy newSemanticsServer) {
void reset(SemanticsServer.Proxy newSemanticsServer) {
mTreeNodes.clear();
sendAccessibilityEvent(mFocusedNode.id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
mFocusedNode = null;
......@@ -315,7 +314,7 @@ public class FlutterSemanticsToAndroidAccessibilityBridge extends AccessibilityN
}
}
for (SemanticsNode childNode : node.children) {
PersistentAccessibilityNode child = FlutterSemanticsToAndroidAccessibilityBridge.this.updateSemanticsNode(childNode);
PersistentAccessibilityNode child = AccessibilityBridge.this.updateSemanticsNode(childNode);
assert child != null;
child.parent = this;
children.add(child);
......@@ -323,7 +322,7 @@ public class FlutterSemanticsToAndroidAccessibilityBridge extends AccessibilityN
if (oldChildren != null) {
for (PersistentAccessibilityNode child : oldChildren) {
if (child.parent == null) {
FlutterSemanticsToAndroidAccessibilityBridge.this.removePersistentNode(child);
AccessibilityBridge.this.removePersistentNode(child);
}
}
}
......@@ -336,16 +335,16 @@ public class FlutterSemanticsToAndroidAccessibilityBridge extends AccessibilityN
}
// fields that we pass straight to the Android accessibility API
public int id = -1;
public PersistentAccessibilityNode parent;
public boolean canBeTapped;
public boolean canBeLongPressed;
public boolean canBeScrolledHorizontally;
public boolean canBeScrolledVertically;
public boolean hasCheckedState;
public boolean isChecked;
public String label;
public List<PersistentAccessibilityNode> children;
int id = -1;
PersistentAccessibilityNode parent;
boolean canBeTapped;
boolean canBeLongPressed;
boolean canBeScrolledHorizontally;
boolean canBeScrolledVertically;
boolean hasCheckedState;
boolean isChecked;
String label;
List<PersistentAccessibilityNode> children;
// geometry, which we have to convert to global coordinates to send to Android
private float[] transform; // can be null, meaning identity transform
......@@ -360,7 +359,7 @@ public class FlutterSemanticsToAndroidAccessibilityBridge extends AccessibilityN
return;
}
geometryDirty = true;
// TODO(ianh): if we are the FlutterSemanticsToAndroidAccessibilityBridge.this.mFocusedNode
// TODO(ianh): if we are the AccessibilityBridge.this.mFocusedNode
// then we may have to unfocus and refocus ourselves to get Android to update the focus rect
for (PersistentAccessibilityNode child : children) {
child.invalidateGlobalGeometry();
......@@ -407,7 +406,7 @@ public class FlutterSemanticsToAndroidAccessibilityBridge extends AccessibilityN
return Math.max(a, Math.max(b, Math.max(c, d)));
}
public Rect getGlobalRect() {
Rect getGlobalRect() {
if (geometryDirty) {
float[] transform = getGlobalTransform();
float[] point1 = transformPoint(transform, new float[]{left, top, 0, 1});
......@@ -426,7 +425,7 @@ public class FlutterSemanticsToAndroidAccessibilityBridge extends AccessibilityN
return globalRect;
}
public PersistentAccessibilityNode hitTest(int x, int y) {
PersistentAccessibilityNode hitTest(int x, int y) {
Rect rect = getGlobalRect();
if (!rect.contains(x, y))
return null;
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.domokit.sky.shell;
package io.flutter.view;
import android.content.Context;
import android.util.Log;
......@@ -44,32 +44,25 @@ import org.domokit.platform.SystemChromeImpl;
import org.domokit.platform.SystemSoundImpl;
import org.domokit.vsync.VSyncProviderImpl;
/**
* A class to intialize the native code.
* A class to intialize the Flutter engine.
**/
@JNINamespace("sky::shell")
public class SkyMain {
private static final String TAG = "SkyMain";
public class FlutterMain {
public static final String APP_BUNDLE = "app.flx";
private static final String TAG = "FlutterMain";
private static final String MANIFEST = "flutter.yaml";
private static final String SERVICES = "services.json";
private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "sky_shell";
private static final String[] SKY_RESOURCES = {"icudtl.dat", APP_BUNDLE, MANIFEST};
/**
* A guard flag for calling nativeInit() only once.
**/
private static boolean sInitialized = false;
private static ResourceExtractor sResourceExtractor;
/**
* Starts initialization of the native system.
**/
public static void startInit(Context applicationContext) {
public static void startInitialization(Context applicationContext) {
initJavaUtils(applicationContext);
initResources(applicationContext);
initNative(applicationContext);
......@@ -79,7 +72,7 @@ public class SkyMain {
/**
* Blocks until initialization of the native system has completed.
**/
public static void ensureInitialized(Context applicationContext, String[] args) {
public static void ensureInitializationComplete(Context applicationContext, String[] args) {
if (sInitialized) {
return;
}
......@@ -90,7 +83,7 @@ public class SkyMain {
CoreImpl.getInstance().createDefaultRunLoop();
sInitialized = true;
} catch (Exception e) {
Log.e(TAG, "SkyMain initialization failed.", e);
Log.e(TAG, "Flutter initialization failed.", e);
throw new RuntimeException(e);
}
}
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.domokit.sky.shell;
package io.flutter.view;
import android.content.Context;
import android.content.res.Configuration;
......@@ -56,16 +56,14 @@ import org.domokit.editing.KeyboardViewState;
import org.domokit.raw_keyboard.RawKeyboardServiceImpl;
import org.domokit.raw_keyboard.RawKeyboardServiceState;
import org.domokit.sky.shell.FlutterSemanticsToAndroidAccessibilityBridge;
/**
* A view containing Sky
* An Android view containing a Flutter app.
*/
@JNINamespace("sky::shell")
public class PlatformViewAndroid extends SurfaceView
public class FlutterView extends SurfaceView
implements AccessibilityManager.AccessibilityStateChangeListener,
AccessibilityManager.TouchExplorationStateChangeListener {
private static final String TAG = "PlatformViewAndroid";
private static final String TAG = "FlutterView";
private long mNativePlatformView;
private SkyEngine.Proxy mSkyEngine;
......@@ -81,11 +79,11 @@ public class PlatformViewAndroid extends SurfaceView
private final RawKeyboardServiceState mRawKeyboardState;
private final AccessibilityManager mAccessibilityManager;
public PlatformViewAndroid(Context context) {
public FlutterView(Context context) {
this(context, null);
}
public PlatformViewAndroid(Context context, AttributeSet attrs) {
public FlutterView(Context context, AttributeSet attrs) {
super(context, attrs);
mMetrics = new ViewportMetrics();
......@@ -471,11 +469,11 @@ public class PlatformViewAndroid extends SurfaceView
return mAccessibilityNodeProvider;
}
private FlutterSemanticsToAndroidAccessibilityBridge mAccessibilityNodeProvider;
private AccessibilityBridge mAccessibilityNodeProvider;
void ensureAccessibilityEnabled() {
if (mAccessibilityNodeProvider == null) {
mAccessibilityNodeProvider = new FlutterSemanticsToAndroidAccessibilityBridge(this, createSemanticsServer());
mAccessibilityNodeProvider = new AccessibilityBridge(this, createSemanticsServer());
}
}
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.domokit.sky.shell;
package io.flutter.view;
import android.content.Context;
import android.os.AsyncTask;
......@@ -18,18 +18,18 @@ import org.domokit.common.ResourcePaths;
/**
* A class to clean up orphaned resource directories after unclean shutdowns.
**/
public class ResourceCleaner {
class ResourceCleaner {
private static final String TAG = "ResourceCleaner";
private static final long DELAY_MS = 5000;
private class CleanTask extends AsyncTask<Void, Void, Void> {
private final File[] mFilesToDelete;
public CleanTask(File[] filesToDelete) {
CleanTask(File[] filesToDelete) {
mFilesToDelete = filesToDelete;
}
public boolean hasFilesToDelete() {
boolean hasFilesToDelete() {
return mFilesToDelete.length > 0;
}
......@@ -56,11 +56,11 @@ public class ResourceCleaner {
private final Context mContext;
public ResourceCleaner(Context context) {
ResourceCleaner(Context context) {
mContext = context;
}
public void start() {
void start() {
File cacheDir = mContext.getCacheDir();
if (cacheDir == null) {
return;
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.domokit.sky.shell;
package io.flutter.view;
import android.content.Context;
import android.content.pm.PackageInfo;
......@@ -26,14 +26,14 @@ import java.util.concurrent.ExecutionException;
/**
* A class to intialize the native code.
**/
public class ResourceExtractor {
class ResourceExtractor {
private static final String TAG = "ResourceExtractor";
private static final String TIMESTAMP_PREFIX = "res_timestamp-";
private class ExtractTask extends AsyncTask<Void, Void, Void> {
private static final int BUFFER_SIZE = 16 * 1024;
public ExtractTask() { }
ExtractTask() { }
private void extractResources() {
final File dataDir = new File(PathUtils.getDataDirectory(mContext));
......@@ -131,24 +131,24 @@ public class ResourceExtractor {
private final HashSet<String> mResources;
private ExtractTask mExtractTask;
public ResourceExtractor(Context context) {
ResourceExtractor(Context context) {
mContext = context;
mResources = new HashSet<String>();
}
public void addResources(String[] resources) {
void addResources(String[] resources) {
for (String resource : resources) {
mResources.add(resource);
}
}
public void start() {
void start() {
assert mExtractTask == null;
mExtractTask = new ExtractTask();
mExtractTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public void waitForCompletion() {
void waitForCompletion() {
assert mExtractTask != null;
try {
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.domokit.sky.shell;
package io.flutter.view;
import android.content.Context;
......@@ -14,6 +14,6 @@ import org.chromium.mojo.system.MessagePipeHandle;
* registered with ServiceRegistry and thereby made available to non-Java
* clients.
**/
public interface ServiceFactory {
public void connectToService(Context context, Core core, MessagePipeHandle pipe);
interface ServiceFactory {
void connectToService(Context context, Core core, MessagePipeHandle pipe);
}
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.domokit.sky.shell;
package io.flutter.view;
import android.content.Context;
......@@ -15,12 +15,12 @@ import org.chromium.mojom.mojo.ServiceProvider;
/**
* A collection of services.
**/
public class ServiceProviderImpl implements ServiceProvider {
class ServiceProviderImpl implements ServiceProvider {
private Core mCore;
private Context mContext;
private ServiceRegistry mRegistry;
public ServiceProviderImpl(Core core, Context context, ServiceRegistry registry) {
ServiceProviderImpl(Core core, Context context, ServiceRegistry registry) {
assert core != null;
assert context != null;
mCore = core;
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.domokit.sky.shell;
package io.flutter.view;
import android.util.Log;
......@@ -12,11 +12,11 @@ import java.util.TreeMap;
/**
* An registry for services.
**/
public class ServiceRegistry {
class ServiceRegistry {
private static final String TAG = "ServiceRegistry";
private Map<String, ServiceFactory> mRegistrations;
public static final ServiceRegistry SHARED = new ServiceRegistry();
static final ServiceRegistry SHARED = new ServiceRegistry();
// In addition to the shared registry, there is a per-view registry
// maintained by the PlatformServiceProvider.
......@@ -24,13 +24,13 @@ public class ServiceRegistry {
mRegistrations = new TreeMap<String, ServiceFactory>();
}
public void register(String interfaceName, ServiceFactory connector) {
void register(String interfaceName, ServiceFactory connector) {
assert !mRegistrations.containsKey(interfaceName);
assert connector != null;
mRegistrations.put(interfaceName, connector);
}
public ServiceFactory get(String interfaceName) {
ServiceFactory get(String interfaceName) {
if (!mRegistrations.containsKey(interfaceName)) {
Log.e(TAG, "Unknown service " + interfaceName);
}
......
......@@ -13,7 +13,7 @@
#include "mojo/android/system/core_impl.h"
#include "sky/engine/bindings/jni/dart_jni.h"
#include "sky/shell/platform/android/platform_view_android.h"
#include "sky/shell/platform/android/sky_main.h"
#include "sky/shell/platform/android/flutter_main.h"
#include "sky/shell/platform/android/tracing_controller.h"
namespace {
......@@ -21,8 +21,8 @@ namespace {
base::android::RegistrationMethod kSkyRegisteredMethods[] = {
{"CoreImpl", mojo::android::RegisterCoreImpl},
{"BaseRunLoop", mojo::android::RegisterBaseRunLoop},
{"PlatformViewAndroid", sky::shell::PlatformViewAndroid::Register},
{"SkyMain", sky::shell::RegisterSkyMain},
{"FlutterView", sky::shell::PlatformViewAndroid::Register},
{"FlutterMain", sky::shell::RegisterFlutterMain},
{"TracingController", sky::shell::RegisterTracingController},
};
......
......@@ -13,6 +13,9 @@ import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import io.flutter.view.FlutterMain;
import io.flutter.view.FlutterView;
import org.chromium.base.PathUtils;
import org.chromium.base.TraceEvent;
import org.chromium.mojom.sky.EventType;
......@@ -29,7 +32,7 @@ import java.util.ArrayList;
*/
public class SkyActivity extends Activity {
private TracingController mTracingController;
private PlatformViewAndroid mView;
private FlutterView mView;
private String[] getArgsFromIntent(Intent intent) {
// Before adding more entries to this list, consider that arbitrary
......@@ -69,8 +72,8 @@ public class SkyActivity extends Activity {
}
String[] args = getArgsFromIntent(getIntent());
SkyMain.ensureInitialized(getApplicationContext(), args);
mView = new PlatformViewAndroid(this);
FlutterMain.ensureInitializationComplete(getApplicationContext(), args);
mView = new FlutterView(this);
ActivityImpl.setCurrentActivity(this);
setContentView(mView);
mTracingController = new TracingController(this);
......@@ -132,7 +135,7 @@ public class SkyActivity extends Activity {
return;
}
File dataDir = new File(PathUtils.getDataDirectory(this));
File appBundle = new File(dataDir, SkyMain.APP_BUNDLE);
File appBundle = new File(dataDir, FlutterMain.APP_BUNDLE);
if (appBundle.exists()) {
mView.runFromBundle(appBundle.getPath(), null);
return;
......
......@@ -6,6 +6,8 @@ package org.domokit.sky.shell;
import android.app.Application;
import io.flutter.view.FlutterMain;
/**
* Sky implementation of {@link android.app.Application}, managing application-level global
* initializations.
......@@ -14,6 +16,6 @@ public class SkyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
SkyMain.startInit(this);
FlutterMain.startInitialization(this);
}
}
......@@ -31,7 +31,7 @@ class TracingController {
private final TracingBroadcastReceiver mBroadcastReceiver;
private final TracingIntentFilter mIntentFilter;
public TracingController(Context context) {
TracingController(Context context) {
mContext = context;
mBroadcastReceiver = new TracingBroadcastReceiver();
mIntentFilter = new TracingIntentFilter(context);
......@@ -39,7 +39,7 @@ class TracingController {
mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
}
public void stop() {
void stop() {
mContext.unregisterReceiver(mBroadcastReceiver);
}
......
......@@ -10,7 +10,7 @@
#include "base/android/jni_android.h"
#include "base/bind.h"
#include "base/location.h"
#include "jni/PlatformViewAndroid_jni.h"
#include "jni/FlutterView_jni.h"
#include "sky/shell/gpu/direct/surface_notifications_direct.h"
#include "sky/shell/shell.h"
#include "sky/shell/shell_view.h"
......
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SKY_SHELL_PLATFORM_VIEW_ANDROID_H_
#define SKY_SHELL_PLATFORM_VIEW_ANDROID_H_
#ifndef SKY_SHELL_PLATFORM_ANDROID_PLATFORM_VIEW_ANDROID_H_
#define SKY_SHELL_PLATFORM_ANDROID_PLATFORM_VIEW_ANDROID_H_
#include "sky/shell/platform_view.h"
......@@ -43,4 +43,4 @@ class PlatformViewAndroid : public PlatformView {
} // namespace shell
} // namespace sky
#endif // SKY_SHELL_PLATFORM_VIEW_ANDROID_H_
#endif // SKY_SHELL_PLATFORM_ANDROID_PLATFORM_VIEW_ANDROID_H_
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册