This source file includes following definitions.
- run
- onClick
- onCancel
- readEmacsClientLog
- displayFailureDialog
- checkReadableOrCopy
- finishSuccess
- finishFailure
- startEmacsClient
- onCreate
- onDestroy
- onWindowFocusChanged
- onPause
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.gnu.emacs;
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 import android.app.AlertDialog;
47 import android.app.Activity;
48
49 import android.content.ContentResolver;
50 import android.content.DialogInterface;
51 import android.content.Intent;
52
53 import android.net.Uri;
54
55 import android.os.Build;
56 import android.os.Bundle;
57 import android.os.ParcelFileDescriptor;
58
59 import android.util.Log;
60
61 import java.io.File;
62 import java.io.FileInputStream;
63 import java.io.FileNotFoundException;
64 import java.io.FileOutputStream;
65 import java.io.FileReader;
66 import java.io.IOException;
67 import java.io.InputStream;
68 import java.io.UnsupportedEncodingException;
69
70 public final class EmacsOpenActivity extends Activity
71 implements DialogInterface.OnClickListener,
72 DialogInterface.OnCancelListener
73 {
74 private static final String TAG = "EmacsOpenActivity";
75
76
77
78
79 public static String fileToOpen;
80
81
82
83
84 public static EmacsOpenActivity currentActivity;
85
86 private class EmacsClientThread extends Thread
87 {
88 private ProcessBuilder builder;
89
90 public
91 EmacsClientThread (ProcessBuilder processBuilder)
92 {
93 builder = processBuilder;
94 }
95
96 @Override
97 public void
98 run ()
99 {
100 Process process;
101 InputStream error;
102 String errorText;
103
104 try
105 {
106
107 process = builder.start ();
108 process.waitFor ();
109
110
111
112 if (process.exitValue () == 0)
113 finishSuccess ();
114 else
115 finishFailure ("Error opening file", null);
116 }
117 catch (IOException exception)
118 {
119 finishFailure ("Internal error", exception.toString ());
120 }
121 catch (InterruptedException exception)
122 {
123 finishFailure ("Internal error", exception.toString ());
124 }
125 }
126 }
127
128 @Override
129 public void
130 onClick (DialogInterface dialog, int which)
131 {
132 finish ();
133 }
134
135 @Override
136 public void
137 onCancel (DialogInterface dialog)
138 {
139 finish ();
140 }
141
142 public String
143 readEmacsClientLog ()
144 {
145 File file, cache;
146 FileReader reader;
147 char[] buffer;
148 int rc;
149 StringBuilder builder;
150
151
152
153
154
155 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
156 return ("This is likely because the Emacs server"
157 + " is not running, or because you did"
158 + " not grant Emacs permission to access"
159 + " external storage.");
160
161 cache = getCacheDir ();
162 file = new File (cache, "emacsclient.log");
163 builder = new StringBuilder ();
164 reader = null;
165
166 try
167 {
168 reader = new FileReader (file);
169 buffer = new char[2048];
170
171 while ((rc = reader.read (buffer, 0, 2048)) != -1)
172 builder.append (buffer, 0, rc);
173
174 reader.close ();
175 return builder.toString ();
176 }
177 catch (IOException exception)
178 {
179
180
181 try
182 {
183 if (reader != null)
184 reader.close ();
185 }
186 catch (IOException e)
187 {
188
189 }
190
191 return ("Couldn't read emacsclient.log: "
192 + exception.toString ());
193 }
194 }
195
196 private void
197 displayFailureDialog (String title, String text)
198 {
199 AlertDialog.Builder builder;
200 AlertDialog dialog;
201
202 builder = new AlertDialog.Builder (this);
203 dialog = builder.create ();
204 dialog.setTitle (title);
205
206 if (text == null)
207
208 text = readEmacsClientLog ();
209
210 dialog.setMessage (text);
211 dialog.setButton (DialogInterface.BUTTON_POSITIVE, "OK", this);
212 dialog.setOnCancelListener (this);
213 dialog.show ();
214 }
215
216
217
218
219
220
221
222
223 private String
224 checkReadableOrCopy (String file, ParcelFileDescriptor fd,
225 Uri uri)
226 throws IOException, FileNotFoundException
227 {
228 File inFile;
229 FileOutputStream outStream;
230 InputStream stream;
231 byte buffer[];
232 int read;
233 String content;
234
235 Log.d (TAG, "checkReadableOrCopy: " + file);
236
237 inFile = new File (file);
238
239 if (inFile.canRead ())
240 return file;
241
242 Log.d (TAG, "checkReadableOrCopy: NO");
243
244 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
245 {
246 content = EmacsService.buildContentName (uri);
247 Log.d (TAG, "checkReadableOrCopy: " + content);
248 return content;
249 }
250
251
252 inFile = new File (getCacheDir (), inFile.getName ());
253 buffer = new byte[4098];
254
255
256 outStream = null;
257 stream = null;
258
259 try
260 {
261 outStream = new FileOutputStream (inFile);
262 stream = new FileInputStream (fd.getFileDescriptor ());
263
264 while ((read = stream.read (buffer)) >= 0)
265 outStream.write (buffer, 0, read);
266 }
267 finally
268 {
269
270
271
272
273
274
275 if (stream != null)
276 stream.close ();
277
278 if (outStream != null)
279 outStream.close ();
280 }
281
282 return inFile.getCanonicalPath ();
283 }
284
285
286
287
288
289
290
291 public void
292 finishSuccess ()
293 {
294 runOnUiThread (new Runnable () {
295 @Override
296 public void
297 run ()
298 {
299 Intent intent;
300
301 intent = new Intent (EmacsOpenActivity.this,
302 EmacsActivity.class);
303
304
305 intent.addFlags (Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
306 startActivity (intent);
307
308 EmacsOpenActivity.this.finish ();
309 }
310 });
311 }
312
313
314
315
316
317
318
319
320
321 public void
322 finishFailure (final String title, final String text)
323 {
324 runOnUiThread (new Runnable () {
325 @Override
326 public void
327 run ()
328 {
329 displayFailureDialog (title, text);
330 }
331 });
332 }
333
334 public void
335 startEmacsClient (String fileName)
336 {
337 String libDir;
338 ProcessBuilder builder;
339 Process process;
340 EmacsClientThread thread;
341 File file;
342 Intent intent;
343
344
345
346
347 if (EmacsService.SERVICE == null)
348 {
349 fileToOpen = fileName;
350 intent = new Intent (EmacsOpenActivity.this,
351 EmacsActivity.class);
352 finish ();
353 startActivity (intent);
354 return;
355 }
356
357 libDir = EmacsService.getLibraryDirectory (this);
358 builder = new ProcessBuilder (libDir + "/libemacsclient.so",
359 fileName, "--reuse-frame",
360 "--timeout=10", "--no-wait");
361
362
363
364
365 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
366 {
367 file = new File (getCacheDir (), "emacsclient.log");
368
369
370
371
372 if (file.exists ())
373 file.delete ();
374
375 builder.redirectError (file);
376 }
377
378
379
380
381
382 thread = new EmacsClientThread (builder);
383 thread.start ();
384 }
385
386
387
388
389
390
391
392
393
394
395 @Override
396 public void
397 onCreate (Bundle savedInstanceState)
398 {
399 String action, fileName;
400 Intent intent;
401 Uri uri;
402 ContentResolver resolver;
403 ParcelFileDescriptor fd;
404 byte[] names;
405 String errorBlurb;
406
407 super.onCreate (savedInstanceState);
408
409
410 intent = getIntent ();
411 action = intent.getAction ();
412
413 if (action == null)
414 {
415 finish ();
416 return;
417 }
418
419
420
421 if (action.equals ("android.intent.action.VIEW")
422 || action.equals ("android.intent.action.EDIT")
423 || action.equals ("android.intent.action.PICK"))
424 {
425
426 uri = intent.getData ();
427
428 if (uri == null)
429 {
430 finish ();
431 return;
432 }
433
434
435
436 if (uri.getScheme ().equals ("file"))
437 fileName = uri.getPath ();
438 else
439 {
440 fileName = null;
441
442 if (uri.getScheme ().equals ("content"))
443 {
444
445
446
447
448
449 resolver = getContentResolver ();
450 fd = null;
451
452 try
453 {
454 fd = resolver.openFileDescriptor (uri, "r");
455 names = EmacsNative.getProcName (fd.getFd ());
456
457
458
459 if (names != null)
460 fileName = new String (names, "UTF-8");
461
462 fileName = checkReadableOrCopy (fileName, fd, uri);
463 }
464 catch (FileNotFoundException exception)
465 {
466
467 }
468 catch (IOException exception)
469 {
470
471 }
472
473 if (fd != null)
474 {
475 try
476 {
477 fd.close ();
478 }
479 catch (IOException exception)
480 {
481
482 }
483 }
484 }
485
486 if (fileName == null)
487 {
488 errorBlurb = ("The URI: " + uri + " could not be opened"
489 + ", as it does not encode file name inform"
490 + "ation.");
491 displayFailureDialog ("Error opening file", errorBlurb);
492 return;
493 }
494 }
495
496
497
498
499 currentActivity = this;
500 startEmacsClient (fileName);
501 }
502 else
503 finish ();
504 }
505
506
507
508 @Override
509 public void
510 onDestroy ()
511 {
512 Log.d (TAG, "onDestroy: " + this);
513
514
515
516
517 if (currentActivity == this)
518 this.currentActivity = null;
519
520 super.onDestroy ();
521 }
522
523 @Override
524 public void
525 onWindowFocusChanged (boolean isFocused)
526 {
527 Log.d (TAG, "onWindowFocusChanged: " + this + ", is now focused: "
528 + isFocused);
529
530 if (isFocused)
531 currentActivity = this;
532 else if (currentActivity == this)
533 currentActivity = null;
534
535 super.onWindowFocusChanged (isFocused);
536 }
537
538 @Override
539 public void
540 onPause ()
541 {
542 Log.d (TAG, "onPause: " + this);
543
544
545
546
547 if (currentActivity == this)
548 currentActivity = null;
549
550 super.onPause ();
551 }
552 }