Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core_internal / src / org / xwalk / core / internal / extension / XWalkExtensionManager.java
1 // Copyright (c) 2013 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.xwalk.core.internal.extension;
6
7 import android.app.Activity;
8 import android.content.Context;
9 import android.content.Intent;
10 import android.content.res.AssetManager;
11 import android.content.res.Resources;
12 import android.content.res.Resources.NotFoundException;
13 import android.util.Log;
14
15 import java.io.File;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.lang.reflect.Constructor;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.Class;
21 import java.util.HashMap;
22
23 import org.json.JSONArray;
24 import org.json.JSONException;
25 import org.json.JSONObject;
26 import org.xwalk.core.internal.extension.api.contacts.Contacts;
27 import org.xwalk.core.internal.extension.api.device_capabilities.DeviceCapabilities;
28 import org.xwalk.core.internal.extension.api.launchscreen.LaunchScreenExtension;
29 import org.xwalk.core.internal.extension.api.messaging.Messaging;
30 import org.xwalk.core.internal.extension.api.presentation.PresentationExtension;
31
32 /**
33  * This internal class acts a manager to manage extensions.
34  */
35 public class XWalkExtensionManager implements XWalkExtensionContext {
36     private final static String TAG = "XWalkExtensionManager";
37     private final static String EXTENSION_CONFIG_FILE = "extensions-config.json";
38     // This class name is from runtime client. Need to keep consistency with it.
39     private final static String EXTENSION_CONTEXT_CLIENT_CLASS_NAME =
40             "org.xwalk.app.runtime.extension.XWalkExtensionContextClient";
41
42     private final Context mContext;
43     private final Activity mActivity;
44
45     private final HashMap<String, XWalkExtensionBridge> mExtensions = new HashMap<String, XWalkExtensionBridge>();
46     // This variable is to set whether to load external extensions. The default is true.
47     private boolean mLoadExternalExtensions;
48
49     public XWalkExtensionManager(Context context, Activity activity) {
50         mContext = context;
51         mActivity = activity;
52         mLoadExternalExtensions = true;
53     }
54
55     @Override
56     public void registerExtension(XWalkExtension extension) {
57         if (mExtensions.get(extension.getExtensionName()) != null) {
58             Log.e(TAG, extension.getExtensionName() + "is already registered!");
59             return;
60         }
61
62         XWalkExtensionBridge bridge = XWalkExtensionBridgeFactory.createInstance(extension);
63         mExtensions.put(extension.getExtensionName(), bridge);
64     }
65
66     @Override
67     public void unregisterExtension(String name) {
68         XWalkExtensionBridge bridge = mExtensions.get(name);
69         if (bridge != null) {
70             mExtensions.remove(name);
71             bridge.onDestroy();
72         }
73     }
74
75     @Override
76     public Context getContext() {
77         return mContext;
78     }
79
80     @Override
81     public Activity getActivity() {
82         return mActivity;
83     }
84
85     @Override
86     public void postMessage(XWalkExtension extension, int instanceID, String message) {
87         XWalkExtensionBridge bridge = mExtensions.get(extension.getExtensionName());
88         if (bridge != null) bridge.postMessage(instanceID, message);
89     }
90
91     public void broadcastMessage(XWalkExtension extension, String message) {
92         XWalkExtensionBridge bridge = mExtensions.get(extension.getExtensionName());
93         if (bridge != null) bridge.broadcastMessage(message);
94     }
95
96     public void onStart() {
97         for(XWalkExtensionBridge extension: mExtensions.values()) {
98             extension.onStart();
99         }
100     }
101
102     public void onResume() {
103         for(XWalkExtensionBridge extension: mExtensions.values()) {
104             extension.onResume();
105         }
106     }
107
108     public void onPause() {
109         for(XWalkExtensionBridge extension: mExtensions.values()) {
110             extension.onPause();
111         }
112     }
113
114     public void onStop() {
115         for(XWalkExtensionBridge extension: mExtensions.values()) {
116             extension.onStop();
117         }
118     }
119
120     public void onDestroy() {
121         for(XWalkExtensionBridge extension: mExtensions.values()) {
122             extension.onDestroy();
123         }
124         mExtensions.clear();
125     }
126
127     public void onActivityResult(int requestCode, int resultCode, Intent data) {
128         for(XWalkExtensionBridge extension: mExtensions.values()) {
129             extension.onActivityResult(requestCode, resultCode, data);
130         }
131     }
132
133     public void loadExtensions() {
134         loadInternalExtensions();
135         loadExternalExtensions();
136     }
137
138     public void setAllowExternalExtensions(boolean load) {
139         mLoadExternalExtensions = load;
140     }
141
142     private void loadInternalExtensions() {
143         // Create all extension instances directly here. The internal extension will register
144         // itself and add itself to XWalkExtensionManager.mExtensions automatically.
145         // The following sample shows how to create an extension that named Device:
146         //    String jsApiContent = "";
147         //    try {
148         //        jsApiContent = getExtensionJSFileContent(mContext, Device.JS_API_PATH, true);
149         //        new Device(jsApiContent, mExtensionContextImpl);
150         //    } catch(IOException e) {
151         //        Log.w(TAG, "Failed to read js API file of internal extension: Device");
152         //    }
153         {
154             String jsApiContent = "";
155             try {
156                 jsApiContent = getExtensionJSFileContent(
157                         mContext, PresentationExtension.JS_API_PATH, true);
158                 new PresentationExtension(jsApiContent, this);
159             } catch (IOException e) {
160                 Log.w(TAG, "Failed to read JS API file: " + PresentationExtension.JS_API_PATH);
161             }
162         }
163
164         {
165             String jsApiContent = "";
166             try {
167                 jsApiContent = getExtensionJSFileContent(
168                         mContext, LaunchScreenExtension.JS_API_PATH, true);
169                 new LaunchScreenExtension(jsApiContent, this);
170             } catch (IOException e) {
171                 Log.w(TAG, "Failed to read JS API file: " + LaunchScreenExtension.JS_API_PATH);
172             }
173         }
174
175         {
176             String jsApiContent = "";
177             try {
178                 jsApiContent = getExtensionJSFileContent(
179                         mContext, Contacts.JS_API_PATH, true);
180                 new Contacts(jsApiContent, this);
181             } catch(IOException e) {
182                 Log.w(TAG, "Failed to read JS API file: " + Contacts.JS_API_PATH);
183             }
184         }
185
186         {
187             String jsApiContent = "";
188             try {
189                 jsApiContent = getExtensionJSFileContent(
190                         mContext, DeviceCapabilities.JS_API_PATH, true);
191                 new DeviceCapabilities(jsApiContent, this);
192             } catch(IOException e) {
193                 Log.w(TAG, "Failed to read JS API file: " + DeviceCapabilities.JS_API_PATH);
194             }
195         }
196
197         {
198             String jsApiContent = "";
199             try {
200                 jsApiContent = getExtensionJSFileContent(
201                         mContext, Messaging.JS_API_PATH, true);
202                 new Messaging(jsApiContent, this);
203             } catch(IOException e) {
204                 Log.w(TAG, "Failed to read JS API file: " + Messaging.JS_API_PATH);
205             }
206         }
207     }
208
209     private void loadExternalExtensions() {
210         if (!mLoadExternalExtensions) return;
211
212         // Read extensions-config.json and create external extensions.
213         String configFileContent;
214         try {
215             configFileContent = getExtensionJSFileContent(mActivity, EXTENSION_CONFIG_FILE, false);
216         } catch (IOException e) {
217             Log.w(TAG, "Failed to read extensions-config.json");
218             return;
219         }
220
221         // Initialize the context for external extensions.
222         XWalkExtensionContextWrapper contextWrapper =
223                 new XWalkExtensionContextWrapper(this);
224         Object contextClient = createExtensionContextClient(contextWrapper);
225
226         try {
227             JSONArray jsonFeatures = new JSONArray(configFileContent);
228             int extensionCount = jsonFeatures.length();
229             for (int i = 0; i < extensionCount; i++) {
230                 JSONObject jsonObject = jsonFeatures.getJSONObject(i);
231                 String name = jsonObject.getString("name");
232                 String className =  jsonObject.getString("class");
233                 String jsApiFile = jsonObject.getString("jsapi");
234
235                 // Load the content of the JavaScript file.
236                 String jsApi;
237                 try {
238                     jsApi = getExtensionJSFileContent(mActivity, jsApiFile, false);
239                 } catch (IOException e) {
240                     Log.w(TAG, "Failed to read the file " + jsApiFile);
241                     return;
242                 }
243
244                 if (name != null && className != null && jsApi != null) {
245                     createExternalExtension(name, className, jsApi, contextClient, contextWrapper);
246                 }
247             }
248         } catch (JSONException e) {
249             Log.w(TAG, "Failed to parse extensions-config.json");
250         }
251     }
252
253     private String getExtensionJSFileContent(Context context, String fileName, boolean fromRaw)
254             throws IOException {
255         String result = "";
256         InputStream inputStream = null;
257         try {
258             if (fromRaw) {
259                 // If fromRaw is true, Try to find js file in res/raw first.
260                 // And then try to get it from assets if failed.
261                 Resources resource = context.getResources();
262                 String resName = (new File(fileName).getName().split("\\."))[0];
263                 int resId = resource.getIdentifier(resName, "raw", context.getPackageName());
264                 if (resId > 0) {
265                     try {
266                         inputStream = resource.openRawResource(resId);
267                     } catch (NotFoundException e) {
268                         Log.w(TAG, "Inputstream failed to open for R.raw." + resName +
269                                    ", try to find it in assets");
270                     }
271                 }
272             }
273             if (inputStream == null) {
274                 AssetManager assetManager = context.getAssets();
275                 inputStream = assetManager.open(fileName);
276             }
277             int size = inputStream.available();
278             byte[] buffer = new byte[size];
279             inputStream.read(buffer);
280             result = new String(buffer);
281         } finally {
282             if (inputStream != null) {
283                 inputStream.close();
284             }
285         }
286         return result;
287     }
288
289     private Object createExtensionContextClient(XWalkExtensionContextWrapper contextWrapper) {
290         Activity activity = contextWrapper.getActivity();
291         try {
292             Class<?> clazz = activity.getClassLoader().loadClass(EXTENSION_CONTEXT_CLIENT_CLASS_NAME);
293             Constructor<?> constructor = clazz.getConstructor(Activity.class, Object.class);
294             return constructor.newInstance(activity, contextWrapper);
295         } catch (ClassNotFoundException e) {
296             handleException(e);
297         } catch (IllegalAccessException e) {
298             handleException(e);
299         } catch (InstantiationException e) {
300             handleException(e);
301         } catch (InvocationTargetException e) {
302             handleException(e);
303         } catch (NoSuchMethodException e) {
304             handleException(e);
305         }
306         return null;
307     }
308
309     private void createExternalExtension(String name, String className, String jsApi,
310             Object contextClient, XWalkExtensionContextWrapper contextWrapper) {
311         Activity activity = contextWrapper.getActivity();
312         try {
313             Class<?> clazz = activity.getClassLoader().loadClass(className);
314             Constructor<?> constructor = clazz.getConstructor(String.class,
315                     String.class, contextClient.getClass());
316             constructor.newInstance(name, jsApi, contextClient);
317         } catch (ClassNotFoundException e) {
318             handleException(e);
319         } catch (IllegalAccessException e) {
320             handleException(e);
321         } catch (InstantiationException e) {
322             handleException(e);
323         } catch (InvocationTargetException e) {
324             handleException(e);
325         } catch (NoSuchMethodException e) {
326             handleException(e);
327         }
328     }
329
330     private static void handleException(Exception e) {
331         // TODO(yongsheng): Handle exceptions here.
332         Log.e(TAG, "Error in calling methods of external extensions. " + e.toString());
333     }
334 }