mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-01-06 20:00:46 -08:00
* configure.ac (ANDROID_STUBIFY): Add androidvfs.o when building libemacs.so. * doc/emacs/android.texi (Android): Add `Android Document Providers'. (Android Startup): Update the location of the content identifier directory. (Android File System): Describe access to document provider directories. (Android Document Providers): New node. * doc/emacs/emacs.texi (Top): Update the menu for the Android appendix. * java/Makefile.in (filename, install_temp/assets/build_info): Make directory-tree depend on build_info. * java/org/gnu/emacs/EmacsActivity.java (onActivityResult): New function. When a document tree is accepted, persist access to it. * java/org/gnu/emacs/EmacsDirectoryEntry.java (EmacsDirectoryEntry): New struct. * java/org/gnu/emacs/EmacsOpenActivity.java (checkReadableOrCopy): Use EmacsService.buildContentName. * java/org/gnu/emacs/EmacsService.java (getEmacsView, openContentUri) (checkContentUri): Remove excessive debug logging. (buildContentName, getDocumentAuthorities, requestDirectoryAccess) (getDocumentTrees, decodeFileName, documentIdFromName, getTreeUri) (statDocument, accessDocument, openDocumentDirectory, readDirectoryEntry) (openDocument, createDocument): New functions. * lib-src/asset-directory-tool.c: Improve commentary by illustrating the difference between directory and ordinary files. * src/android.c (ANDROID_THROW, enum android_fd_table_entry_flags) (struct android_emacs_service, android_extract_long) (android_scan_directory_tree, android_is_directory) (android_get_asset_name, android_url_encode, android_content_name_p) (android_get_content_name, android_check_content_access, android_fstat) (android_fstatat, android_file_access_p, android_hack_asset_fd_fallback) (android_detect_ashmem, android_hack_asset_fd, android_close_on_exec) (android_open, android_close, android_fclose, android_create_lib_link) (android_faccessat, struct android_dir, android_opendir, android_dirfd) (android_readdir, android_closedir, android_lookup_asset_directory_fd) (android_exception_check_3, android_get_current_api_level) (android_open_asset, android_close_asset, android_asset_read_quit) (android_asset_read, android_asset_lseek, android_asset_fstat): Move content and asset related functions to androidvfs.c. (android_init_emacs_service): Obtain handles for new JNI functions. (initEmacsParams): Initialize the VFS layer. (android_request_directory_access): New function. (android_display_toast): Remove unused function. * src/android.h (android_get_current_api_level): Assume that this function never returns less than __ANDROID_API__. (struct android_emacs_service): Move `struct android_emacs_service' here. * src/androidfns.c (Fandroid_request_directory_access): New interactive function. (syms_of_androidfns): Register new subr. * src/androidvfs.c (struct android_vdir, struct android_vops) (struct android_vnode, struct android_special_vnode) (enum android_vnode_type, struct android_cursor_class) (struct emacs_directory_entry_class) (struct android_parcel_file_descriptor_class) (android_init_cursor_class, android_init_entry_class) (android_init_fd_class, android_vfs_canonicalize_name) (struct android_unix_vnode, struct android_unix_vdir, unix_vfs_ops) (android_unix_name, android_unix_vnode, android_unix_open) (android_unix_close, android_unix_unlink, android_unix_symlink) (android_unix_rmdir, android_unix_rename, android_unix_stat) (android_unix_access, android_unix_mkdir, android_unix_readdir) (android_unix_closedir, android_unix_dirfd, android_unix_opendir) (android_extract_long, android_scan_directory_tree) (android_is_directory, android_init_assets) (android_hack_asset_fd_fallback, android_detect_ashmem) (android_hack_asset_fd, struct android_afs_vnode) (struct android_afs_vdir, struct android_afs_open_fd, afs_vfs_ops) (android_afs_name, android_afs_initial, android_close_on_exec) (android_afs_open, android_afs_close, android_afs_unlink) (android_afs_symlink, android_afs_rmdir, android_afs_rename) (android_afs_stat, android_afs_access, android_afs_mkdir) (android_afs_readdir, android_afs_closedir, android_afs_dirfd) (android_afs_opendir, android_afs_get_directory_name) (struct android_content_vdir, content_vfs_ops) (content_directory_contents, android_content_name) (android_content_open, android_content_close) (android_content_unlink, android_content_symlink) (android_content_rmdir, android_content_rename) (android_content_stat, android_content_access) (android_content_mkdir, android_content_readdir) (android_content_closedir, android_content_dirfd) (android_content_opendir, android_content_get_directory_name) (android_content_initial, android_get_content_name) (android_check_content_access, struct android_authority_vnode) (authority_vfs_ops, android_authority_name, android_authority_open) (android_authority_close, android_authority_unlink) (android_authority_symlink, android_authority_rmdir) (android_authority_rename, android_authority_stat) (android_authority_access, android_authority_mkdir) (android_authority_opendir, android_authority_initial) (struct android_saf_root_vnode, struct android_saf_root_vdir) (saf_root_vfs_ops, android_saf_root_name, android_saf_root_open) (android_saf_root_close, android_saf_root_unlink) (android_saf_root_symlink, android_saf_root_rmdir) (android_saf_root_rename, android_saf_root_stat) (android_saf_root_access, android_saf_root_mkdir) (android_saf_root_readdir, android_saf_root_closedir) (android_saf_root_dirfd, android_saf_root_opendir) (android_saf_root_initial, android_saf_root_get_directory) (android_saf_stat, android_saf_access) (struct android_saf_tree_vnode, struct android_saf_tree_vdir) (saf_tree_vfs_ops, android_document_id_from_name) (android_saf_tree_name, android_saf_tree_open) (android_saf_tree_close, android_saf_tree_unlink) (android_saf_tree_symlink, android_saf_tree_rmdir) (android_saf_tree_rename, android_saf_tree_stat) (android_saf_tree_access, android_saf_tree_mkdir) (android_saf_tree_opendir_1, android_saf_tree_readdir) (android_saf_tree_closedir, android_saf_tree_dirfd) (android_saf_tree_opendir, android_saf_tree_from_name) (android_saf_tree_get_directory, android_saf_file_vnode) (saf_file_vfs_ops, android_saf_file_name, android_saf_file_open) (android_saf_file_unlink, android_saf_file_rmdir) (android_saf_file_opendir, android_close_parcel_fd) (android_saf_new_vnode, android_saf_new_name, android_saf_new_open) (android_saf_new_unlink, android_saf_new_symlink) (android_saf_new_rmdir, android_saf_new_rename) (android_saf_new_stat, android_saf_new_access) (android_saf_new_mkdir, android_saf_new_opendir, root_vfs_ops) (special_vnodes, android_root_name, android_name_file) (android_vfs_init, android_open, android_unlink, android_symlink) (android_rmdir, android_mkdir, android_renameat_noreplace) (android_rename, android_fstat, android_fstatat_1, android_fstatat) (android_faccessat, android_fdopen, android_close, android_fclose) (android_open_asset, android_close_asset, android_asset_read_quit) (android_asset_read, android_asset_lseek, android_asset_fstat) (android_opendir, android_dirfd, android_readdir) (android_closedir): Move file system emulation routines here. Introduce a new ``VFS'' layer for translating between Emacs-specific file names and the various disparate interfaces for accessing files on Android. * src/callproc.c (delete_temp_file): * src/charset.c (load_charset_map_from_file): * src/dired.c: * src/emacs.c (Fkill_emacs): * src/fileio.c (check_mutable_filename, Fcopy_file) (Fmake_directory_internal, Fdelete_directory_internal) (Fdelete_file, Frename_file, Fadd_name_to_file) (Fmake_symbolic_link, file_accessible_directory_p, Fset_file_modes) (Fset_file_times, write_region): * src/filelock.c (get_boot_time, rename_lock_file) (create_lock_file, current_lock_owner, unlock_file): * src/image.c (slurp_file, png_load_body, jpeg_load_body): * src/keyboard.c (Fopen_dribble_file): * src/lisp.h: * src/lread.c (Fload): * src/process.c (handle_child_signal): * src/sysdep.c (init_standard_fds, emacs_fopen, emacs_fdopen) (emacs_unlink, emacs_symlink, emacs_rmdir, emacs_mkdir) (emacs_renameat_noreplace, emacs_rename): * src/term.c (Fresume_tty, init_tty): Use and add new wrappers for fopen, fdopen, unlink, symlink, rmdir, mkdir, renameat_norepalce and rename.
552 lines
13 KiB
Java
552 lines
13 KiB
Java
/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
|
||
|
||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||
|
||
This file is part of GNU Emacs.
|
||
|
||
GNU Emacs is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation, either version 3 of the License, or (at
|
||
your option) any later version.
|
||
|
||
GNU Emacs is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||
|
||
package org.gnu.emacs;
|
||
|
||
/* This class makes the Emacs server work reasonably on Android.
|
||
|
||
There is no way to make the Unix socket publicly available on
|
||
Android.
|
||
|
||
Instead, this activity tries to connect to the Emacs server, to
|
||
make it open files the system asks Emacs to open, and to emulate
|
||
some reasonable behavior when Emacs has not yet started.
|
||
|
||
First, Emacs registers itself as an application that can open text
|
||
and image files.
|
||
|
||
Then, when the user is asked to open a file and selects ``Emacs''
|
||
as the application that will open the file, the system pops up a
|
||
window, this activity, and calls the `onCreate' function.
|
||
|
||
`onCreate' then tries very to find the file name of the file that
|
||
was selected, and give it to emacsclient.
|
||
|
||
If emacsclient successfully opens the file, then this activity
|
||
starts EmacsActivity (to bring it on to the screen); otherwise, it
|
||
displays the output of emacsclient or any error message that occurs
|
||
and exits. */
|
||
|
||
import android.app.AlertDialog;
|
||
import android.app.Activity;
|
||
|
||
import android.content.ContentResolver;
|
||
import android.content.DialogInterface;
|
||
import android.content.Intent;
|
||
|
||
import android.net.Uri;
|
||
|
||
import android.os.Build;
|
||
import android.os.Bundle;
|
||
import android.os.ParcelFileDescriptor;
|
||
|
||
import android.util.Log;
|
||
|
||
import java.io.File;
|
||
import java.io.FileInputStream;
|
||
import java.io.FileNotFoundException;
|
||
import java.io.FileOutputStream;
|
||
import java.io.FileReader;
|
||
import java.io.IOException;
|
||
import java.io.InputStream;
|
||
import java.io.UnsupportedEncodingException;
|
||
|
||
public final class EmacsOpenActivity extends Activity
|
||
implements DialogInterface.OnClickListener,
|
||
DialogInterface.OnCancelListener
|
||
{
|
||
private static final String TAG = "EmacsOpenActivity";
|
||
|
||
/* The name of any file that should be opened as EmacsThread starts
|
||
Emacs. This is never cleared, even if EmacsOpenActivity is
|
||
started a second time, as EmacsThread only starts once. */
|
||
public static String fileToOpen;
|
||
|
||
/* Any currently focused EmacsOpenActivity. Used to show pop ups
|
||
while the activity is active and Emacs doesn't have permission to
|
||
display over other programs. */
|
||
public static EmacsOpenActivity currentActivity;
|
||
|
||
private class EmacsClientThread extends Thread
|
||
{
|
||
private ProcessBuilder builder;
|
||
|
||
public
|
||
EmacsClientThread (ProcessBuilder processBuilder)
|
||
{
|
||
builder = processBuilder;
|
||
}
|
||
|
||
@Override
|
||
public void
|
||
run ()
|
||
{
|
||
Process process;
|
||
InputStream error;
|
||
String errorText;
|
||
|
||
try
|
||
{
|
||
/* Start emacsclient. */
|
||
process = builder.start ();
|
||
process.waitFor ();
|
||
|
||
/* Now figure out whether or not starting the process was
|
||
successful. */
|
||
if (process.exitValue () == 0)
|
||
finishSuccess ();
|
||
else
|
||
finishFailure ("Error opening file", null);
|
||
}
|
||
catch (IOException exception)
|
||
{
|
||
finishFailure ("Internal error", exception.toString ());
|
||
}
|
||
catch (InterruptedException exception)
|
||
{
|
||
finishFailure ("Internal error", exception.toString ());
|
||
}
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void
|
||
onClick (DialogInterface dialog, int which)
|
||
{
|
||
finish ();
|
||
}
|
||
|
||
@Override
|
||
public void
|
||
onCancel (DialogInterface dialog)
|
||
{
|
||
finish ();
|
||
}
|
||
|
||
public String
|
||
readEmacsClientLog ()
|
||
{
|
||
File file, cache;
|
||
FileReader reader;
|
||
char[] buffer;
|
||
int rc;
|
||
StringBuilder builder;
|
||
|
||
/* Because the ProcessBuilder functions necessary to redirect
|
||
process output are not implemented on Android 7 and earlier,
|
||
print a generic error message. */
|
||
|
||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||
return ("This is likely because the Emacs server"
|
||
+ " is not running, or because you did"
|
||
+ " not grant Emacs permission to access"
|
||
+ " external storage.");
|
||
|
||
cache = getCacheDir ();
|
||
file = new File (cache, "emacsclient.log");
|
||
builder = new StringBuilder ();
|
||
reader = null;
|
||
|
||
try
|
||
{
|
||
reader = new FileReader (file);
|
||
buffer = new char[2048];
|
||
|
||
while ((rc = reader.read (buffer, 0, 2048)) != -1)
|
||
builder.append (buffer, 0, rc);
|
||
|
||
reader.close ();
|
||
return builder.toString ();
|
||
}
|
||
catch (IOException exception)
|
||
{
|
||
/* Close the reader if it's already been opened. */
|
||
|
||
try
|
||
{
|
||
if (reader != null)
|
||
reader.close ();
|
||
}
|
||
catch (IOException e)
|
||
{
|
||
/* Not sure what to do here. */
|
||
}
|
||
|
||
return ("Couldn't read emacsclient.log: "
|
||
+ exception.toString ());
|
||
}
|
||
}
|
||
|
||
private void
|
||
displayFailureDialog (String title, String text)
|
||
{
|
||
AlertDialog.Builder builder;
|
||
AlertDialog dialog;
|
||
|
||
builder = new AlertDialog.Builder (this);
|
||
dialog = builder.create ();
|
||
dialog.setTitle (title);
|
||
|
||
if (text == null)
|
||
/* Read in emacsclient.log instead. */
|
||
text = readEmacsClientLog ();
|
||
|
||
dialog.setMessage (text);
|
||
dialog.setButton (DialogInterface.BUTTON_POSITIVE, "OK", this);
|
||
dialog.setOnCancelListener (this);
|
||
dialog.show ();
|
||
}
|
||
|
||
/* Check that the specified FILE is readable. If Android 4.4 or
|
||
later is being used, return URI formatted into a `/content/' file
|
||
name.
|
||
|
||
If it is not, then copy the file in FD to a location in the
|
||
system cache directory and return the name of that file. */
|
||
|
||
private String
|
||
checkReadableOrCopy (String file, ParcelFileDescriptor fd,
|
||
Uri uri)
|
||
throws IOException, FileNotFoundException
|
||
{
|
||
File inFile;
|
||
FileOutputStream outStream;
|
||
InputStream stream;
|
||
byte buffer[];
|
||
int read;
|
||
String content;
|
||
|
||
Log.d (TAG, "checkReadableOrCopy: " + file);
|
||
|
||
inFile = new File (file);
|
||
|
||
if (inFile.canRead ())
|
||
return file;
|
||
|
||
Log.d (TAG, "checkReadableOrCopy: NO");
|
||
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
|
||
{
|
||
content = EmacsService.buildContentName (uri);
|
||
Log.d (TAG, "checkReadableOrCopy: " + content);
|
||
return content;
|
||
}
|
||
|
||
/* inFile is now the file being written to. */
|
||
inFile = new File (getCacheDir (), inFile.getName ());
|
||
buffer = new byte[4098];
|
||
|
||
/* Initialize both streams to NULL. */
|
||
outStream = null;
|
||
stream = null;
|
||
|
||
try
|
||
{
|
||
outStream = new FileOutputStream (inFile);
|
||
stream = new FileInputStream (fd.getFileDescriptor ());
|
||
|
||
while ((read = stream.read (buffer)) >= 0)
|
||
outStream.write (buffer, 0, read);
|
||
}
|
||
finally
|
||
{
|
||
/* Note that this does not close FD.
|
||
|
||
Keep in mind that execution is transferred to ``finally''
|
||
even if an exception happens inside the while loop
|
||
above. */
|
||
|
||
if (stream != null)
|
||
stream.close ();
|
||
|
||
if (outStream != null)
|
||
outStream.close ();
|
||
}
|
||
|
||
return inFile.getCanonicalPath ();
|
||
}
|
||
|
||
/* Finish this activity in response to emacsclient having
|
||
successfully opened a file.
|
||
|
||
In the main thread, close this window, and open a window
|
||
belonging to an Emacs frame. */
|
||
|
||
public void
|
||
finishSuccess ()
|
||
{
|
||
runOnUiThread (new Runnable () {
|
||
@Override
|
||
public void
|
||
run ()
|
||
{
|
||
Intent intent;
|
||
|
||
intent = new Intent (EmacsOpenActivity.this,
|
||
EmacsActivity.class);
|
||
|
||
/* This means only an existing frame will be displayed. */
|
||
intent.addFlags (Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
|
||
startActivity (intent);
|
||
|
||
EmacsOpenActivity.this.finish ();
|
||
}
|
||
});
|
||
}
|
||
|
||
/* Finish this activity after displaying a dialog associated with
|
||
failure to open a file.
|
||
|
||
Use TITLE as the title of the dialog. If TEXT is non-NULL,
|
||
display that text in the dialog. Otherwise, use the contents of
|
||
emacsclient.log in the cache directory instead, or describe why
|
||
that file cannot be read. */
|
||
|
||
public void
|
||
finishFailure (final String title, final String text)
|
||
{
|
||
runOnUiThread (new Runnable () {
|
||
@Override
|
||
public void
|
||
run ()
|
||
{
|
||
displayFailureDialog (title, text);
|
||
}
|
||
});
|
||
}
|
||
|
||
public void
|
||
startEmacsClient (String fileName)
|
||
{
|
||
String libDir;
|
||
ProcessBuilder builder;
|
||
Process process;
|
||
EmacsClientThread thread;
|
||
File file;
|
||
Intent intent;
|
||
|
||
/* If the Emacs service is not running, then start Emacs and make
|
||
it open this file. */
|
||
|
||
if (EmacsService.SERVICE == null)
|
||
{
|
||
fileToOpen = fileName;
|
||
intent = new Intent (EmacsOpenActivity.this,
|
||
EmacsActivity.class);
|
||
finish ();
|
||
startActivity (intent);
|
||
return;
|
||
}
|
||
|
||
libDir = EmacsService.getLibraryDirectory (this);
|
||
builder = new ProcessBuilder (libDir + "/libemacsclient.so",
|
||
fileName, "--reuse-frame",
|
||
"--timeout=10", "--no-wait");
|
||
|
||
/* Redirection is unfortunately not possible in Android 7 and
|
||
earlier. */
|
||
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||
{
|
||
file = new File (getCacheDir (), "emacsclient.log");
|
||
|
||
/* Redirect standard error to a file so that errors can be
|
||
meaningfully reported. */
|
||
|
||
if (file.exists ())
|
||
file.delete ();
|
||
|
||
builder.redirectError (file);
|
||
}
|
||
|
||
/* Track process output in a new thread, since this is the UI
|
||
thread and doing so here can cause deadlocks when EmacsService
|
||
decides to wait for something. */
|
||
|
||
thread = new EmacsClientThread (builder);
|
||
thread.start ();
|
||
}
|
||
|
||
/* Run emacsclient to open the file specified in the Intent that
|
||
caused this activity to start.
|
||
|
||
Determine the name of the file corresponding to the URI specified
|
||
in that intent; then, run emacsclient and wait for it to finish.
|
||
|
||
Finally, display any error message, transfer the focus to an
|
||
Emacs frame, and finish the activity. */
|
||
|
||
@Override
|
||
public void
|
||
onCreate (Bundle savedInstanceState)
|
||
{
|
||
String action, fileName;
|
||
Intent intent;
|
||
Uri uri;
|
||
ContentResolver resolver;
|
||
ParcelFileDescriptor fd;
|
||
byte[] names;
|
||
String errorBlurb;
|
||
|
||
super.onCreate (savedInstanceState);
|
||
|
||
/* Obtain the intent that started Emacs. */
|
||
intent = getIntent ();
|
||
action = intent.getAction ();
|
||
|
||
if (action == null)
|
||
{
|
||
finish ();
|
||
return;
|
||
}
|
||
|
||
/* Now see if the action specified is supported by Emacs. */
|
||
|
||
if (action.equals ("android.intent.action.VIEW")
|
||
|| action.equals ("android.intent.action.EDIT")
|
||
|| action.equals ("android.intent.action.PICK"))
|
||
{
|
||
/* Obtain the URI of the action. */
|
||
uri = intent.getData ();
|
||
|
||
if (uri == null)
|
||
{
|
||
finish ();
|
||
return;
|
||
}
|
||
|
||
/* Now, try to get the file name. */
|
||
|
||
if (uri.getScheme ().equals ("file"))
|
||
fileName = uri.getPath ();
|
||
else
|
||
{
|
||
fileName = null;
|
||
|
||
if (uri.getScheme ().equals ("content"))
|
||
{
|
||
/* This is one of the annoying Android ``content''
|
||
URIs. Most of the time, there is actually an
|
||
underlying file, but it cannot be found without
|
||
opening the file and doing readlink on its file
|
||
descriptor in /proc/self/fd. */
|
||
resolver = getContentResolver ();
|
||
fd = null;
|
||
|
||
try
|
||
{
|
||
fd = resolver.openFileDescriptor (uri, "r");
|
||
names = EmacsNative.getProcName (fd.getFd ());
|
||
|
||
/* What is the right encoding here? */
|
||
|
||
if (names != null)
|
||
fileName = new String (names, "UTF-8");
|
||
|
||
fileName = checkReadableOrCopy (fileName, fd, uri);
|
||
}
|
||
catch (FileNotFoundException exception)
|
||
{
|
||
/* Do nothing. */
|
||
}
|
||
catch (IOException exception)
|
||
{
|
||
/* Do nothing. */
|
||
}
|
||
|
||
if (fd != null)
|
||
{
|
||
try
|
||
{
|
||
fd.close ();
|
||
}
|
||
catch (IOException exception)
|
||
{
|
||
/* Do nothing. */
|
||
}
|
||
}
|
||
}
|
||
|
||
if (fileName == null)
|
||
{
|
||
errorBlurb = ("The URI: " + uri + " could not be opened"
|
||
+ ", as it does not encode file name inform"
|
||
+ "ation.");
|
||
displayFailureDialog ("Error opening file", errorBlurb);
|
||
return;
|
||
}
|
||
}
|
||
|
||
/* And start emacsclient. Set `currentActivity' to this now.
|
||
Presumably, it will shortly become capable of displaying
|
||
dialogs. */
|
||
currentActivity = this;
|
||
startEmacsClient (fileName);
|
||
}
|
||
else
|
||
finish ();
|
||
}
|
||
|
||
|
||
|
||
@Override
|
||
public void
|
||
onDestroy ()
|
||
{
|
||
Log.d (TAG, "onDestroy: " + this);
|
||
|
||
/* Clear `currentActivity' if it refers to the activity being
|
||
destroyed. */
|
||
|
||
if (currentActivity == this)
|
||
this.currentActivity = null;
|
||
|
||
super.onDestroy ();
|
||
}
|
||
|
||
@Override
|
||
public void
|
||
onWindowFocusChanged (boolean isFocused)
|
||
{
|
||
Log.d (TAG, "onWindowFocusChanged: " + this + ", is now focused: "
|
||
+ isFocused);
|
||
|
||
if (isFocused)
|
||
currentActivity = this;
|
||
else if (currentActivity == this)
|
||
currentActivity = null;
|
||
|
||
super.onWindowFocusChanged (isFocused);
|
||
}
|
||
|
||
@Override
|
||
public void
|
||
onPause ()
|
||
{
|
||
Log.d (TAG, "onPause: " + this);
|
||
|
||
/* XXX: clear currentActivity here as well; I don't know whether
|
||
or not onWindowFocusChanged is always called prior to this. */
|
||
|
||
if (currentActivity == this)
|
||
currentActivity = null;
|
||
|
||
super.onPause ();
|
||
}
|
||
}
|