|
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko.webapp; |
|
7 |
|
8 import java.io.Closeable; |
|
9 import java.io.File; |
|
10 import java.io.FileOutputStream; |
|
11 import java.io.IOException; |
|
12 import java.io.InputStream; |
|
13 import java.io.OutputStream; |
|
14 |
|
15 import org.json.JSONException; |
|
16 import org.json.JSONObject; |
|
17 import org.mozilla.gecko.GeckoAppShell; |
|
18 import org.mozilla.gecko.GeckoEvent; |
|
19 import org.mozilla.gecko.GeckoProfile; |
|
20 import org.mozilla.gecko.gfx.BitmapUtils; |
|
21 import org.mozilla.gecko.util.GeckoEventListener; |
|
22 import org.mozilla.gecko.util.ThreadUtils; |
|
23 |
|
24 import android.content.Context; |
|
25 import android.graphics.Bitmap; |
|
26 import android.net.Uri; |
|
27 import android.util.Log; |
|
28 |
|
29 public class InstallHelper implements GeckoEventListener { |
|
30 private static final String LOGTAG = "GeckoWebappInstallHelper"; |
|
31 private static final String[] INSTALL_EVENT_NAMES = new String[] {"Webapps:Postinstall"}; |
|
32 private final Context mContext; |
|
33 private final InstallCallback mCallback; |
|
34 private final ApkResources mApkResources; |
|
35 |
|
36 public static interface InstallCallback { |
|
37 // on the GeckoThread |
|
38 void installCompleted(InstallHelper installHelper, String event, JSONObject message); |
|
39 |
|
40 // on the GeckoBackgroundThread |
|
41 void installErrored(InstallHelper installHelper, Exception exception); |
|
42 } |
|
43 |
|
44 public InstallHelper(Context context, ApkResources apkResources, InstallCallback cb) { |
|
45 mContext = context; |
|
46 mCallback = cb; |
|
47 mApkResources = apkResources; |
|
48 } |
|
49 |
|
50 public void startInstall(String profileName) throws IOException { |
|
51 startInstall(profileName, null); |
|
52 } |
|
53 |
|
54 public void startInstall(final String profileName, final JSONObject message) throws IOException { |
|
55 ThreadUtils.postToBackgroundThread(new Runnable() { |
|
56 @Override |
|
57 public void run() { |
|
58 try { |
|
59 install(profileName, message); |
|
60 } catch (IOException e) { |
|
61 handleException(e); |
|
62 } |
|
63 } |
|
64 }); |
|
65 } |
|
66 |
|
67 protected void handleException(Exception e) { |
|
68 if (mCallback != null) { |
|
69 mCallback.installErrored(this, e); |
|
70 } else { |
|
71 Log.e(LOGTAG, "mozApps.install failed", e); |
|
72 } |
|
73 } |
|
74 |
|
75 private void install(String profileName, JSONObject message) throws IOException { |
|
76 if (message == null) { |
|
77 message = new JSONObject(); |
|
78 } |
|
79 |
|
80 // we can change the profile to be in the app's area here |
|
81 GeckoProfile profile = GeckoProfile.get(mContext, profileName); |
|
82 |
|
83 try { |
|
84 message.put("apkPackageName", mApkResources.getPackageName()); |
|
85 message.put("manifestURL", mApkResources.getManifestUrl()); |
|
86 message.put("title", mApkResources.getAppName()); |
|
87 message.put("manifest", new JSONObject(mApkResources.getManifest(mContext))); |
|
88 |
|
89 String appType = mApkResources.getWebappType(); |
|
90 message.putOpt("type", appType); |
|
91 if ("packaged".equals(appType)) { |
|
92 message.putOpt("updateManifest", new JSONObject(mApkResources.getMiniManifest(mContext))); |
|
93 } |
|
94 |
|
95 message.putOpt("profilePath", profile.getDir()); |
|
96 |
|
97 if (mApkResources.isPackaged()) { |
|
98 File zipFile = copyApplicationZipFile(); |
|
99 message.putOpt("zipFilePath", Uri.fromFile(zipFile).toString()); |
|
100 } |
|
101 } catch (JSONException e) { |
|
102 handleException(e); |
|
103 return; |
|
104 } |
|
105 |
|
106 registerGeckoListener(); |
|
107 |
|
108 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Webapps:AutoInstall", message.toString())); |
|
109 calculateColor(); |
|
110 } |
|
111 |
|
112 public File copyApplicationZipFile() throws IOException { |
|
113 if (!mApkResources.isPackaged()) { |
|
114 return null; |
|
115 } |
|
116 |
|
117 Uri uri = mApkResources.getZipFileUri(); |
|
118 |
|
119 InputStream in = null; |
|
120 OutputStream out = null; |
|
121 File destPath = new File(mApkResources.getFileDirectory(), "application.zip"); |
|
122 try { |
|
123 in = mContext.getContentResolver().openInputStream(uri); |
|
124 out = new FileOutputStream(destPath); |
|
125 byte[] buffer = new byte[1024]; |
|
126 int read = 0; |
|
127 while ((read = in.read(buffer)) != -1) { |
|
128 out.write(buffer, 0, read); |
|
129 } |
|
130 out.flush(); |
|
131 } catch (IOException e) { |
|
132 throw e; |
|
133 } finally { |
|
134 close(in); |
|
135 close(out); |
|
136 } |
|
137 return destPath; |
|
138 } |
|
139 |
|
140 private static void close(Closeable close) { |
|
141 if (close == null) { |
|
142 return; |
|
143 } |
|
144 try { |
|
145 close.close(); |
|
146 } catch (IOException e) { |
|
147 // NOP |
|
148 } |
|
149 } |
|
150 |
|
151 public void registerGeckoListener() { |
|
152 for (String eventName : INSTALL_EVENT_NAMES) { |
|
153 GeckoAppShell.registerEventListener(eventName, this); |
|
154 } |
|
155 } |
|
156 |
|
157 private void calculateColor() { |
|
158 ThreadUtils.assertOnBackgroundThread(); |
|
159 Allocator slots = Allocator.getInstance(mContext); |
|
160 int index = slots.getIndexForApp(mApkResources.getPackageName()); |
|
161 Bitmap bitmap = BitmapUtils.getBitmapFromDrawable(mApkResources.getAppIcon()); |
|
162 slots.updateColor(index, BitmapUtils.getDominantColor(bitmap)); |
|
163 } |
|
164 |
|
165 @Override |
|
166 public void handleMessage(String event, JSONObject message) { |
|
167 for (String eventName : INSTALL_EVENT_NAMES) { |
|
168 GeckoAppShell.unregisterEventListener(eventName, this); |
|
169 } |
|
170 |
|
171 if (mCallback != null) { |
|
172 mCallback.installCompleted(this, event, message); |
|
173 } |
|
174 } |
|
175 } |