source: trunk/src/org/expeditee/auth/AuthenticatorBrowser.java@ 1434

Last change on this file since 1434 was 1434, checked in by bln4, 5 years ago

Implementation of ProfileManager. Refactor + additional content for how new profiles are created. The refactoring split out the creation of the default profile from user profiles. Refactoring revealed a long term bug that was causing user profiles to generate with incorrect information. The additional content fixed this bug by introducing the ${USER.NAME} variable, so that the default profile frameset can specify resource locations located in the users resource directory.

org.expeditee.auth.AuthenticatorBrowser
org.expeditee.auth.account.Create
org.expeditee.gui.Browser
org.expeditee.gui.management.ProfileManager
org.expeditee.setting.DirectoryListSetting
org.expeditee.setting.ListSetting
org.expeditee.settings.UserSettings

Implementation of ResourceManager as a core location to get resources from the file system. Also the additional variable ${CURRENT_FRAMESET} to represent the current frameset, so that images can be stored in the directory of the current frameset. This increases portability of framesets.

org.expeditee.gui.FrameIO
org.expeditee.gui.management.ResourceManager
org.expeditee.gui.management.ResourceUtil
Audio:

#NB: Audio used to only operate on a single directory. This has been updated to work in a same way as images. That is: when you ask for a specific resouce, it looks to the user settings to find a sequence of directories to look at in order until it manages to find the desired resource.


There is still need however for a single(ish) source of truth for the .banks and .mastermix file. Therefore these files are now always located in resource-<username>\audio.
org.apollo.agents.MelodySearch
org.apollo.audio.structure.AudioStructureModel
org.apollo.audio.util.MultiTrackPlaybackController
org.apollo.audio.util.SoundDesk
org.apollo.gui.FrameLayoutDaemon
org.apollo.io.AudioPathManager
org.apollo.util.AudioPurger
org.apollo.widgets.FramePlayer
org.apollo.widgets.SampledTrack

Images:

org.expeditee.items.ItemUtils

Frames:

org.expeditee.gui.FrameIO

Fixed a error in the FramePlayer class caused by an incorrect use of toArray().

org.apollo.widgets.FramePlayer


Added several short cut keys to allow for the Play/Pause (Ctrl + P), mute (Ctrl + M) and volume up/down (Ctrl + +/-) when hovering over SampledTrack widgets.

org.apollo.widgets.SampledTrack


Changed the way that Authenticate.login parses the new users profile to be more consistance with other similar places in code.

org.expeditee.auth.account.Authenticate


Encapsulated _body, _surrogateItemsBody and _primaryItemsBody in Frame class. Also changed getBody function to take a boolean flag as to if it should respect the current surrogate mode. If it should then it makes sure that labels have not changed since last time getBody was called.

org.expeditee.gui.Frame

File size: 20.3 KB
Line 
1package org.expeditee.auth;
2
3import java.io.File;
4import java.io.FileFilter;
5import java.io.FileInputStream;
6import java.io.FileNotFoundException;
7import java.io.FileOutputStream;
8import java.io.FileWriter;
9import java.io.IOException;
10import java.io.InputStream;
11import java.nio.file.Path;
12import java.nio.file.Paths;
13import java.security.KeyFactory;
14import java.security.KeyStore;
15import java.security.KeyStore.SecretKeyEntry;
16import java.security.KeyStoreException;
17import java.security.NoSuchAlgorithmException;
18import java.security.PublicKey;
19import java.security.SecureRandom;
20import java.security.UnrecoverableEntryException;
21import java.security.cert.CertificateException;
22import java.security.spec.InvalidKeySpecException;
23import java.security.spec.X509EncodedKeySpec;
24import java.sql.Connection;
25import java.sql.DriverManager;
26import java.sql.PreparedStatement;
27import java.sql.ResultSet;
28import java.sql.SQLException;
29import java.text.ParseException;
30import java.text.SimpleDateFormat;
31import java.util.Arrays;
32import java.util.Base64;
33import java.util.Collection;
34import java.util.Date;
35import java.util.HashMap;
36import java.util.HashSet;
37import java.util.Map;
38import java.util.Scanner;
39import java.util.Set;
40import java.util.stream.Stream;
41
42import javax.crypto.SecretKey;
43import javax.crypto.spec.SecretKeySpec;
44
45import org.expeditee.actions.Actions;
46import org.expeditee.auth.mail.Mail;
47import org.expeditee.core.Dimension;
48import org.expeditee.core.Point;
49import org.expeditee.encryption.CryptographyConstants;
50import org.expeditee.gio.EcosystemManager;
51import org.expeditee.gio.GraphicsManager;
52import org.expeditee.gio.InputManager;
53import org.expeditee.gio.InputManager.WindowEventListener;
54import org.expeditee.gio.InputManager.WindowEventType;
55import org.expeditee.gio.gesture.StandardGestureActions;
56import org.expeditee.gui.Browser;
57import org.expeditee.gui.DisplayController;
58import org.expeditee.gui.Frame;
59import org.expeditee.gui.FrameIO;
60import org.expeditee.gui.FrameUtils;
61import org.expeditee.gui.MessageBay;
62import org.expeditee.io.ExpReader;
63import org.expeditee.items.Item;
64import org.expeditee.items.ItemUtils;
65import org.expeditee.items.Text;
66import org.expeditee.settings.Settings;
67import org.expeditee.settings.UserSettings;
68import org.expeditee.settings.identity.secrets.KeyList;
69import org.expeditee.stats.Formatter;
70
71public final class AuthenticatorBrowser extends Browser implements CryptographyConstants {
72
73 // The frame number of the frame containing the current authenticated users public key.
74 public static int CREDENTIALS_FRAME = -1;
75 public static int PASSWORD_RECOVERY_FRAME = -1;
76 public static int SECRETS_FRAME = -1;
77 public static final String ADMINACCOUNT = "authadmin";
78 public static final String PROFILEENCRYPTIONLABEL = "Profile";
79
80 public static boolean Authenticated = false;
81
82 private KeyStore keyStore = KeyStore.getInstance(KeystoreType);
83 public static String USER_NOBODY = "${USER.NAME}";
84
85 private static final byte[] TRUE = "yes".getBytes();
86 private static final byte[] FALSE = "no".getBytes();
87 private static final String KEYSTOREFILENAME = "keystore.ks" + File.separator;
88
89 private static AuthenticatorBrowser instance;
90
91 public static AuthenticatorBrowser getInstance() throws KeyStoreException, FileNotFoundException, NoSuchAlgorithmException, CertificateException, IOException, ClassNotFoundException, SQLException {
92 if (instance == null) { instance = new AuthenticatorBrowser(); }
93 return instance;
94 }
95
96 public static boolean isAuthenticationRequired() {
97 return Boolean.getBoolean("expeditee.authentication");
98 }
99
100 public static boolean isAuthenticated() {
101 return isAuthenticationRequired() && !UserSettings.UserName.get().equals(AuthenticatorBrowser.USER_NOBODY);
102 }
103
104 private AuthenticatorBrowser() throws KeyStoreException, FileNotFoundException, IOException, NoSuchAlgorithmException, CertificateException, ClassNotFoundException, SQLException {
105 super("Authentication");
106 UserSettings.setupDefaultFolders();
107
108 // initialise keystore and actions
109 loadKeystore();
110 Actions.LoadMethods(org.expeditee.auth.Actions.class);
111 Actions.LoadMethods(org.expeditee.encryption.Actions.class);
112
113 // Does the account Authentication.ADMINACCOUNT exist?
114 // If not then we have get the user to assign a password to it.
115 if (!keyStore.containsAlias(AuthenticatorBrowser.ADMINACCOUNT)) {
116 new File(FrameIO.PARENT_FOLDER).mkdirs();
117 protectAdmin();
118 }
119
120 // draw the window
121 GraphicsManager g = EcosystemManager.getGraphicsManager();
122 g.setWindowLocation(new Point(50, 50));
123 DisplayController.Init();
124 g.setWindowSize(new Dimension(UserSettings.InitialWidth.get(), UserSettings.InitialHeight.get()));
125 setInputManagerWindowRoutines();
126
127 // Load documentation and start pages
128 FrameUtils.extractResources(false);
129
130 // Load fonts before loading any frames so the items on the frames will be able to access their fonts
131 Text.InitFonts();
132
133 // initialing settings does not require a user profile established
134 Settings.Init();
135
136 // Ensure messages go to public directory whilst we are not logged into an account.
137 FrameIO.MESSAGES_PATH = Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-public").resolve("messages").toString() + File.separator;
138
139 // navigate to authentication frame
140 Frame authFrame = FrameIO.LoadFrame("authentication1");
141 DisplayController.setCurrentFrame(authFrame, true);
142
143 // set initial values
144 Stream<Text> usernameItemsStream = authFrame.getTextItems().stream().filter(t -> t.getData() != null && t.getData().contains("txtUsername"));
145 Stream<Text> passwordItemsStream = authFrame.getTextItems().stream().filter(t -> t.getData() != null && t.getData().contains("txtPassword"));
146 usernameItemsStream.forEach(txtUsername -> txtUsername.setText(System.getProperty("startinguser.name", "")));
147 passwordItemsStream.forEach(txtPassword -> { txtPassword.setText(""); txtPassword.invalidateAll(); });
148
149 MessageBay.warningMessages(org.expeditee.actions.Actions.Init());
150
151 // class load database classes
152 Class.forName("org.sqlite.JDBC");
153 }
154
155 private void protectAdmin() throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
156 FileNotFoundException, IOException {
157 // Fetch desired password
158 @SuppressWarnings("resource")
159 Scanner in = new Scanner(System.in);
160 System.out.println("No administrative password set.");
161 boolean passwordIsSet = false;
162
163 for (int i = 0; i < 3; i++) {
164 System.out.print("Please enter it now: ");
165 System.out.flush();
166 String password = in.nextLine();
167 System.out.print("And again: ");
168 System.out.flush();
169 if (in.nextLine().equals(password)) {
170 // Register account.
171 putKey(ADMINACCOUNT, password, new SecretKeySpec("null".getBytes(), AsymmetricAlgorithm));
172 //in.close();
173 passwordIsSet = true;
174 break;
175 } else {
176 System.out.println("Mismatched passwords, let's try that again.");
177 }
178 }
179
180 if (!passwordIsSet) {
181 System.out.println("Failed to set an admin password. Exiting Expeditee.");
182 System.exit(1);
183 }
184 }
185
186 private void loadKeystore()
187 throws IOException, NoSuchAlgorithmException, CertificateException, FileNotFoundException {
188 final File keyStoreFile = new File(FrameIO.PARENT_FOLDER + KEYSTOREFILENAME);
189 if (!keyStoreFile.exists()) {
190 keyStore.load(null, "ExpediteeAuthPassword".toCharArray());
191 } else {
192 try (final InputStream in = new FileInputStream(FrameIO.PARENT_FOLDER + KEYSTOREFILENAME)) {
193 keyStore.load(in, "ExpediteeAuthPassword".toCharArray());
194 }
195 }
196 }
197
198 final void loadMailFromFile(Path dbFile) throws SQLException {
199 // Load in all mail.
200 Connection c = DriverManager.getConnection("jdbc:sqlite:" + dbFile.toAbsolutePath().toString());
201 String sql = "SELECT * FROM EXPMAIL";
202 PreparedStatement query = c.prepareStatement(sql);
203 ResultSet allMail = query.executeQuery();
204
205 // Construct all mail objects using content from database.
206 while(allMail.next()) {
207 String timestamp = allMail.getString("time");
208 String from = allMail.getString("snd");
209 String to = allMail.getString("rec");
210 String msg = allMail.getString("msg");
211 String msg2 = allMail.getString("msg2");
212 String[] opts = allMail.getString("opts").split(",");
213 opts[0] = opts[0].replace("[", "");
214 opts[opts.length - 1] = opts[opts.length - 1].replace("]", "");
215 String[] optsVal = allMail.getString("optsval").split(",");
216 optsVal[0] = optsVal[0].replace("[", "");
217 optsVal[optsVal.length - 1] = optsVal[optsVal.length - 1].replace("]", "");
218
219 Map<String, String> options = new HashMap<String, String>();
220 for (int i = 0, o = 0; i < opts.length && o < optsVal.length; i++, o++) {
221 String key = opts[i].trim();
222 String val = optsVal[o].trim();
223 options.put(key, val);
224 }
225
226 Mail.MailEntry mail = new Mail.MailEntry(timestamp, from, to, msg, msg2, options);
227 mail.deadDropSource = dbFile;
228 Mail.addEntry(mail);
229 }
230
231 // Disconnect from database.
232 allMail.close();
233 query.close();
234 c.close();
235 }
236
237 public final void loadMailDatabase() throws SQLException, FileNotFoundException, ParseException {
238 Path deadDropPath = Paths.get(FrameIO.DEAD_DROPS_PATH);
239 for (File connectionDir: deadDropPath.toFile().listFiles()) {
240 if (connectionDir.isDirectory()) {
241 Path deaddropforcontactPath = Paths.get(connectionDir.getAbsolutePath());
242 Path dbFile = deaddropforcontactPath.resolve(UserSettings.UserName.get() + ".db");
243 if (dbFile.toFile().exists()) {
244 loadMailFromFile(dbFile);
245 }
246 clearOldMailFromDatabase(deaddropforcontactPath);
247 }
248 }
249 }
250
251 public final void updateLastReadMailTime(Path deaddropforcontactPath) {
252 Path timestamp = deaddropforcontactPath.resolve(UserSettings.UserName.get() + ".last-accessed");
253 try(FileWriter out = new FileWriter(timestamp.toFile())) {
254 out.write(Formatter.getDateTime() + System.getProperty("line.separator"));
255 } catch (IOException e) {
256 e.printStackTrace();
257 }
258 }
259
260 private void clearOldMailFromDatabase(Path directory) throws FileNotFoundException, ParseException, SQLException {
261 File[] files = directory.toFile().listFiles(new FileFilter() {
262 @Override
263 public boolean accept(File file) {
264 return !file.getName().startsWith(UserSettings.UserName.get());
265 }
266 });
267
268 File dbFile = null;
269 File lastAccessedFile = null;
270 for (File file: files) {
271 if (file.getName().endsWith(".db")) {
272 dbFile = file;
273 } else {
274 lastAccessedFile = file;
275 }
276 }
277
278 if (dbFile == null || lastAccessedFile == null) {
279 return; // Not the end of the world if we cannot clear out old messages, these files may not be present yet if the others are recently new.
280 }
281
282 SimpleDateFormat format = new SimpleDateFormat("ddMMMyyyy[HH:mm]");
283 Date timestamp = null;
284 try(Scanner in = new Scanner(lastAccessedFile)) {
285 timestamp = format.parse(in.nextLine());
286 } catch (ParseException e) {
287 return; // Not the end of the world if we cannot clear out old messages, the database might be empty.
288 }
289
290 Connection c = DriverManager.getConnection("jdbc:sqlite:" + dbFile.getAbsolutePath());
291 String sql = "SELECT * FROM EXPMAIL";
292 PreparedStatement query = c.prepareStatement(sql);
293 ResultSet allMail = query.executeQuery();
294 Set<String> oldTimestamps = new HashSet<String>();
295
296 while (allMail.next()) {
297 String time = allMail.getString("time");
298 Date messageTimestamp = format.parse(time);
299 if (timestamp.after(messageTimestamp)) {
300 oldTimestamps.add(time);
301 }
302 }
303
304 if (oldTimestamps.isEmpty()) {
305 return;
306 }
307
308 for(String oldTimestamp: oldTimestamps) {
309 System.out.println("Deleting message with timestamp: " + oldTimestamp);
310 sql = "DELETE FROM EXPMAIL WHERE time='" + oldTimestamp + "'";
311 query = c.prepareStatement(sql);
312 query.executeUpdate();
313 }
314 }
315
316 public final SecretKey getSecretKey(final String label, final String password) throws NoSuchAlgorithmException, KeyStoreException {
317
318 char[] password_ca = password.toCharArray();
319
320 SecretKey secret_key;
321 try {
322 secret_key = (SecretKey) keyStore.getKey(label, password_ca);
323 } catch (final UnrecoverableEntryException e) {
324 return null;
325 }
326
327 return secret_key;
328 }
329
330 public final void putKey(final String label, final String password, final SecretKey key) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException {
331 final KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(key);
332 final KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(password.toCharArray());
333 keyStore.setEntry(label, entry, entryPassword);
334 keyStore.store(new FileOutputStream(FrameIO.PARENT_FOLDER + KEYSTOREFILENAME), "ExpediteeAuthPassword".toCharArray());
335 }
336
337 public final boolean confirmIntergalaticNumber(final String username, final String intergalacticNumber) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, FileNotFoundException, IOException {
338 try {
339 KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(intergalacticNumber.toCharArray());
340 KeyStore.SecretKeyEntry entry = (SecretKeyEntry) keyStore.getEntry(username + "_IntergalacticNumber", entryPassword);
341 if (entry == null) {
342 return false;
343 } else if (Arrays.equals(entry.getSecretKey().getEncoded(), TRUE)) {
344 keyStore.deleteEntry(username + "_IntergalacticNumber");
345 keyStore.store(new FileOutputStream(FrameIO.PARENT_FOLDER + KEYSTOREFILENAME), "ExpediteeAuthPassword".toCharArray());
346 return true;
347 } else {
348 return false;
349 }
350 } catch (final UnrecoverableEntryException e) {
351 return false;
352 }
353 }
354
355 public final String newIntergalacticNumber(final String username, final String email) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException {
356 // generate intergalactic number
357 SecureRandom rand = new SecureRandom();
358 byte[] intergalacticNumberBytes = new byte[16];
359 rand.nextBytes(intergalacticNumberBytes);
360 String intergalacticNumber = Base64.getEncoder().encodeToString(intergalacticNumberBytes);
361
362 // store intergalactic number
363 KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(new SecretKeySpec(TRUE, SymmetricAlgorithm));
364 KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(intergalacticNumber.toCharArray());
365 keyStore.setEntry(username + "_IntergalacticNumber", entry, entryPassword);
366 keyStore.store(new FileOutputStream(FrameIO.PARENT_FOLDER + KEYSTOREFILENAME), "ExpediteeAuthPassword".toCharArray());
367
368 return intergalacticNumber;
369 }
370
371 public final PublicKey getPublicKey(String username) throws InvalidKeySpecException, NoSuchAlgorithmException, FileNotFoundException {
372 // load in frame with public key on it.
373 String credentialsFramesetPath = FrameIO.CONTACTS_PATH + username + "-credentials" + File.separator;
374 if (!new File(credentialsFramesetPath).exists()) {
375 return null;
376 }
377 Scanner in = new Scanner(new File(credentialsFramesetPath + "credentials.inf"));
378 String credentialsFrameNumber = in.nextLine().replace(ExpReader.EXTENTION, "");
379 in.close();
380 Frame frame = FrameIO.LoadFrame(username + "-credentials" + credentialsFrameNumber, FrameIO.CONTACTS_PATH);
381 if (frame == null) {
382 return null;
383 }
384
385 // obtain public key from frame
386 Collection<Item> canditates = org.expeditee.auth.Actions.getByContent(frame, "PublicKey");
387 String keyEncoded = "";
388 for (Item i: canditates) {
389 if (i.getData() != null) {
390 keyEncoded = i.getData().get(0);
391 }
392 }
393 if (keyEncoded.isEmpty()) {
394 return null;
395 }
396 byte[] keyBytes = Base64.getDecoder().decode(keyEncoded);
397 return KeyFactory.getInstance(AsymmetricAlgorithm).generatePublic(new X509EncodedKeySpec(keyBytes));
398 }
399
400 public final void markRequestedColleagues(String username) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException {
401 KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(new SecretKeySpec(TRUE, SymmetricAlgorithm));
402 KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(KeyList.PersonalKey.get().getText().toCharArray());
403 keyStore.setEntry(username + "colleaguesRequested", entry, entryPassword);
404 keyStore.store(new FileOutputStream(FrameIO.PARENT_FOLDER + KEYSTOREFILENAME), "ExpediteeAuthPassword".toCharArray());
405 }
406
407 public final void clearRequestedColleagues(String username) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException {
408 KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(new SecretKeySpec(FALSE, SymmetricAlgorithm));
409 KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(KeyList.PersonalKey.get().getText().toCharArray());
410 keyStore.setEntry(username + "colleaguesRequested", entry, entryPassword);
411 keyStore.store(new FileOutputStream(FrameIO.PARENT_FOLDER + KEYSTOREFILENAME), "ExpediteeAuthPassword".toCharArray());
412 }
413
414 public final boolean hasRequestedColleagues(String username) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException {
415 String alias = username + "colleaguesRequested";
416 if (!keyStore.containsAlias(alias)) {
417 return false;
418 } else {
419 KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(KeyList.PersonalKey.get().getText().toCharArray());
420 KeyStore.SecretKeyEntry entry = (SecretKeyEntry) keyStore.getEntry(alias, entryPassword);
421 return Arrays.equals(entry.getSecretKey().getEncoded(), TRUE);
422 }
423 }
424
425// final void putColleagues(String username, String[] colleagues) throws KeyStoreException {
426// String alias = username + "colleagues";
427// final SecretKeySpec secretKeySpec = new SecretKeySpec((colleagues[0] + System.getProperty("line.separator") + colleagues[1]).getBytes(), SymmetricAlgorithm);
428// KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(secretKeySpec);
429// KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(KeyList.PersonalKey.get().getText().toCharArray());
430// keyStore.setEntry(alias, entry, entryPassword);
431// }
432//
433// final String[] getColleagues(String username) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException {
434// String alias = username + "colleagues";
435// if (!keyStore.containsAlias(alias)) {
436// return null;
437// } else {
438// KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(KeyList.PersonalKey.get().getText().toCharArray());
439// KeyStore.SecretKeyEntry entry = (SecretKeyEntry) keyStore.getEntry(alias, entryPassword);
440// byte[] colleaguesEncoded = entry.getSecretKey().getEncoded();
441// String colleagues = new String(colleaguesEncoded);
442// return colleagues.split(System.getProperty("line.separator"));
443// }
444// }
445
446 private static void setInputManagerWindowRoutines() {
447 InputManager manager = EcosystemManager.getInputManager();
448
449 // Refresh the layout when the window resizes
450 manager.addWindowEventListener(new WindowEventListener() {
451 @Override
452 public void onWindowEvent(WindowEventType type)
453 {
454 if (type != WindowEventType.WINDOW_RESIZED) {
455 return;
456 }
457 DisplayController.refreshWindowSize();
458 FrameIO.RefreshCacheImages();
459 for (Frame frame : DisplayController.getFrames()) {
460 if (frame != null) {
461 ItemUtils.Justify(frame);
462 frame.refreshSize();
463 }
464 }
465 DisplayController.requestRefresh(false);
466 }
467 });
468
469 manager.addWindowEventListener(new WindowEventListener() {
470 @Override
471 public void onWindowEvent(WindowEventType type)
472 {
473 if (type != WindowEventType.MOUSE_EXITED_WINDOW) {
474 return;
475 }
476 StandardGestureActions.mouseExitedWindow();
477 }
478 });
479
480 manager.addWindowEventListener(new WindowEventListener() {
481 @Override
482 public void onWindowEvent(WindowEventType type)
483 {
484 if (type != WindowEventType.MOUSE_ENTERED_WINDOW) {
485 return;
486 }
487 StandardGestureActions.mouseEnteredWindow();
488 }
489 });
490
491 manager.addWindowEventListener(new WindowEventListener() {
492 @Override
493 public void onWindowEvent(WindowEventType type)
494 {
495 if (type != WindowEventType.WINDOW_CLOSED) {
496 return;
497 }
498 if (Browser._theBrowser != null) {
499 Browser._theBrowser.exit();
500 }
501 }
502 });
503 }
504}
Note: See TracBrowser for help on using the repository browser.