/* Communication module for Android terminals. -*- c-file-style: "GNU" -*- Copyright (C) 2023-2024 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 . */ package org.gnu.emacs; import java.util.ArrayList; import java.util.List; import android.app.ActivityOptions; import android.content.Intent; import android.os.Build; import android.util.Log; /* Code to paper over the differences in lifecycles between "activities" and windows. There are four interfaces to an instance of this class: registerWindowConsumer (WindowConsumer) registerWindow (EmacsWindow) removeWindowConsumer (WindowConsumer) removeWindow (EmacsWindow) A WindowConsumer is expected to allow an EmacsWindow to be attached to it, and be created or destroyed. Every time a window is created, registerWindow checks the list of window consumers. If a consumer exists and does not currently have a window of its own attached, it gets the new window. Otherwise, the window attachment manager starts a new consumer. Every time a consumer is registered, registerWindowConsumer checks the list of available windows. If a window exists and is not currently attached to a consumer, then the consumer gets it. Finally, every time a window is removed, the consumer is destroyed. */ public final class EmacsWindowAttachmentManager { private final static String TAG = "EmacsWindowAttachmentManager"; /* The single window attachment manager ``object''. */ public static final EmacsWindowAttachmentManager MANAGER; static { MANAGER = new EmacsWindowAttachmentManager (); }; public interface WindowConsumer { public void attachWindow (EmacsWindow window); public EmacsWindow getAttachedWindow (); public void detachWindow (); public void destroy (); }; /* List of currently attached window consumers. */ public List consumers; /* List of currently attached windows. */ public List windows; public EmacsWindowAttachmentManager () { consumers = new ArrayList (); windows = new ArrayList (); } public void registerWindowConsumer (WindowConsumer consumer) { consumers.add (consumer); for (EmacsWindow window : windows) { if (window.getAttachedConsumer () == null) { consumer.attachWindow (window); return; } } EmacsNative.sendWindowAction ((short) 0, 0); } public synchronized void registerWindow (EmacsWindow window) { Intent intent; ActivityOptions options; if (windows.contains (window)) /* The window is already registered. */ return; windows.add (window); for (WindowConsumer consumer : consumers) { if (consumer.getAttachedWindow () == null) { consumer.attachWindow (window); return; } } intent = new Intent (EmacsService.SERVICE, EmacsMultitaskActivity.class); intent.addFlags (Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) EmacsService.SERVICE.startActivity (intent); else { /* Specify the desired window size. */ options = ActivityOptions.makeBasic (); options.setLaunchBounds (window.getGeometry ()); EmacsService.SERVICE.startActivity (intent, options.toBundle ()); } } public void removeWindowConsumer (WindowConsumer consumer, boolean isFinishing) { EmacsWindow window; window = consumer.getAttachedWindow (); if (window != null) { consumer.detachWindow (); window.onActivityDetached (isFinishing); } consumers.remove (consumer); } public synchronized void detachWindow (EmacsWindow window) { WindowConsumer consumer; if (window.getAttachedConsumer () != null) { consumer = window.getAttachedConsumer (); consumers.remove (consumer); consumer.destroy (); } windows.remove (window); } public void noticeIconified (WindowConsumer consumer) { EmacsWindow window; /* If a window is attached, send the appropriate iconification events. */ window = consumer.getAttachedWindow (); if (window != null) window.noticeIconified (); } public void noticeDeiconified (WindowConsumer consumer) { EmacsWindow window; /* If a window is attached, send the appropriate iconification events. */ window = consumer.getAttachedWindow (); if (window != null) window.noticeDeiconified (); } public synchronized List copyWindows () { return new ArrayList (windows); } };