diff --git a/addon/doc/ja/readme.md b/addon/doc/ja/readme.md
index 87635f1..a6bdbd2 100644
--- a/addon/doc/ja/readme.md
+++ b/addon/doc/ja/readme.md
@@ -1,112 +1,135 @@
-# Upside Braille-Down 説明書
+# NVDA Add-on Scons Template #
+
+This package contains a basic template structure for NVDA add-on development, building, distribution and localization.
+For details about NVDA add-on development, please see the [NVDA Add-on Development Guide](https://github.com/nvdaaddons/DevGuide/wiki/NVDA-Add-on-Development-Guide).
+The NVDA add-on development/discussion list [is here](https://nvda-addons.groups.io/g/nvda-addons)
+Information specific to NV Access add-on store [can be found here](https://github.com/nvaccess/addon-datastore).
+Copyright (C) 2012-2023 NVDA Add-on team contributors.
-## 目次
+This package is distributed under the terms of the GNU General Public License, version 2 or later. Please see the file COPYING.txt for further details.
-1. Upside Braille-Down について
-2. 公開場所について
-3. 動作環境
-4. 使い方
-5. 著作権
-6. ご寄付・ご協力のお願い
-7. 更新履歴
-8. 問い合わせ先
-## 1. Upside Braille-Down について
+[alekssamos](https://github.com/alekssamos/) added automatic package of add-ons through Github Actions.
-### 概要
+For details about Github Actions please see the [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions).
-Upside Braille-Down (USB-D) は、NVDAで点字ディスプレイを上下反転状態で使用するためのアドオンです。
+Copyright (C) 2022 alekssamos
-### 特徴
-* 点字ディスプレイを上下サカサマで使用可能
- 点字ディスプレイを上下反転で使用することで、セルが手前にあるディスプレイを使用する際に、PCのキーボードと点字セルをより近づけることができます。
-*点字表示とキースイッチを反転
- 対応点字ディスプレイのセル表示、カーソルルーティングスイッチ(タッチカーソルキー)が反転します。
- また、対応点字ディスプレイでは、行スクロールキー、方向キー、その他一部の点字入力キーも反転します。
-* 反転の有効と無効を切り替え可能
- NVDAメニューから点字ディスプレイを反転するかどうか、そのときの状況によって切り替えることができます。
+## Features
-### 動作確認済み、およびキー反転対応の点字ディスプレイ
+This template provides the following features you can use during NVDA add-on development and packaging:
-以下に、開発者が動作確認した点字ディスプレイを示します。
-うち、キー反転対応作業を行ったものには、(キー反転)と表記してあります。
+* Automatic add-on package creation, with naming and version loaded from a centralized build variables file (buildVars.py) or command-line interface.
+ * See packaging section for details on using command-line switches when packaging add-ons with custom version information.
+ * This process will happen automatically when receiving a pull request, and there is also the possibility of manual launch.
+ * To let the workflow run automatically when pushing to main or master (development) branch, remove the comment for branches line in GitHub Actions (`.github/workflows/build_addon.yml`).
+ * If you have created a tag (E.G.: `git tag v1.0 && git push --tag`), then a release will be automatically created and the add-on file will be uploaded as an asset.
+ * Otherwise, with normal commits or with manual startup, you can download the artifacts from the Actions page of your repository.
+* Manifest file creation using a template (manifest.ini.tpl). Build variables are replaced on this template. See below for add-on manifest specification.
+* Compilation of gettext mo files before distribution, when needed.
+ * To generate a gettext pot file, please run `scons pot`. An `addon-name.pot` file will be created with all gettext messages for your add-on. You need to check the `buildVars.i18nSources` variable to comply with your requirements.
+* Automatic generation of manifest localization files directly from gettext po files. Please make sure buildVars.py is included in i18nFiles.
+* Automatic generation of HTML documents from markdown (.md) files, to manage documentation in different languages.
+* Automatic generation of entries for NV Access add-on store (json format).
-* KGS: Next Touch 40 (キー反転)
-* 日本テレソフト: 精華40 version5 (キー反転)
+In addition, this template includes configuration files for the following tools for use in add-on development and testing (see "additional tools" section for details):
-## 2. 公開場所について
+* Flake8 (flake8.ini): a base configuration file for Flake8 linting tool based on NVDA's own Flake8 configuration file.
+* Configuration for VS Code. It requires NVDA's repo at the same level as the add-on folder containing your actual source files, with prepared source code (`scons source`). preparing the source code is a step in the instructions for building NVDA itself, see [The NVDA Repository](https://github.com/nvaccess/nvda) for details.
+ * Place the .vscode in this repo within the addon folder, where your add-on source files (will) reside. The settings file within this folder assumes the NVDA repository is within the parent folder of this folder. If your addon folder is within the addonTemplate folder, then your NVDA repository folder needs to also be within the addonTemplate folder, or the source will not be found.
+ * Open the addon folder in VS Code. This should initialize VS Code with the correct settings and provide you with code completion and other VS Code features.
+ * Press `control+shift+m` after saving a file to search for problems.
+ * Use arrow and tab keys for the autocompletion feature.
+ * Press `control+shift+p` to open the commands palette and search for recommended extensions to install or check if they are installed.
-本アドオンは、以下のページにて公開しております。
-最新版のアドオンとともに、更新内容なども案内しておりますのでご確認ください。
+## Requirements
-* Upside Braille-Down のページ: https://actlab.org/software/USB-D
+You need the following software to use this code for your NVDA add-on development and packaging:
-## 3. 動作環境
+* a Python distribution (3.7 or later is recommended). Check the [Python Website](https://www.python.org) for Windows Installers. Please note that at present, preparing the NVDA source code requires the 32-bit version of Python 3.7.
+* Scons - [Website](https://www.scons.org/) - version 4.3.0 or later. You can install it via PIP.
+* GNU Gettext tools, if you want to have localization support for your add-on - Recommended. Any Linux distro or cygwin have those installed. You can find windows builds [here](https://gnuwin32.sourceforge.net/downlinks/gettext.php).
+* Markdown 3.3.0 or later, if you want to convert documentation files to HTML documents. You can install it via PIP.
-本アドオンを利用するには、以下の環境が必要です。
+Note, that you may not need these tools in a local build environment, if you are using [Appveyor](https://appveyor.com/) or [GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions), to build and package your add-ons.
-* NVDA 2023.3以降
-* その他、Windows、およびNVDAが快適に動作する環境
+## Usage
-なお、本アドオンを使用するには、対応の点字ディスプレイとそのドライバーが正しくセットアップされている必要があります。
+### To create a new NVDA add-on using this template:
-## 4. 使い方
+1. Create an empty folder to hold the files for your add-on.
+2. Copy the folder:
+```
+site_scons
+```
+and the following files, into your new empty folder:
+```
+buildVars.py
+manifest.ini.tpl
+manifest-translated.ini.tpl
+sconstruct
+.gitignore
+.gitattributes
+```
+3. If you intend to use the provided GitHub workflow, also copy the folder:
+```
+.github
+```
+and file:
+```
+.pre-commit-config.yaml
+```
+4. Create an `addon` folder inside your new folder. You will put your code in the usual folders for NVDA extensions, under the `addon` folder. For instance: `globalPlugins`, `synthDrivers`, etc.
+5. In the `buildVars.py` file, change variable `addon_info` with your add-on's information (name, summary, description, version, author, url, source url, license, and license URL). Also, be sure to carefully set the paths contained in the other variables in that file.
+6. Gettext translations must be placed into `addon\locale\/LC_MESSAGES\nvda.po`.
-### 点字ディスプレイの反転状態を切り替える
+#### Add-on manifest specification
-NVDAメニューから、「Upside Braille-Down」を選択する。
+An add-on manifest generated manually or via `buildVars.py` must include the following information:
-* 「点字ディスプレイの向きをサカサマにする」を選択すると、点字ディスプレイの向きが反転状態になります。
-* 「点字ディスプレイの向きを元に戻す」を選択すると、点字ディスプレイが通常の向きになります。
+* Name (string): a unique identifier for the add-on. It must use camel case (e.g. someModule). This is also used as part of add-on store to identify the add-on uniquely.
+* Summary (string): name as shown on NVDA's Add-ons Manager.
+* Description (string): a short detailed description about the add-on.
+* Version (string), ideally number.number with an optional third number, denoting major.minor.patch.
+* Author (string and an email address): one or more add-on author contact information in the form "name ".
+* URL (string): a web address where the add-on information can be found (typically community add-ons website address (https://addons.nvda-project.org) is used).
+* docFileName (string): name of the documentation file.
+* minimumNVDAVersion (year.major or year.major.minor): the earliest version of NVDA the add-on is compatible with (e.g. 2019.3). Add-ons are expected to use features introduced in this version of NVDA or declare compatibility with it.
+* lastTestedNVDAVersion (year.major or year.major.minor): the latest or last tested version of NVDA the add-on is said to be compatible with (e.g. 2020.3). Add-on authors are expected to declare this value after testing add-ons with the version of NVDA specified.
+* addon_updateChannel (string or None): the update channel for the add-on release.
-なお、NVDA起動時には、前回終了時の設定になっています。
+In addition, the following information must be filled out (not used in the manifest but used elsewhere such as add-on store):
+* sourceURL (string): repository URL for the add-on source code.
+* license (string): the license of the add-on and its source code.
+* licenseURL: the URL for the license file.
-### アップデートの確認と実行
+### To manage documentation files for your addon:
-本アドオンは、機能の更新、および不具合の修正などのため、アップデートが提供されることがあります。
-アドオンのアップデートは、NVDAメニューから「Upside Braille-Down」を選択し、「アップデートの確認」を実行することでいつでも確認することができます。
-アップデートが見つかると、更新を促すメッセージが表示されます。案内に従ってアップデート作業を行ってください。
+1. Copy the `readme.md` file for your add-on to the first created folder, where you copied `buildVars.py`. You can also copy `style.css` to improve the presentation of HTML documents.
+2. Documentation files (named `readme.md`) must be placed into `addon\doc\/`.
-また、本アドオンには、NVDA起動時に自動でアップデートを確認する機能が搭載されています。
-NVDAメニューから「Upside Braille-Down」を選択し、「起動時のアップデートの確認を無効化」あるいは「起動時のアップデートの確認を有効化」を実行することで設定を変更できます。
-
-## 5. 著作権
-
-(c) 2021 Hiroki Fujii - ACT Laboratory
-
-GNU General Public License, version 2 or later. (一部を除く)
-
-* URL: https://actlab.org/
-
-
-## 6. ご寄付・ご協力のお願い
-
-ACT Laboratory(Accessible Tools Laboratory)は、プログラミングを学ぶ視覚障害者の集まりです。
-本アドオンは無償ですが、公開には多少の経費も掛かっています。
-
-本アドオンを気に入っていただけた方などで、活動にご支援・ご協力を頂ける方がいらっしゃいましたら、ぜひお力をお貸しください。
-なお、ご支援を頂きました方につきましては、TwitterやACT Laboratoryサイトにてご紹介させて頂く予定です。
-
-詳しくはこちらへ
-https://actlab.org/donate/
-
-
-## 7. 更新履歴
-
-* 1.0.0 (2024/02/23)
- * 初回リリース
-
-## 8. 問い合わせ先
-
-本アドオンを利用しての感想やご要望、不具合のご報告などは、以下のメールアドレスまたは掲示板へご連絡ください。
-
-* ACT Laboratory サポート: support@actlab.org
-* ACT Laboratory 掲示板: https://actlab.org/bbs/
-
-GitHubリポジトリは、こちらです。
-
-* https://github.com/actlaboratory/USB-D
+### To package the add-on for distribution:
+
+1. Open a command line, change to the folder that has the `sconstruct` file (usually the root of your add-on development folder) and run the `scons` command. The created add-on, if there were no errors, is placed in the current directory.
+2. You can further customize variables in the `buildVars.py` file.
+3. You can also customize version and update channel information from command line by passing the following switches when running scons:
+ * version: add-on version string.
+ * versionNumber: add-on version number of the form major.minor.patch (all integers)
+ * channel: update channel (do not use this switch unless you know what you are doing).
+ * dev: suitable for development builds, names the add-on according to current date (yyyymmdd) and sets update channel to "dev".
+
+### Additional tools
+
+The template includes configuration files for use with additional tools such as linters. These include:
+
+* Flake8 (flake8.ini): a Python code linter (3.7.9 or later, can be installed with PIP).
+
+Read the documentation for the tools you wish to use when building and developing add-ons.
+
+Note that this template only provides a basic add-on structure and build infrastructure. You may need to adapt it for your specific needs such as using additional tools.
+
+If you have any issues please use the NVDA addon list mentioned above.
diff --git a/addon/globalPlugins/USB-D/__init__.py b/addon/globalPlugins/USB-D/__init__.py
index dd2370a..f4126b3 100644
--- a/addon/globalPlugins/USB-D/__init__.py
+++ b/addon/globalPlugins/USB-D/__init__.py
@@ -9,6 +9,7 @@
from .inputManagerPatch import InputManagerPatch
from .brailleHandlerPatch import BrailleHandlerPatch
from . import configUtil
+from . import updater
try:
import addonHandler
@@ -21,7 +22,11 @@ class GlobalPlugin(globalPluginHandler.GlobalPlugin):
def __init__(self, *args, **kwargs):
super(GlobalPlugin, self).__init__(*args, **kwargs)
configUtil.initializeSettings()
-
+
+ if configUtil.getAutoUpdateCheckSetting():
+ self.autoUpdateChecker = updater.AutoUpdateChecker()
+ self.autoUpdateChecker.autoUpdateCheck(mode=updater.AUTO)
+
BrailleHandlerPatch.handlerWriteCellsOriginal = braille.BrailleHandler._writeCells
braille.BrailleHandler._writeCells = BrailleHandlerPatch.handlerWriteCells
BrailleHandlerPatch.handleBgThreadExecutorOriginal = braille.BrailleHandler._bgThreadExecutor
@@ -32,6 +37,7 @@ def __init__(self, *args, **kwargs):
def terminate(self):
super(GlobalPlugin, self).terminate()
+ self.autoUpdateChecker.terminate()
try:
gui.mainFrame.sysTrayIcon.menu.Remove(self.rootMenuItem)
except BaseException:
@@ -45,6 +51,23 @@ def _setupMenu(self):
)
gui.mainFrame.sysTrayIcon.Bind(
wx.EVT_MENU, self.toggleTurnOverState, self.turnOverStateToggleItem)
+
+ self.updateCheckToggleItem = self.rootMenu.Append(
+ wx.ID_ANY,
+ self._updateCheckToggleString(),
+ _("起動時に自動でアップデートを確認するかどうかを切り替えます。")
+ )
+ gui.mainFrame.sysTrayIcon.Bind(
+ wx.EVT_MENU, self.toggleUpdateCheck, self.updateCheckToggleItem)
+
+ self.updateCheckPerformItem = self.rootMenu.Append(
+ wx.ID_ANY,
+ _("アップデートを確認"),
+ _("アップデートを手動で確認します。")
+ )
+ gui.mainFrame.sysTrayIcon.Bind(
+ wx.EVT_MENU, self.performUpdateCheck, self.updateCheckPerformItem)
+
self.rootMenuItem = gui.mainFrame.sysTrayIcon.menu.Insert(
2, wx.ID_ANY, _("Upside Braille-Down"), self.rootMenu)
@@ -53,9 +76,26 @@ def _setupMenu(self):
def _turnOverStateToggleString(self):
return _("点字ディスプレイの向きを元に戻す(&S)") if configUtil.getEnableTurnOverSetting() else _("点字ディスプレイの向きをサカサマにする(&S)")
+ def _updateCheckToggleString(self):
+ return _("起動時のアップデートの確認を無効化") if configUtil.getAutoUpdateCheckSetting() else _("起動時のアップデートの確認を有効化")
+
+
def toggleTurnOverState(self, evt=None):
self.script_toggleTurnOverState()
def script_toggleTurnOverState(self, gesture=None):
configUtil.setEnableTurnOverSetting(not configUtil.getEnableTurnOverSetting())
self.turnOverStateToggleItem.SetItemLabel(self._turnOverStateToggleString())
+
+ def toggleUpdateCheck(self, evt):
+ changed = not configUtil.getAutoUpdateCheckSetting()
+ configUtil.setAutoUpdateCheckSetting(changed)
+ msg = _("NVDA起動時に、自動でアップデートを確認します。") if changed is True else _(
+ "NVDA起動時に、自動でアップデートを確認しません。")
+ self.updateCheckToggleItem.SetItemLabel(self._updateCheckToggleString())
+ gui.messageBox(msg, _("設定完了"))
+
+ def performUpdateCheck(self, evt):
+ updater.AutoUpdateChecker().autoUpdateCheck(mode=updater.MANUAL)
+
+
diff --git a/addon/globalPlugins/USB-D/configUtil.py b/addon/globalPlugins/USB-D/configUtil.py
index ab2df17..f3569fa 100644
--- a/addon/globalPlugins/USB-D/configUtil.py
+++ b/addon/globalPlugins/USB-D/configUtil.py
@@ -4,6 +4,14 @@
CONFIG_GLOBAL_KEY = "actlab_USB-D_global"
+def getAutoUpdateCheckSetting():
+ try:
+ return config.conf[CONFIG_GLOBAL_KEY]["checkForUpdatesOnStartup"]
+ except:
+ return False
+def setAutoUpdateCheckSetting(val):
+ config.conf[CONFIG_GLOBAL_KEY]["checkForUpdatesOnStartup"] = val
+
def getEnableTurnOverSetting():
try:
return config.conf[CONFIG_GLOBAL_KEY]["enableTurnOver"]
@@ -14,6 +22,8 @@ def setEnableTurnOverSetting(val):
config.conf[CONFIG_GLOBAL_KEY]["enableTurnOver"] = val
def initializeSettings():
- config.conf[CONFIG_GLOBAL_KEY] = {
- "enableTurnOver": True
- }
+ if (not hasattr(config.conf, CONFIG_GLOBAL_KEY)) or (not hasattr(config.conf[CONFIG_GLOBAL_KEY], "enableTurnOver")):
+ setEnableTurnOverSetting(True)
+ else:
+ setEnableTurnOverSetting(getEnableTurnOverSetting())
+ setAutoUpdateCheckSetting(getAutoUpdateCheckSetting())
diff --git a/addon/globalPlugins/USB-D/constants.py b/addon/globalPlugins/USB-D/constants.py
new file mode 100644
index 0000000..f3d69fa
--- /dev/null
+++ b/addon/globalPlugins/USB-D/constants.py
@@ -0,0 +1,26 @@
+import addonHandler, config, languageHandler, os
+
+lang = languageHandler.getLanguage().split("_")[0]
+addonDir = os.path.abspath(os.path.dirname(__file__))
+addonRootDir = os.path.abspath(os.path.join(addonDir, "..", ".."))
+
+curAddon = addonHandler.Addon(addonRootDir)
+addonName = curAddon.manifest["name"]
+addonSummary = curAddon.manifest["summary"]
+addonVersion = curAddon.manifest["version"]
+addonDocFileName = curAddon.manifest["docFileName"]
+homepageURL = "https://actlab.org"
+updateURL = "%s/api/checkUpdate" % homepageURL
+
+UPDATER_NEED_UPDATE = 200
+UPDATER_LATEST = 204
+UPDATER_VISIT_SITE = 205
+UPDATER_BAD_PARAM = 400
+UPDATER_NOT_FOUND = 404
+
+if os.path.isfile(os.path.join(addonRootDir, "doc", lang, addonDocFileName)):
+ docFilePath = os.path.join(addonRootDir, "doc", lang, addonDocFileName)
+elif os.path.isfile(os.path.join(addonRootDir, "doc", "en", addonDocFileName)):
+ docFilePath = os.path.join(addonRootDir, "doc", "en", addonDocFileName)
+else:
+ docFilePath = None
diff --git a/addon/globalPlugins/USB-D/translate.py b/addon/globalPlugins/USB-D/translate.py
new file mode 100644
index 0000000..81e1664
--- /dev/null
+++ b/addon/globalPlugins/USB-D/translate.py
@@ -0,0 +1,2 @@
+def translate(text):
+ return _(text)
diff --git a/addon/globalPlugins/USB-D/updater.py b/addon/globalPlugins/USB-D/updater.py
new file mode 100644
index 0000000..7e0dbef
--- /dev/null
+++ b/addon/globalPlugins/USB-D/updater.py
@@ -0,0 +1,261 @@
+from __future__ import unicode_literals
+import addonHandler
+import globalVars
+import gui
+import hashlib
+import json
+import os
+import sys
+import tempfile
+import threading
+import time
+import versionInfo
+import winreg
+import wx
+from logHandler import log
+from urllib.request import Request, urlopen
+from urllib.parse import urlencode
+from .constants import *
+from .translate import *
+
+try:
+ import addonHandler
+ addonHandler.initTranslation()
+except BaseException:
+ def _(x): return x
+
+try:
+ import updateCheck
+ if (globalVars.appArgs.install and globalVars.appArgs.minimal):
+ updatable = False
+ elif globalVars.appArgs.secure or config.isAppX:
+ updatable = False
+ else:
+ updatable = True
+except RuntimeError:
+ updatable = False
+
+AUTO=0
+MANUAL=1
+
+class AutoUpdateChecker:
+ def __init__(self):
+ self.updater = None
+ if not updatable:
+ log.warning("Update check not supported.")
+
+ def autoUpdateCheck(self, mode=AUTO):
+ """
+ Call this method to check for updates. mode=AUTO means automatic update check, and MANUAL means manual triggering like "check for updates" menu invocation.
+ When set to AUTO mode, some dialogs are not displayed (latest and error).
+ """
+ if not updatable:
+ return
+ self.updater = NVDAAddOnUpdater(mode)
+
+class NVDAAddOnUpdater ():
+ def __init__(self, mode, version=addonVersion):
+ self.mode = mode
+ self.version = version
+ if updatable:
+ t = threading.Thread(target=self.check_update)
+ t.daemon = True
+ t.start()
+
+ def check_update(self):
+ """Called as the thread entry point."""
+ post_params = {
+ "name": addonName,
+ "version": addonVersion,
+ "updater_version": "1.0.0",
+ }
+ req = Request("%s?%s" % (updateURL, urlencode(post_params)))
+ try:
+ f = urlopen(req)
+ except BaseException:
+ if self.mode == MANUAL:
+ gui.messageBox(_("Unable to connect to update server.\nCheck your internet connection."),
+ _("Error"), style=wx.CENTER | wx.ICON_WARNING)
+ return False
+
+ if f.getcode() != 200:
+ if self.mode == MANUAL:
+ gui.messageBox(_("Unable to connect to update server."), _("Error"), style=wx.CENTER | wx.ICON_WARNING)
+ return False
+
+ try:
+ update_dict = f.read().decode("utf-8")
+ f.close()
+ update_dict = json.loads(update_dict)
+ except BaseException:
+ if self.mode == MANUAL:
+ gui.messageBox(
+ _("The update information is invalid.\nPlease contact ACT Laboratory for further information."),
+ _("Error"),
+ style=wx.CENTER | wx.ICON_WARNING)
+ return False
+
+ code = update_dict["code"]
+ if code == UPDATER_LATEST:
+ if self.mode == MANUAL:
+ gui.messageBox(_("No updates found.\nYou are using the latest version."), _("Update check"), style=wx.CENTER | wx.ICON_INFORMATION)
+ return False
+ elif code == UPDATER_BAD_PARAM:
+ if self.mode == MANUAL:
+ gui.messageBox(_("The request parameter is invalid. Please contact the developer."),
+ _("Update check"), style=wx.CENTER | wx.ICON_INFORMATION)
+ return False
+ elif code == UPDATER_NOT_FOUND:
+ if self.mode == MANUAL:
+ gui.messageBox(_("The updater is not registered. Please contact the developer."),
+ _("Update check"), style=wx.CENTER | wx.ICON_INFORMATION)
+ return False
+ elif code == UPDATER_VISIT_SITE:
+ if self.mode == MANUAL:
+ gui.messageBox(_("An update was found, but updating from the current version is not possible. Please visit the software's website. "),
+ _("Update check"), style=wx.CENTER | wx.ICON_INFORMATION)
+ return False
+
+ new_version = update_dict["update_version"]
+ url = update_dict["updater_url"]
+ if "updater_hash" not in update_dict or update_dict["updater_hash"] is None or update_dict["updater_hash"] == "":
+ hash = None
+ else:
+ hash = update_dict["updater_hash"]
+ # end set hash
+
+ caption = _("Update confirmation")
+ question = _("{summary} Ver.{newVersion} is available.\nWould you like to update?\nCurrent version: {currentVersion}\nNew version: {newVersion}").format(
+ summary=addonSummary, newVersion=new_version, currentVersion=addonVersion)
+ answer = gui.messageBox(question, caption, style=wx.CENTER | wx.OK | wx.CANCEL | wx.CANCEL_DEFAULT | wx.ICON_INFORMATION)
+ if answer == wx.OK:
+ downloader = UpdateDownloader(addonName, [url], hash)
+ wx.CallAfter(downloader.start)
+ return
+ else:
+ return True
+
+
+class UpdateDownloader(updateCheck.UpdateDownloader):
+ def __init__(self, addonCode, urls, fileHash=None):
+ try:
+ super(UpdateDownloader, self).__init__(urls, fileHash)
+ except BaseException:
+ pass
+ self.urls = urls
+ self.fp = tempfile.NamedTemporaryFile(prefix="%s_update_" % addonCode, suffix=".nvda-addon", mode="wb", delete=False)
+ self.destPath = self.fp.name
+ self.fileHash = fileHash
+
+ def start(self):
+ self._shouldCancel = False
+ self._guiExecTimer = wx.PyTimer(self._guiExecNotify)
+ gui.mainFrame.prePopup()
+ self._progressDialog = wx.ProgressDialog(_("Downloading add-on update"),
+ _("Connecting"),
+ style=wx.PD_CAN_ABORT | wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME | wx.PD_AUTO_HIDE,
+ parent=gui.mainFrame)
+ self._progressDialog.Raise()
+ t = threading.Thread(target=self._bg)
+ t.daemon = True
+ t.start()
+
+ def _error(self):
+ self._stopped()
+ self.cleanup_tempfile()
+ gui.messageBox(
+ _("Error downloading add-on update."),
+ translate("Error"),
+ wx.OK | wx.ICON_ERROR)
+
+ def _download(self, url):
+ headers = {}
+ if updaterUserAgent:
+ headers["User-Agent"] = updaterUserAgent
+ req = Request(url, headers=headers)
+ try:
+ remote = urlopen(req, timeout=120)
+ except BaseException:
+ raise RuntimeError("Download failed")
+ return
+ if remote.code != 200:
+ raise RuntimeError("Download failed with code %d" % remote.code)
+ size = int(remote.headers["content-length"])
+ if self.fileHash:
+ hasher = hashlib.sha1()
+ self._guiExec(self._downloadReport, 0, size)
+ read = 0
+ chunk = 8192
+ while True:
+ if self._shouldCancel:
+ self.fp.close()
+ self.cleanup_tempfile()
+ return
+ if size - read < chunk:
+ chunk = size - read
+ block = remote.read(chunk)
+ if not block:
+ break
+ read += len(block)
+ if self._shouldCancel:
+ self.fp.close()
+ self.cleanup_tempfile()
+ return
+ self.fp.write(block)
+ if self.fileHash:
+ hasher.update(block)
+ self._guiExec(self._downloadReport, read, size)
+ if read < size:
+ raise RuntimeError("Content too short")
+ if self.fileHash and hasher.hexdigest().lower() != self.fileHash.lower():
+ raise RuntimeError("Content has incorrect file hash")
+ self.fp.close()
+ self._guiExec(self._downloadReport, read, size)
+
+ def _downloadSuccess(self):
+ self._stopped()
+ try:
+ try:
+ bundle = addonHandler.AddonBundle(self.destPath.decode("mbcs"))
+ except AttributeError:
+ bundle = addonHandler.AddonBundle(self.destPath)
+ except BaseException:
+ log.error("Error opening addon bundle from %s" % self.destPath, exc_info=True)
+ gui.messageBox(translate("Failed to open add-on package file at %s - missing file or invalid file format") % self.destPath,
+ translate("Error"),
+ wx.OK | wx.ICON_ERROR)
+ return
+ bundleName = bundle.manifest['name']
+ for addon in addonHandler.getAvailableAddons():
+ if not addon.isPendingRemove and bundleName == addon.manifest['name']:
+ addon.requestRemove()
+ break
+ progressDialog = gui.IndeterminateProgressDialog(gui.mainFrame,
+ _("Updating add-on"),
+ _("Please wait while the add-on is being updated."))
+ try:
+ gui.ExecAndPump(addonHandler.installAddonBundle, bundle)
+ except BaseException:
+ log.error("Error installing addon bundle from %s" % self.destPath, exc_info=True)
+ progressDialog.done()
+ del progressDialog
+ gui.messageBox(_("Failed to update add-on from %s.") % self.destPath,
+ translate("Error"),
+ wx.OK | wx.ICON_ERROR)
+ return
+ else:
+ progressDialog.done()
+ del progressDialog
+ finally:
+ self.cleanup_tempfile()
+ from gui import addonGui
+ addonGui.promptUserForRestart()
+
+ def cleanup_tempfile(self):
+ if not os.path.isfile(self.destPath):
+ return
+ try:
+ os.remove(self.destPath)
+ except BaseException:
+ pass
+ return
diff --git a/buildVars.py b/buildVars.py
index 074e1e9..ef2da22 100644
--- a/buildVars.py
+++ b/buildVars.py
@@ -14,7 +14,7 @@ def _(arg):
ADDON_NAME = "UpSideBraille-Down"
ADDON_KEYWORD = "USB-D"
-ADDON_VERSION = "0.1.0"
+ADDON_VERSION = "1.0.1"
ADDON_RELEASE_DATE = "2024-02-23"
# Add-on information variables
@@ -46,11 +46,11 @@ def _(arg):
# Add-on update channel (default is None, denoting stable releases,
# and for development releases, use "dev".)
# Do not change unless you know what you are doing!
- "addon_updateChannel": None,
+ "addon_updateChannel": "stable",
# Add-on license such as GPL 2
- "addon_license": None,
+ "addon_license": "GPL 2",
# URL for the license document the ad-on is licensed under
- "addon_licenseURL": None,
+ "addon_licenseURL": "https://www.gnu.org/licenses/old-licenses/gpl-2.0.html",
}
# Define the python files that are the sources of your add-on.