Differences

This shows you the differences between the selected revisions of the page.

history 2022-09-12 history 2024-06-26 (current)
Line 1: Line 1:
-/* +====== Recent Version History ======
- * Headwind Remote: Open Source Remote Access Software for Android +
- * https://headwind-remote.com +
- +
- * Copyright (C) 2022 headwind-remote.com +
-+
- * Licensed under the Apache License, Version 2.0 (the "License"); +
- * you may not use this file except in compliance with the License. +
- * You may obtain a copy of the License at +
-+
- *      http://www.apache.org/licenses/LICENSE-2.0 +
-+
- * Unless required by applicable law or agreed to in writing, software +
- * distributed under the License is distributed on an "AS IS" BASIS, +
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +
- * See the License for the specific language governing permissions and +
- * limitations under the License. +
- */+
-package com.hmdm.control;+This is a full list of changes for each release of WinSCP. See also [[project_history|Project history]] and [[incompatible_changes|Incompatible changes between versions]].
-import android.app.AlertDialog; +===== [[6.4]] 6.4 (not released yet) ((2024-06-26)) =====
-import android.app.Dialog; +
-import android.content.BroadcastReceiver; +
-import android.content.ClipData; +
-import android.content.ClipboardManager; +
-import android.content.Context; +
-import android.content.DialogInterface; +
-import android.content.Intent; +
-import android.content.IntentFilter; +
-import android.graphics.PixelFormat; +
-import android.media.projection.MediaProjectionManager; +
-import android.os.Build; +
-import android.os.Bundle; +
-import android.os.Handler; +
-import android.util.DisplayMetrics; +
-import android.util.Log; +
-import android.view.Gravity; +
-import android.view.Menu; +
-import android.view.MenuItem; +
-import android.view.View; +
-import android.view.WindowManager; +
-import android.widget.EditText; +
-import android.widget.ImageView; +
-import android.widget.TextView; +
-import android.widget.Toast;+
-import androidx.appcompat.app.AppCompatActivity+  * Thumbnail view in file panel (local). [[bug>912]] 
-import androidx.appcompat.widget.Toolbar+  * Optimized working with large local directories: [[bug>2264]] 
-import androidx.localbroadcastmanager.content.LocalBroadcastManager;+    * Loading large directory trees on the background. 
 +    * Hidden directory trees are loaded only in the background. 
 +    * Optimizing directory loading by checking existence of subfolders on the background. 
 +    * Optimized reading directories for file panel. 
 +    * Optimized browsing within directory tree with lots of subfolders. 
 +  * Dark theme for session tabs. [[bug>;1806]] 
 +··* Improvements to AWS/S3 authentication and configuration handling: 
 +    * Allowed assuming IAM role. [[bug>2249]] 
 +    * Credential profiles in ''.aws/config'' file are also recognized. 
 +    * Preferring configuration file defined using an environment variable over the default one. 
 +    * Only profiles that contain both ''aws_access_key_id'' and ''aws_secret_access_key'' are listed on the Login dialog 
 +    * Bug fix: The ''.aws/credentials'' file path was resolved using ''AWS_CONFIG_FILE'' environment variable instead of correct ''AWS_SHARED_CREDENTIALS_FILE''. 
 +  * Incremental search improvements: 
 +    * Search panel pops up on ''Ctrl+F'' to make the function easier to find. [[bug>;2281]] 
 +····* Layout of search panel on Login dialog improvend. 
 +    * Search options context menu added to the search panel on Login dialog. 
 +  * Site import from an INI file. [[bug>2290]] 
 +  * Installer upgraded to Inno Setup 6.3.1. 
 +  * In icons view, files are rearranged automatically when the panel is resized. 
 +  * Fallback to nonresumable transfer when temporary target file cannot be created. [[bug>;2277]] 
 +  * Logging can be enabled for installer subtasks. 
 +  * Limited SFTP upload queue length to avoid networking congestion. 
 +  * Updated to JCL library 2.8 commit 6380ce72. 
 +  * //Columns// menu in Explorer interface is enabled only in //Details// view. 
 +  * Bug fix: Possibility that files in root folder were incorrectly used. 
 +  * Bug fix: Failure when system theme is changed at the same time directory change is detected. [[bug>2286]] 
 +  * Bug fix: Tree indentation does not scale correctly. [[bug>2288]] 
 +  * Bug fix: It was possible to start renaming a site by double-clicking its node while editing the site. 
 +  * Bug fix: Cannot download from WebDAV server when the request is redirected to the same path on another server. [[bug>2293]] 
 +  * Bug fix: Overlay images were not drawn in icons view when //Name// column in //Details// view was too narrow.
-import com.hmdm.control.janus.SharingEngineJanus;+===== [[6.3.4]] 6.3.4 ((2024-06-17)) =====
-public class MainActivity extends AppCompatActivity implements SharingEngineJanus.EventListener, SharingEngineJanus.StateListener {+  * TLS/SSL core upgraded to OpenSSL 3.2.2. 
 +  * Translation updated: Belarusian, Danish and Russian. 
 + * Standalone executable installer can run over corrupted MSI installation. [[bug>2294]] 
 +  * Support for up-to 16KB WebDAV cookies. [[bug>2289]] 
 +  * Bug fix: Failure when trying to automate file synchronization by checksum on an SFTP server that does not support it natively. [[bug>2291]] 
 +  * Bug fix: Remote panel does not refresh after //"ZIP and Upload"//. [[bug>2292]]
-····private ImageView imageViewConnStatus; +===== [[6.3.3]] 6.3.3 ((2024-04-16)) =====
-····private TextView textViewConnStatus; +
-····private EditText editTextSessionId; +
-····private EditText editTextPassword; +
- ···private TextView textViewComment; +
-····private TextView textViewConnect; +
-····private TextView textViewSendLink; +
- ···private TextView textViewExit;+
-····private ImageView overlayDot+··* SSH core and SSH private key tools (PuTTYgen and Pageant) upgraded to [[&;url(puttychanges)|PuTTY 0.81]]. \\ It brings the following change: 
-    private Handler handler = new Handler()+    * Security fix for CVE-2024-31497: NIST P521/ecdsa-sha2-nistp521 signatures are no longer generated with biased values of //k//. The previous bias compromises private keys. [[bug>2285]] [[pbug>vuln-p521-bias]] 
- ···private int overlayDotAlpha+  * Translation updated: Belarusian. 
- ···private int overlayDotDirection = 1;+  * XML parser upgraded to Expat 2.6.2. 
 +  * Support for TortoiseMerge in //Compare Files// extension. [[bug>;2279]] 
 + ·* Bug fix: File panel does not have focus after Login in Explorer interface. [[bug>;2276]] 
 + ·* Bug fix: Failure when closing the last remote tab. [[bug>;2283]] 
 +  * Bug fix: Copy and paste to another application in Store installation sometimes does not work. [[bug>2284]]
-····private Dialog exitOnIdleDialog; +===== [[6.3.2]] 6.3.2 ((2024-03-12)) =====
- ···private int exitCounter; +
- ···private static final int EXIT_PROMPT_SEC = 10;+
-····private static final int OVERLAY_DOT_ANIMATION_INCREMENT = 20+··* Translation updated: Belarusian. 
- ···private static final int OVERLAY_DOT_ANIMATION_DELAY = 200;+  * XML parser upgraded to Expat 2.6.1. 
 +  * Optimized startup when right panel local directory tree is not visible. 
 +  * Workaround for SFTP servers (Cisco) which omit message field from status response. [[bug>;2272]] 
 + ·* Bug fix: Password pipe cannot be used to open a session in an existing instance. [[bug>;2265]] 
 +  * Bug fix: Hang when canceling connection while reading remote directory. [[bug>2266]] 
 +  * Bug fix: Failure when canceling FTP connection while reading remote directory. [[bug>2267]] 
 +  * Bug fix: Cannot start on Windows XP. [[bug>2268]] 
 +  * Bug fix: Installation hangs when adding installation path to search path when executed in session 0. [[bug>2270]] 
 +  * Bug fix: Misplaced stored site use warning in scripting when session name is specified. [[bug>2271]] 
 +  * Bug fix: Correcting neon version in About box and logs.
-····private SharingEngine sharingEngine;+===== [[6.3.1]] 6.3.1 ((2024-02-21)) ===== 
 +  * Translation completed: Norwegian. 
 +  * Bug fix: Badly encoded SFTP packet when renaming a file using SFTP version 5 and newer. [[bug>;2259]] 
 +  * Bug fix: Failure when trying to synchronize files by checksum on server that does not support it. [[bug>2260]] 
 +  * Bug fix: Random hang/failure when closing FTP TLS 1.3 connection. [[bug>2261]] 
 +  * Bug fix: Cannot use IPv6 literal as hostname on Login dialog. [[bug>2263]]
-····private SettingsHelper settingsHelper;+===== [[6.3]] 6.3 ((2024-02-14)) =====
-····private String sessionId; +··* XML parser upgraded to Expat 2.6.0. 
- ···private String password+ ·* Bug fix: Hang when prompt pops up while SFTP session is being reconnected. [[bug>;2258]]
-   private String adminName;+
-····private final static String ATTR_SESSION_ID = "sessionId"; +===== [[6.2.4]] 6.2.4 RC ((2024-02-03)) =====
- ···private final static String ATTR_PASSWORD = "password"; +
- ···private final static String ATTR_ADMIN_NAME = "adminName";+
-····private boolean needReconnect = false;+··* Translations completed: Farsi, French, Japanese, Spanish and Traditional Chinese. 
 +  * TLS/SSL core upgraded to OpenSSL 3.2.1. 
 +  * WebDAV/HTTP core upgraded to neon 0.33.0. 
 +  * Bug fix: Failure when trying to upload file using double-click over disconnected session. [[bug>;2254]] 
 +  * Bug fix: Failure after long frequent use of session tabs. [[bug>2255]] 
 +  * Bug fix: //New tab// icon is drawn incorrectly on Explorer interface with //Show selective text labels// turned off. [[bug>2257]] 
 +  * Bug fix: Failure when switching to another application while new session is being opened using //New Tab// tab. [[bug>2251]]
-····private MediaProjectionManager projectionManager;+===== [[6.2.3]] 6.2.3 RC ((2024-01-19)) =====
-····private BroadcastReceiver mSharingServiceReceiver = new BroadcastReceiver() { +··* Added new ''ca-west-1'' AWS region. 
- ·······@Override +· * Translations completed: Catalan, Czech, Dutch, Finnish, German, Hungarian, Italian, Korean, Polish, Portuguese, Romanian, Russian, Simplified Chinese, Slovak, Turkish and Tamil; and updated: Japanese. 
- ·······public void onReceive(Context context, Intent intent) { + * Support for ''posix-rename@openssh.com'' SFTP extension. [[bug>2231]] 
- ···········if (intent == null || intent.getAction() == null) { + ·* When cleaning up application data, deleting even ''Martin Prikryl'' and ''WinSCP 2'' root keys, if they remain empty. 
- ···············return+ ·* Ignoring attempts to directly move/duplicate file over itself, as if protocol requires deleting, the file would be lost
- ···········} +  * Configurable FTP TLS shutdown procedure. [[bug>2250]] 
- ···········if (intent.getAction().equals(Const.ACTION_SCREEN_SHARING_START)) { +· * Not failing connection when FTP server responds to ''CSID'' command with an error. [[bug>2253]] 
- ···············notifySharingStart();+ ·* Bug fix: Certificate authority cache was not copied to new configuration storage nor cleaned up with other caches. 
 + ·* Bug fix: ''ssh'' protocol URL handling was not completely unregistered. 
 +··* Bug fix: Reported transfer size is rarely incorrect during FTP downloads. 
 +··* Bug fix: Failure after connecting to server. [[bug>2251]] 
 + ·* Bug fix: FTP ''CSID'' command does not end with semicolon. [[bug>;2252]]
-············} else if (intent.getAction().equals(Const.ACTION_SCREEN_SHARING_STOP)) { +===== [[6.2.2]] 6.2.2 beta ((2023-12-22)) =====
-················notifySharingStop(); +
-               adminName = null; +
-                updateUI(); +
-················cancelSharingTimeout()+
- ···············scheduleExitOnIdle();+
-············} else if (intent.getAction().equals(Const.ACTION_SCREEN_SHARING_FAILED)) { +··* SSH core and SSH private key tools (PuTTYgen and Pageant) upgraded to [[&url(puttychanges)|PuTTY 0.80]]. \\ It brings the following change: 
- ···············String message = intent.getStringExtra(Const.EXTRA_MESSAGE); + ···* Mitigations for SSH protocol "Terrapin" vulnerability. [[bug>;2246]] [[pbug>vuln-terrapin]] 
- ···············if (message != null) { +· * Support for ''Include'' directive when importing sites from OpenSSH. [[bug>2239]] 
- ···················Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show(); + ·* Change: .NET assembly collections are tagged with ''ClassInterfaceType.None'' to avoid warnings from ''regasm''. 
-················} +··* Not using directory listing to keep FTP session alive by default. [[bug>;2244]] 
- ···············adminName = null+ ·* Windows Store installation on Windows 11 was incorrectly using INI file for configuration storage by default. [[bug>;2245]] 
- ···············updateUI()+ ·* Bug fix: Find dialog file list is scaled incorrectly on some multi monitor systems with different scaling. [[bug>;2241]] 
- ···············cancelSharingTimeout()+ ·* Bug fix: Cannot browse long local paths. [[bug>;2242]]
- ···············scheduleExitOnIdle();+
-············} else if (intent.getAction().equals(Const.ACTION_CONNECTION_FAILURE)) +===== [[6.2.1]] 6.2.1 beta ((2023-12-05)) =====
-                sharingEngine.setState(Const.STATE_DISCONNECTED); +
-               Toast.makeText(MainActivity.this, R.string.connection_failure_hint, Toast.LENGTH_LONG).show()+
- ···············updateUI();+
-············} else if (intent.getAction().equals(Const.ACTION_SCREEN_SHARING_PERMISSION_NEEDED)) { +··* File hash can be used as criterion for synchronization. [[bug>52]] 
- ···············startActivityForResult(projectionManager.createScreenCaptureIntent(), Const.REQUEST_SCREEN_SHARE)+  * Consistent behavior across protocols and protocol capabilities when duplicating remote files. [[bug>2233]] 
- ···········} +  * //Columns > Reset Layout// command added to Explorer interface too. 
- ·······} +··* TLS/SSL core upgraded to OpenSSL 3.2.0. 
- ···};+· * Support for "Requester pays" S3 buckets. [[bug>2213]] 
 + ·* Optional more prominent active session tab. [[bug>2229]] 
 +  * Optionally not shortening tab titles. [[bug>2202]] 
 +  * New ''winscp.net'' root certificate. 
 +  * Restored support for legacy version of the Digest algorithm specified in RFC 2069. [[bug>2109]] 
 +  * Restored consistent behavior of failing, between duplicating and moving/renaming files over existing file with WebDAV protocol in scripting and .NET assembly. 
 +  * When moving a folder by drag&drop to a path that already contains a subfolder with the same name, the existing folder is overwritten. 
 + ·* Shorter and more friendly formatting of long time intervals. [[bug>2236]] 
 + ·* When typing ambiguous port numbers in Login dialog, keeping the current protocol, even if it is not the default protocol for the port. 
 + ·* Bug fix: Failure when //New Tab// is clicked while another session is still being loaded. 
 +  * Bug fix: Corrected some painting artifacts on session tabs, particularly on Windows 11. 
 +  * Bug fix: ''Shift+F5'' shortcut operated with a focused file, rather than with selected files. 
 +  * Bug fix: OpenSSL version in About dialog was not up to date. 
 +  * Bug fix: Cannot leave directory entered via cache with SCP protocol if it was deleted meanwhile. [[bug>;2234]] 
 +  * Bug fix: Failure when connection is lost while reading remote directory with SFTP protocol. [[bug>2235]] 
 +  * Bug fix: Multipart upload to Cloudflare R2 S3 interface fails due to too long upload ID. [[bug>2237]] 
 +  * Bug fix: Panel focus was lost in some situations. 
 +  * Bug fix: When S3 or WebDAV server did not provide file timestamp, downloaded file was set to oldest possible timestamp. 
 +  * Bug fix: When session URL is typed into //Host name// box or pasted using context menu of the box and the Login dialog is submitted using ''Enter'' key, the URL is not parsed correctly. 
 +  * Bug fix: Failure when saving edited file over reconnected session after previous reconnect attempt was aborted. [[bug>2238]]
-····@Override +===== [[6.2]] 6.2 beta ((2023-10-05)) ===== 
- ···protected void onCreate(Bundle savedInstanceState) { + ·* Single large file can be downloaded using multiple SFTP connections. [[bug>513]] 
- ·······super.onCreate(savedInstanceState); +  * Support for OpenSSH certificates for host verification. Sponsored by [[https://goteleport.com/|Teleport]]. [[bug>2145]] 
- ·······setContentView(R.layout.activity_main);+  * SSH core and SSH private key tools (PuTTYgen and Pageant) upgraded to [[&url(puttychanges)|PuTTY 0.79]]. \\ It brings the following change: 
 + ···* Support for HMAC-SHA-512. [[pbug>hmac-sha2-512]] 
 +  * TLS/SSL core upgraded to OpenSSL 3.1.3. 
 +  * Allowed S3 connection with IAM role instead of credentials. [[bug>2089]] 
 +  * Command to open the same folder as in the other panel in local file manager mode. [[bug>2189]] 
 +  * Support appending when streaming file contents to remote server. [[bug>2214]] 
 +  * Commands to reset layout of file panels and background transfer queue list columns. 
 +  * Change: SSL (3.0) is no longer supported. TLS 1.0 and 1.1 are disabled by default, to match the OpenSSL 3 defaults. 
 +  * Using optimized OpenSSL implementations of some algorithms. 
 +  * Command to automatically size file panel columns. [[bug>;2196]] 
 + ·* Allowed browsing a source folder/file instead of downloading when handling a download URL. [[bug>2211]] 
 +  * Passing password to PuTTY using named pipe instead of commandline. 
 +  * Made it harder to mis-click "never show this again" checkboxes. [[bug>2217]] 
 +  * ''winscp.net'' root certificate is always trusted when checking for updates, even when (corporate managed) Windows certificate store does not trust it. [[bug>;2212]] 
 +  * Not browsing a source folder when handling a download URL when a download dialog is canceled. 
 +  * Recognizing ''CertificateFile'' directive when importing sites from OpenSSH. [[bug>2220]] 
 +  * Restored ability to duplicate remote folders using ''cp'' command in secondary shell session even when the SFTP server supports ''copy-file''/''copy-data'' extension. [[bug>2227]] 
 +  * ''%%https://%%'' URL with ''r2.cloudflarestorage.com'' hostname is interpreted as S3 protocol, instead of WebDAV. 
 +  * Recognizing path in //Host name// box on Login dialog. [[bug>2219]] 
 +  * Improved behavior when moving/renaming over an existing folder. [[bug>2209]] 
 +  * Improved OpenSSH ''config'' file parsing, particularly quoted and escaped values. [[bug>2206]] 
 +  * Support for ProFTPD command ''%%OPTS REST STOR%%'' to query if upload restart is possible. [[bug>2194]] 
 +  * Not showing a filter mask on a disconnected panel. 
 +  * Implemented generic ''ICollection'' implicitly by .NET assembly collections. [[bug>2187]] 
 +  * ''ProxyMethod'' raw session setting supports symbolical value names. 
 +  * Ignoring disconnects from the server while closing the connection. [[bug>2195]] 
 +  * Translations updated: Danish and Traditional Chinese. 
 +  * Preventing background transfer queue list columns width to shrink too much. [[bug>2208]] 
 +  * Not forcing text mode for edits with Windows Notepad on Windows 10 1809 and newer, as it already supports non-Windows line endings. 
 +  * Allowed disabling SFTP extension use. [[bug>2222]] 
 +  * Using packet size limit announced by OpenSSH ''limits@openssh.com'' extension. 
 +  * Improved HTTP error reporting and logging. 
 +  * With application logging enabled, automatic updates installation is started with logging too. 
 +  * Throwing an exception when ''Session.SessionLogPath'' is set to a path with invalid ''.xml'' extension. [[bug>2215]] 
 +  * Recognizing IP addresses starting with zero in FTP PASV response as unroutable. [[bug>2224]] 
 +  * When there is both administrative and non administrative installation, automatic update automatically selects the the correct one for update. 
 +  * Not displaying Administrator shield icon on //Upgrade// button, when Administrator permissions are not needed for the upgrade. 
 +  * More meaningful error message when credentials are missing in scripting and .NET assembly. 
 +  * Bug fix: WebDAV or S3 certificate that is recognized by Windows Certificate store, but have other issues, cannot be marked trusted by the user. [[bug>2191]] 
 +  * Bug fix: Localized HTTP connection error messages are incorrectly decoded. [[bug>2197]] 
 +  * Bug fix: Special characters in directory names were not correctly restored when uploading on background with multiple connections for single transfer enabled. 
 +  * Bug fix: Master password prompt was not added to taskbar when opening session from commandline or when automatically loading workspace to yet invisible main window. 
 +  * Bug fix: Handling download URL does not work when another idle instance is running. [[bug>2203]] 
 +  * Bug fix: TLS session resumption is not working for subsequent FTP data connections with TLS 1.3 with some servers. [[bug>2210]] 
 +  * Bug fix: Failure when using ''/browse='' switch and a file panel is empty. 
 +  * Bug fix: Correcting default OpenSSL configuration paths. 
 +  * Bug fix: Potential failure when opening unencrypted HTTP/WebDAV connection. 
 +  * Bug fix: When second local panel tree view has focus, some keyboards shortcuts still operated on the first local panel. 
 +  * Bug fix: Local file panel malfunctions when it starts on a drive hidden by Explorer's policy. [[bug>2216]] 
 +  * Bug fix: When opening UNC path, the network drive is not added to the other local panel directory tree. 
 +  * Bug fix: When opening the UNC path on the second local panel, the network drive is not added to drive drop-down menus. 
 +  * Bug fix: Script sometimes does not abort after receiving no answer for prompt, when running in batch mode. 
 +  * Bug fix: Configuration reading was broken after an attempt to access a non-existing sub-section within a section existed in raw configuration only. 
 +  * Bug fix: WinSCP could ask the server to return more data during SFTP download than it can process. [[bug>2218]] 
 +  * Bug fix: Renaming tab invalidated remembered password. 
 +  * Bug fix: WinSCP loses focus after custom command is executed. [[bug>2221]] 
 +  * Bug fix: Some DLLs were not protected against hijacking. [[bug>2223]] 
 +  * Bug fix: Failure when error occurs on secondary shell session with //Continue on error// option enabled. [[bug>2226]] 
 +  * Bug fix: Failure when reconnect on edited/opened file save is canceled. [[bug>2228]]
-········if (savedInstanceState != null) { +===== [[6.1.2]] 6.1.2 ((2023-09-19)) =====
- ···········restoreInstanceState(savedInstanceState); +
- ·······}+
-········Toolbar toolbar = findViewById(R.id.toolbar)+··* MSI installer is not localized anymore to avoid problems with GPO. [[bug>2200]] 
- ·······setSupportActionBar(toolbar); + ·* TLS/SSL core upgraded to OpenSSL 1.1.1w. 
- ·······settingsHelper = SettingsHelper.getInstance(this); + ·* Translations updated: Catalan, Danish, Russian and Turkish. 
- ·······sharingEngine = SharingEngineFactory.getSharingEngine(); + ·* Added new ''il-central-1'' AWS region. 
- ·······sharingEngine.setEventListener(this); + ·* Bug fix: Typo in GPL license in installer. [[bug>2201]] 
-········sharingEngine.setStateListener(this);+··* Bug fix: Check for application updates was limited to TLS 1.2.
-········DisplayMetrics metrics = new DisplayMetrics()+===== [[6.1.1]] 6.1.1 ((2023-06-21)) =====
- ·······ScreenSharingHelper.getRealScreenSize(this, metrics); +
-        float videoScale = ScreenSharingHelper.adjustScreenMetrics(metrics); +
-       settingsHelper.setFloat(SettingsHelper.KEY_VIDEO_SCALE, videoScale); +
-        ScreenSharingHelper.setScreenMetrics(this, metrics.widthPixels, metrics.heightPixels, metrics.densityDpi);+
-········sharingEngine.setScreenWidth(metrics.widthPixels); +··* Translations completed: Brazilian Portuguese, Farsi and Portuguese; and updated: French and Italian
-       sharingEngine.setScreenHeight(metrics.heightPixels); +  * Support for file masks in //Keep local directory up to date// extension. [[bug>1892]]. 
- +  * TLS/SSL core upgraded to OpenSSL 1.1.1u
- ·······IntentFilter intentFilter = new IntentFilter(Const.ACTION_SCREEN_SHARING_START); +  * Bug fix: Patterns in default values of extension options were escaped in an extension tooltip
-        intentFilter.addAction(Const.ACTION_SCREEN_SHARING_STOP); +  * Bug fix: //Calculate Directory Sizes// command was redundantly in all local panel column context menus
-        intentFilter.addAction(Const.ACTION_SCREEN_SHARING_PERMISSION_NEEDED); +  * Bug fix: Thanks and transitioning help toolbar message in Store installation was not readable in Dark theme. [[bug>2198]] 
-        intentFilter.addAction(Const.ACTION_SCREEN_SHARING_FAILED); +  * Bug fix: MSI installer runs in Catalan language (or possibly fails) when used in GPO. [[bug>2199]]
-        intentFilter.addAction(Const.ACTION_CONNECTION_FAILURE); +
-        LocalBroadcastManager.getInstance(this).registerReceiver(mSharingServiceReceiver, intentFilter); +
- +
-       projectionManager = (MediaProjectionManager)getSystemService(Context.MEDIA_PROJECTION_SERVICE); +
- +
-       initUI(); +
- ·······setDefaultSettings(); +
-   } +
- +
-   @Override +
-   protected void onResume() { +
-        super.onResume(); +
-       updateUI(); +
- +
- ·······startService(new Intent(MainActivity.this, GestureDispatchService.class)); +
-//       connect(); +
-       checkAccessibility(); +
-   } +
- +
-   private void checkAccessibility() { +
-        if (!Utils.isAccessibilityPermissionGranted(this)) { +
-            textViewConnect.setVisibility(View.INVISIBLE); +
-            new AlertDialog.Builder(this) +
-                    .setMessage(R.string.accessibility_hint) +
-                    .setPositiveButton(R.string.continue_button, new DialogInterface.OnClickListener() { +
-                        @Override +
-                        public void onClick(DialogInterface dialog, int which) { +
-                            Intent intent = new Intent(android.provider.Settings.ACTION_ACCESSIBILITY_SETTINGS); +
-                            try { +
-                                startActivityForResult(intent, 0); +
-                            } catch (Exception e) { +
-································// Accessibility settings cannot be opened +
-                               reportAccessibilityUnavailable(); +
-                           } +
-                       } +
-                    }) +
-                    .setCancelable(false) +
-                    .create() +
-                    .show(); +
-        } else { +
-            configureAndConnect(); +
-        } +
-    } +
- +
-    private void reportAccessibilityUnavailable() { +
-        new AlertDialog.Builder(this) +
-                .setMessage(R.string.accessibility_unavailable_error) +
-                .setPositiveButton(R.string.button_exit, new DialogInterface.OnClickListener() { +
-                    @Override +
-                    public void onClick(DialogInterface dialog, int which) { +
-                        exitApp(); +
-                    } +
-                }) +
-                .setCancelable(false) +
-                .create() +
-                .show(); +
-    } +
- +
-    private void configureAndConnect() { +
-        if (settingsHelper.getString(SettingsHelper.KEY_SERVER_URL) == null) { +
-            // Not configured yet +
-            settingsHelper.setString(SettingsHelper.KEY_SERVER_URL, BuildConfig.DEFAULT_SERVER_URL); +
-            settingsHelper.setString(SettingsHelper.KEY_SECRET, BuildConfig.DEFAULT_SECRET); +
-            settingsHelper.setBoolean(SettingsHelper.KEY_USE_DEFAULT, !BuildConfig.DEFAULT_SECRET.equals("")); +
-            Intent intent = new Intent(this, SettingsActivity.class); +
-            startActivityForResult(intent, Const.REQUEST_SETTINGS); +
-            return; +
-        } +
- +
-        if (needReconnect) { +
-            // Here we go after changing settings +
-            needReconnect = false; +
-            if (sharingEngine.getState() != Const.STATE_DISCONNECTED) { +
-                sharingEngine.disconnect(MainActivity.this, (success, errorReason) -> connect()); +
-            } else { +
-                connect(); +
-            } +
-        } else { +
-            if (sharingEngine.getState() == Const.STATE_DISCONNECTED && sharingEngine.getErrorReason() == null) { +
-                connect(); +
-            } +
-        } +
-    } +
- +
-    @Override +
-    public void onDestroy() { +
-        try { +
-            LocalBroadcastManager.getInstance(this).unregisterReceiver(mSharingServiceReceiver); +
-        } catch (Exception e) { +
-        } +
-        super.onDestroy(); +
-    } +
- +
-    @Override +
-    public void onSaveInstanceState(Bundle savedInstanceState) { +
-        savedInstanceState.putString(ATTR_SESSION_ID, sessionId); +
-        savedInstanceState.putString(ATTR_PASSWORD, password); +
-        savedInstanceState.putString(ATTR_ADMIN_NAME, adminName); +
-        super.onSaveInstanceState(savedInstanceState); +
-    } +
- +
-    private void restoreInstanceState(Bundle savedInstanceState) { +
-        sessionId = savedInstanceState.getString(ATTR_SESSION_ID); +
-        password = savedInstanceState.getString(ATTR_PASSWORD); +
-        adminName = savedInstanceState.getString(ATTR_ADMIN_NAME); +
-    } +
- +
-    @Override +
-    public void onBackPressed() { +
-        Toast.makeText(this, R.string.back_pressed, Toast.LENGTH_LONG).show(); +
-    } +
- +
-    @Override +
-    public boolean onCreateOptionsMenu(Menu menu) { +
-        // Inflate the menu; this adds items to the action bar if it is present. +
-        getMenuInflater().inflate(R.menu.menu_main, menu); +
-        return true; +
-    } +
- +
-    @Override +
-    public boolean onOptionsItemSelected(MenuItem item) { +
-········// Handle action bar item clicks here. The action bar will +
-        // automatically handle clicks on the Home/Up button, so long +
-        // as you specify a parent activity in AndroidManifest.xml. +
-        int id = item.getItemId(); +
- +
-        if (id == R.id.action_settings) { +
-            if (adminName != null) { +
-                Toast.makeText(this, R.string.settings_unavailable, Toast.LENGTH_LONG).show(); +
-                return true; +
-            } +
-            Intent intent = new Intent(this, SettingsActivity.class); +
-            startActivityForResult(intent, Const.REQUEST_SETTINGS); +
-            cancelExitOnIdle(); +
-            return true; +
-        } else if (id == R.id.action_about) { +
-            showAbout(); +
-            return true; +
-        } +
- +
-        return super.onOptionsItemSelected(item); +
-    } +
- +
-    @Override +
-    public void onActivityResult(int requestCode, int resultCode, Intent data) { +
-        super.onActivityResult(requestCode, resultCode, data); +
- +
-        if (requestCode == Const.REQUEST_SETTINGS) { +
-            if (resultCode == Const.RESULT_DIRTY) { +
-                needReconnect = true; +
-            } else { +
-                scheduleExitOnIdle(); +
-            } +
-        } else if (requestCode == Const.REQUEST_SCREEN_SHARE) { +
-            if (resultCode != RESULT_OK) { +
-                Toast.makeText(this, R.string.screen_cast_denied, Toast.LENGTH_LONG).show(); +
-                adminName = null; +
-                updateUI(); +
-                cancelSharingTimeout(); +
-                scheduleExitOnIdle(); +
-            } else { +
-                ScreenSharingHelper.startSharing(this, resultCode, data); +
-            } +
-        } +
-    } +
- +
-    private void initUI() { +
-        imageViewConnStatus = findViewById(R.id.image_conn_status); +
-        textViewConnStatus = findViewById(R.id.conn_status); +
-        editTextSessionId = findViewById(R.id.session_id_edit); +
-        editTextPassword = findViewById(R.id.password_edit); +
-        textViewComment = findViewById(R.id.comment); +
-        textViewConnect = findViewById(R.id.reconnect); +
-        textViewSendLink = findViewById(R.id.send_link); +
-        textViewExit = findViewById(R.id.disconnect_exit); +
- +
-        textViewConnect.setOnClickListener(v -> connect()); +
- +
-        textViewSendLink.setOnClickListener(v -> sendLink()); +
- +
-        textViewExit.setOnClickListener(v -> gracefulExit()); +
-    } +
- +
-    private void gracefulExit() { +
-        if (adminName != null) { +
-            notifySharingStop(); +
-            ScreenSharingHelper.stopSharing(MainActivity.this, true); +
-        } +
-        sharingEngine.disconnect(MainActivity.this, (success, errorReason) -> exitApp()); +
-        // 5 sec timeout to exit +
-        handler.postDelayed(() -> exitApp(), 5000); +
-    } +
- +
-    private void exitApp() { +
-        Intent intent = new Intent(MainActivity.this, ScreenSharingService.class); +
-        stopService(intent); +
-        intent = new Intent(MainActivity.this, GestureDispatchService.class); +
-        stopService(intent); +
-        finishAffinity(); +
-        System.exit(0); +
-    } +
- +
-    private void updateUI() { +
-        int[] stateLabels = {R.string.state_disconnected, R.string.state_connecting, R.string.state_connected, R.string.state_sharing, R.string.state_disconnecting}; +
-        int[] stateImages = {R.drawable.ic_disconnected, R.drawable.ic_connecting, R.drawable.ic_connected, R.drawable.ic_sharing, R.drawable.ic_connecting}; +
- +
-        int state = sharingEngine.getState(); +
-        if (state == Const.STATE_CONNECTED && adminName != null) { +
-            imageViewConnStatus.setImageDrawable(getDrawable(stateImages[Const.STATE_SHARING])); +
-            textViewConnStatus.setText(stateLabels[Const.STATE_SHARING]); +
-        } else { +
-            imageViewConnStatus.setImageDrawable(getDrawable(stateImages[state])); +
-           textViewConnStatus.setText(stateLabels[state]); +
- ·······+
- ·······String serverUrl = Utils.prepareDisplayUrl(settingsHelper.getString(SettingsHelper.KEY_SERVER_URL)); +
- +
-       textViewSendLink.setVisibility(state == Const.STATE_CONNECTED ? View.VISIBLE : View.INVISIBLE); +
-        textViewConnect.setVisibility(state == Const.STATE_DISCONNECTED ? View.VISIBLE : View.INVISIBLE); +
-       switch (state) +
-           case Const.STATE_DISCONNECTED: +
-               editTextSessionId.setText(""); +
-               editTextPassword.setText(""); +
-               if (sharingEngine.getErrorReason() != null) +
-                   textViewComment.setText(getString(R.string.hint_connection_error, serverUrl)); +
-               } +
-               break; +
-           case Const.STATE_CONNECTING: +
-               textViewComment.setText(getString(R.string.hint_connecting, serverUrl)); +
-               break; +
-           case Const.STATE_DISCONNECTING: +
-               textViewComment.setText(getString(R.string.hint_disconnecting)); +
-                break; +
-            case Const.STATE_CONNECTED: +
-                editTextSessionId.setText(sessionId); +
-                editTextPassword.setText(password); +
-                textViewComment.setText(adminName != null ? +
-                        getString(R.string.hint_sharing, adminName) : +
-                        getString(R.string.hint_connected, serverUrl) +
-                        ); +
-                break; +
-        } +
-    } +
- +
-    private void setDefaultSettings() { +
-        if (settingsHelper.getString(SettingsHelper.KEY_DEVICE_NAME) == null) { +
-            settingsHelper.setString(SettingsHelper.KEY_DEVICE_NAME, Build.MANUFACTURER + " " + Build.MODEL); +
-        } +
-        if (settingsHelper.getInt(SettingsHelper.KEY_BITRATE) == 0) { +
-            settingsHelper.setInt(SettingsHelper.KEY_BITRATE, Const.DEFAULT_BITRATE); +
-        } +
-        if (settingsHelper.getInt(SettingsHelper.KEY_FRAME_RATE) == 0) { +
-            settingsHelper.setInt(SettingsHelper.KEY_FRAME_RATE, Const.DEFAULT_FRAME_RATE); +
-        } +
-        if (settingsHelper.getInt(SettingsHelper.KEY_IDLE_TIMEOUT) == 0) { +
-            settingsHelper.setInt(SettingsHelper.KEY_IDLE_TIMEOUT, Const.DEFAULT_IDLE_TIMEOUT); +
-        } +
-        if (settingsHelper.getInt(SettingsHelper.KEY_PING_TIMEOUT) == 0) { +
-            settingsHelper.setInt(SettingsHelper.KEY_PING_TIMEOUT, Const.DEFAULT_PING_TIMEOUT); +
-        } +
-    } +
- +
-    private void sendLink() { +
-        String url = settingsHelper.getString(SettingsHelper.KEY_SERVER_URL); +
-        url += "?session=" + sessionId + "&pin=" + password; +
-        try { +
-            Intent shareIntent = new Intent(Intent.ACTION_SEND); +
-            shareIntent.setType("text/plain"); +
-            shareIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.send_link_subject)); +
-            String shareMessage= getString(R.string.send_link_message, url, settingsHelper.getString(SettingsHelper.KEY_DEVICE_NAME)); +
-            shareIntent.putExtra(Intent.EXTRA_TEXT, shareMessage); +
-            startActivity(Intent.createChooser(shareIntent, getString(R.string.send_link_chooser))); +
-        } catch(Exception e) { +
-            e.printStackTrace(); +
-            Toast.makeText(this, R.string.send_link_failed, Toast.LENGTH_LONG).show(); +
-        } +
-    } +
- +
-    private void showAbout() { +
-        ImageView imageView = new ImageView(this); +
-        imageView.setImageDrawable(getResources().getDrawable(R.mipmap.ic_launcher)); +
-        new AlertDialog.Builder(this) +
-                .setTitle(R.string.about_title) +
-                .setMessage(getString(R.string.about_message, BuildConfig.VERSION_NAME, BuildConfig.VARIANT)) +
-                .setPositiveButton(R.string.ok, (dialog, which) -> dialog.dismiss()) +
-                .create() +
-                .show(); +
-    } +
- +
-    private void connect() { +
-        if (sessionId == null || password == null) { +
-            sessionId = Utils.randomString(8, true); +
-            password = Utils.randomString(4, true); +
-        } +
-        sharingEngine.setUsername(settingsHelper.getString(SettingsHelper.KEY_DEVICE_NAME)); +
-        sharingEngine.connect(this, sessionId, password, (success, errorReason) -> { +
-            if (!success) { +
-                if (errorReason != null && errorReason.equals(Const.ERROR_ICE_FAILED)) { +
-                    errorReason = getString(R.string.connection_error_ice); +
-                } +
-                String message = getString(R.string.connection_error, settingsHelper.getString(SettingsHelper.KEY_SERVER_URL), errorReason); +
-                reportError(message); +
-                editTextSessionId.setText(null); +
-                editTextPassword.setText(null); +
-            } +
-        }); +
- +
-        scheduleExitOnIdle(); +
-    } +
- +
-    private void reportError(final String message) { +
-//       Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show(); +
-        final AlertDialog dialog = new AlertDialog.Builder(this) +
-                .setMessage(message) +
-                .setNegativeButton(R.string.copy_message, (dialog1, which) -> { +
-                    ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); +
-                    ClipData clip = ClipData.newPlainText(Const.LOG_TAG, message); +
-                    clipboard.setPrimaryClip(clip); +
-                    Toast.makeText(MainActivity.this, R.string.message_copied, Toast.LENGTH_LONG).show(); +
-                    dialog1.dismiss(); +
-                }) +
-                .setPositiveButton(R.string.close, (dialog1, which) -> dialog1.dismiss()) +
-                .create(); +
-        try { +
-            dialog.show(); +
-            handler.postDelayed(() -> { +
-                try { +
-                    dialog.dismiss(); +
-                } catch (Exception e) { +
-                } +
-            }, 10000); +
-        } catch (Exception e) { +
-        } +
-    } +
- +
-    @Override +
-    public void onStartSharing(String adminName) { +
-········// This event is raised when the admin joins the text room +
-        this.adminName = adminName; +
-       updateUI(); +
- ·······cancelExitOnIdle(); +
-       scheduleSharingTimeout(); +
-       ScreenSharingHelper.requestSharing(this); +
-    } +
- +
-    @Override +
-    public void onStopSharing() { +
-        // This event is raised when the admin leaves the text room +
-        notifySharingStop(); +
-        adminName = null; +
-        updateUI(); +
-        cancelSharingTimeout(); +
-        scheduleExitOnIdle(); +
-        ScreenSharingHelper.stopSharing(this, false); +
-    } +
- +
-    @Override +
-    public void onRemoteControlEvent(String event) { +
-        Intent intent = new Intent(MainActivity.this, GestureDispatchService.class); +
-        intent.setAction(Const.ACTION_GESTURE); +
-        intent.putExtra(Const.EXTRA_EVENT, event); +
-        startService(intent); +
-    } +
- +
-    @Override +
-    public void onPing() { +
-        if (adminName != null) { +
-            cancelSharingTimeout(); +
-            scheduleSharingTimeout(); +
-        } +
-    } +
- +
-    @Override +
-    public void onSharingApiStateChanged(int state) { +
-        updateUI(); +
-        if (state == Const.STATE_CONNECTED) { +
-            String rtpHost = Utils.getRtpUrl(settingsHelper.getString(SettingsHelper.KEY_SERVER_URL)); +
-            int rtpAudioPort = sharingEngine.getAudioPort(); +
-            int rtpVideoPort = sharingEngine.getVideoPort(); +
-            String testDstIp = settingsHelper.getString(SettingsHelper.KEY_TEST_DST_IP); +
-            if (testDstIp != null && !testDstIp.trim().equals("")) { +
-                rtpHost = testDstIp; +
-                rtpVideoPort = Const.TEST_RTP_PORT; +
-                Toast.makeText(this, "Test mode: sending stream to " + rtpHost + ":" + rtpVideoPort, Toast.LENGTH_LONG).show(); +
-            } +
- +
-            ScreenSharingHelper.configure(this, settingsHelper.getBoolean(SettingsHelper.KEY_TRANSLATE_AUDIO), +
-                    settingsHelper.getInt(SettingsHelper.KEY_FRAME_RATE), +
-                    settingsHelper.getInt(SettingsHelper.KEY_BITRATE), +
-                    rtpHost, +
-                    rtpAudioPort, +
-                    rtpVideoPort +
-                    ); +
-        } +
-    } +
- +
-    private void scheduleExitOnIdle() { +
-        int exitOnIdleTimeout = settingsHelper.getInt(SettingsHelper.KEY_IDLE_TIMEOUT); +
-        if (exitOnIdleTimeout > 0) { +
-            exitCounter = EXIT_PROMPT_SEC; +
-            handler.postDelayed(warningOnIdleRunnable, exitOnIdleTimeout * 1000); +
-            Log.d(Const.LOG_TAG, "Scheduling exit in " + (exitOnIdleTimeout * 1000) + " sec"); +
-        } +
-    } +
- +
-    private void cancelExitOnIdle() { +
-        Log.d(Const.LOG_TAG, "Cancelling scheduled exit"); +
-        handler.removeCallbacks(warningOnIdleRunnable); +
-        handler.removeCallbacks(exitRunnable); +
-    } +
- +
-    private Runnable exitRunnable = () -> { +
-        exitCounter--; +
-        if (exitCounter > 0) { +
-            TextView messageView = exitOnIdleDialog.findViewById(android.R.id.message); +
-            if (messageView != null) { +
-                messageView.setText(MainActivity.this.getResources().getString(R.string.app_idle_warning, exitCounter)); +
-            } +
-            scheduleExitRunnable(); +
- +
-        } else { +
-            gracefulExit(); +
-        } +
-    }; +
- +
-    private Runnable warningOnIdleRunnable = () -> { +
-        exitOnIdleDialog = new AlertDialog.Builder(MainActivity.this) +
-                .setMessage(MainActivity.this.getResources().getString(R.string.app_idle_warning, exitCounter)) +
-                .setPositiveButton(R.string.button_exit, (dialog1, which) -> { +
-                    gracefulExit(); +
-                }) +
-                .setNegativeButton(R.string.button_keep_idle, (dialog1, which) -> { +
-                    scheduleExitOnIdle(); +
-                    handler.removeCallbacks(exitRunnable); +
-                    dialog1.dismiss(); +
-                }) +
-                .setCancelable(false) +
-                .create(); +
-        try { +
-            exitOnIdleDialog.show(); +
-            scheduleExitRunnable(); +
-        } catch (Exception e) { +
-            gracefulExit(); +
-        } +
-    }; +
- +
-    private void scheduleExitRunnable() { +
-        handler.postDelayed(exitRunnable, 1000); +
-    } +
- +
-    private void scheduleSharingTimeout() { +
-        int pingTimeout = settingsHelper.getInt(SettingsHelper.KEY_PING_TIMEOUT); +
-        if (pingTimeout > 0) { +
-            Log.d(Const.LOG_TAG, "Scheduling sharing stop in " + (pingTimeout * 1000) + " sec"); +
-            handler.postDelayed(sharingStopByPingTimeoutRunnable, pingTimeout * 1000)+
-        } +
-    } +
- +
-    private void cancelSharingTimeout() { +
-        Log.d(Const.LOG_TAG, "Cancelling scheduled sharing stop"); +
-        handler.removeCallbacks(sharingStopByPingTimeoutRunnable); +
-    } +
- +
-    private Runnable sharingStopByPingTimeoutRunnable = new Runnable() { +
-        @Override +
-        public void run() { +
-            Toast.makeText(MainActivity.this, R.string.app_sharing_session_ping_timeout, Toast.LENGTH_LONG).show(); +
-            if (adminName != null) { +
-                notifySharingStop(); +
-                ScreenSharingHelper.stopSharing(MainActivity.this, false); +
-            } +
-            adminName = null; +
-            updateUI(); +
-            cancelSharingTimeout(); +
-            scheduleExitOnIdle(); +
-            sharingEngine.disconnect(MainActivity.this, (success, errorReason) -> connect()); +
-       } +
- ···}; +
- +
-    private Runnable overlayDotRunnable = new Runnable() { +
-        @Override +
-        public void run() { +
-            if (overlayDotDirection == 0) { +
-                return; +
-            } +
-            overlayDotAlpha += OVERLAY_DOT_ANIMATION_INCREMENT * overlayDotDirection; +
- ···········if (overlayDotAlpha > 255) +
-               overlayDotAlpha = 255; +
-                overlayDotDirection = -overlayDotDirection; +
-            } +
-            if (overlayDotAlpha < 128) +
-               overlayDotAlpha = 128; +
-                overlayDotDirection = -overlayDotDirection; +
-            } +
-            overlayDot.setImageAlpha(overlayDotAlpha); +
- ···········handler.postDelayed(overlayDotRunnable, OVERLAY_DOT_ANIMATION_DELAY); +
-        } +
-    }; +
- +
-    private void notifySharingStart() { +
-        notifyGestureService(Const.ACTION_SCREEN_SHARING_START); +
-        if (settingsHelper.getBoolean(SettingsHelper.KEY_NOTIFY_SHARING)) { +
-            // Show a flashing dot +
-            Utils.lockDeviceRotation(this, true); +
-            overlayDot = createOverlayDot(); +
-            overlayDotAlpha = 0; +
-            overlayDotDirection = 1; +
-            handler.postDelayed(overlayDotRunnable, OVERLAY_DOT_ANIMATION_DELAY); +
- +
-        } else { +
-            // Just show some dialog to trigger the traffic +
-            final AlertDialog dialog = new AlertDialog.Builder(MainActivity.this) +
-                    .setMessage(R.string.share_start_text) +
-                    .setPositiveButton(R.string.ok, (dialog1, which) -> dialog1.dismiss()) +
-                    .create(); +
-            dialog.show(); +
-            handler.postDelayed(() -> { +
-                if (dialog != null && dialog.isShowing()) { +
-                    try { +
-                        dialog.dismiss(); +
-                    } catch (Exception e) { +
-                    } +
-                } +
-            }, 3000); +
-        } +
-    } +
- +
-    private void notifySharingStop() { +
-        notifyGestureService(Const.ACTION_SCREEN_SHARING_STOP); +
-        if (settingsHelper.getBoolean(SettingsHelper.KEY_NOTIFY_SHARING)) { +
-            overlayDotDirection = 0; +
-            if (overlayDot != null) { +
-                WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); +
-                wm.removeView(overlayDot); +
-                overlayDot = null; +
-            } +
-            Utils.lockDeviceRotation(this, false); +
-        } +
-    } +
- +
-    private void notifyGestureService(String action) { +
-        Intent intent = new Intent(MainActivity.this, GestureDispatchService.class); +
-        intent.setAction(action); +
-        startService(intent); +
-    } +
- +
-    public ImageView createOverlayDot() { +
-        int size = getResources().getDimensionPixelOffset(R.dimen.overlay_dot_size); +
-        WindowManager.LayoutParams params = new WindowManager.LayoutParams(size, size, +
-                Utils.OverlayWindowType(), +
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE +
-                        |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL +
-                        |WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, +
-                PixelFormat.TRANSLUCENT); +
-        params.gravity = Gravity.LEFT | Gravity.TOP; +
-        params.x = getResources().getDimensionPixelOffset(R.dimen.overlay_dot_offset); +
-        params.y = getResources().getDimensionPixelOffset(R.dimen.overlay_dot_offset); +
- +
-        ImageView view = new ImageView(this); +
-        view.setImageResource(R.drawable.flash_dot); +
-        view.setImageAlpha(0); +
-        WindowManager wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE); +
-        wm.addView(view, params); +
-        return view; +
-    } +
-}+
 +[[history_old|Older versions]]
 +~~NOTOC~~
 +~~ARCHIVE=history_old~~

Last modified: by 188.43.235.177