Compare commits

...

113 commits
v2.8.0 ... main

Author SHA1 Message Date
Tom Fong
e7ba5042ee
Update README.md 2024-03-31 02:39:34 +08:00
Tom Fong
4e436c70ce
Merge pull request #77 from tomfong/dev/v4.1
Dev/v4.1
2024-03-31 02:28:46 +08:00
Tom Fong
350da61d17 docs: update 2024-03-31 02:19:05 +08:00
Tom Fong
f6ab4c1e4e update README 2024-03-31 02:14:56 +08:00
Tom Fong
1b0d12eb65 chore: version bump 2024-03-31 02:08:26 +08:00
Tom Fong
f1244c4fbd style 2024-03-31 01:59:56 +08:00
Tom Fong
62dda58723 feat: support geolocation QR code 2024-03-31 01:55:57 +08:00
Tom Fong
03c04c94d1 feat: add Portuguese (Brazil) language 2024-03-31 00:43:10 +08:00
Tom Fong
91b81b1054 feat: now won't show release note after update 2024-03-31 00:11:12 +08:00
Tom Fong
84dc016cb4 feat: default auto url opening changed to off 2024-03-31 00:08:33 +08:00
Tom Fong
ca629c760d chore: update dependencies 2024-03-30 23:56:28 +08:00
Tom Fong
d11464883c chore: regular update on dependencies 2023-07-15 18:32:06 +08:00
Tom Fong
244ee01e2b fix: import image failure on Android 2023-07-15 17:35:07 +08:00
Tom Fong
0a798516d1
Update README.md 2023-07-07 14:13:50 +08:00
Tom Fong
311ed12770
Merge pull request #70 from tomfong/dev/v4
Version 4.0.0
2023-07-07 14:13:18 +08:00
Tom Fong
10ed53b92f Create output-metadata.json 2023-07-07 14:12:33 +08:00
Tom Fong
c536146abb doc: update README 2023-07-07 14:01:42 +08:00
Tom Fong
691eebd416 chore: version bump 2023-07-07 13:55:10 +08:00
Tom Fong
e3a1961773 fix: enable inner html 2023-07-07 13:52:51 +08:00
Tom Fong
5210141b9c feat: directly edit content of QR ccode in Result page 2023-07-07 13:45:16 +08:00
Tom Fong
475146a8a0 feat: auto open url 2023-07-07 12:41:33 +08:00
Tom Fong
0fd54cf17f adjust haptics 2023-07-01 11:39:11 +08:00
Tom Fong
57577d257d chore: update dependencies 2023-07-01 11:34:02 +08:00
Tom Fong
8ca3a493c6 chore: upgrade dependencies 2023-07-01 11:26:02 +08:00
Tom Fong
b4ada90d22 chore: upgrade @angular/material 2023-07-01 11:16:31 +08:00
Tom Fong
043d5b3462 chore: upgrade to Angular 16 2023-07-01 11:08:48 +08:00
Tom Fong
4e5134bdaf chore 2023-07-01 11:02:43 +08:00
Tom Fong
2128a7b9ae chore: update dependencies 2023-06-29 21:02:40 +08:00
Tom Fong
26d28e811f feat: support Brave Search 2023-06-27 12:14:47 +08:00
Tom Fong
f8bfdcf52e chore: upgrade @capacitor-community plugins 2023-06-27 11:57:42 +08:00
Tom Fong
94bd973730 chore: upgrade to capacitor 5 2023-06-18 12:10:48 +08:00
Tom Fong
75caab2976 chore: upgrade to ionic 7 2023-06-18 11:48:00 +08:00
Tom Fong
59791dfb42 chore: upgrade @angular/material, @ionic/storage, @ngx-translate and change to use angularx-qrcode 2023-06-17 21:25:27 +08:00
Tom Fong
f5dd413a93 chore: upgrade to Angular 15 2023-06-11 12:17:04 +08:00
Tom Fong
68dbecdbee chore: update dependencies 2023-06-11 11:47:59 +08:00
Tom Fong
6812279341
Merge pull request #66 from tomfong/dev/post-v3.2.0
Release v3.3.0
2022-12-01 21:10:52 +08:00
Tom Fong
ae1a4847d0 doc: update fastlane 2022-12-01 21:06:53 +08:00
Tom Fong
3f4a9220d9 chore: version bump 2022-12-01 21:02:58 +08:00
Tom Fong
b9d329bd13 chore: regular update on dependencies 2022-12-01 20:59:53 +08:00
Tom Fong
90ebc52537 feat: allow manually saving if auto logging is off 2022-12-01 20:57:26 +08:00
Tom Fong
3a42fb84ef feat: russian language adjustment 2022-11-30 18:00:52 +08:00
Tom Fong
01d67ec2a8 feat: russian language adjustment 2022-11-30 18:00:49 +08:00
Tom Fong
493bfd2d64
Merge pull request #65 from Smooth-E/russian-translations
Russian translations
2022-11-30 17:35:16 +08:00
Smooth-E
1575d7ec92 Improve Russian translations. Add Android values files. Add settings entry for the language. 2022-11-29 23:48:23 +05:00
Smooth-E
41de9c763e Translate JSON into Russian 2022-11-29 18:26:36 +05:00
Tom Fong
262459f1e1 feat: show rate the app on setting menu 2022-11-24 11:28:27 +08:00
Tom Fong
04949d2bf9 feat: allow user to hide exit app alert 2022-11-24 11:01:07 +08:00
Tom Fong
c1c8a2171e chore: regular update on dependencies 2022-11-24 09:43:30 +08:00
Tom Fong
7a6c4115f3
Merge pull request #64 from tomfong/changelog
doc: update fastlane docs
2022-11-24 09:17:47 +08:00
Tom Fong
fe2833c41b doc: update fastlane docs 2022-11-24 09:16:21 +08:00
Tom Fong
bede343db9
Create FUNDING.yml 2022-11-21 20:12:17 +08:00
Tom Fong
f87717efb2
Update README.md 2022-11-21 19:51:36 +08:00
Tom Fong
d4b42cb09a
Merge pull request #60 from tomfong/post-v3.1.0-dev
v3.2.0
2022-11-21 12:10:57 +08:00
Tom Fong
b4e42d1528 chore: version bump 2022-11-21 12:03:37 +08:00
Tom Fong
1291878921 style: color adjustment 2022-11-21 11:54:10 +08:00
Tom Fong
f38b266dde chore: regular update on dependencies 2022-11-21 11:46:14 +08:00
Tom Fong
525eb600a3 style: wordings 2022-11-21 11:40:05 +08:00
Tom Fong
dd41e28c28 feat: support Open Food Facts 2022-11-21 11:37:35 +08:00
Tom Fong
1830a78c54 fix: export bookmarks as well even if they are not in history 2022-11-21 10:54:24 +08:00
Tom Fong
d21f5d98b9 chore: regular update on dependencies 2022-11-15 09:48:54 +08:00
Tom Fong
6f29f52a96 chore: regular update on dependencies 2022-11-10 10:47:08 +08:00
Tom Fong
28e21f7de2 chore: update @capacitor-community/screen-brightness 2022-10-27 09:53:16 +08:00
Tom Fong
fd5517767b chore: regular update on dependencies 2022-10-27 09:48:16 +08:00
Tom Fong
a7ba31fe45 chore: regular update on dependencies 2022-10-25 11:30:01 +08:00
Tom Fong
01de9b31c9 feat(ios): show splash screen when app pause 2022-10-21 12:00:17 +08:00
Tom Fong
9263e05a2d
Merge pull request #58 from tomfong/post-v3.0.1-dev
v3.1.0
2022-10-18 12:59:08 +08:00
Tom Fong
c3eece4717 update release notes 2022-10-18 12:48:42 +08:00
Tom Fong
92d355ae1c chore: update dependencies 2022-10-18 12:31:50 +08:00
Tom Fong
20e105c68f fix: color detection not work 2022-10-18 12:28:14 +08:00
Tom Fong
0e1661fe4b fix: make csv content safer 2022-10-18 12:24:15 +08:00
Tom Fong
4112190dbc style 2022-10-18 12:18:07 +08:00
Tom Fong
6aa3746f09 feat: export to CSV 2022-10-18 12:13:38 +08:00
Tom Fong
786528f52b chore: upgrade @awesome-cordova-plugins 2022-10-16 14:42:13 +08:00
Tom Fong
1975b9a6f7 chore: version bump 2022-10-16 14:21:46 +08:00
Tom Fong
484004ce1f style: change wordings 2022-10-16 14:10:35 +08:00
Tom Fong
63ea056c2a chore: change to use @capacitor/preferences as storage 2022-10-16 14:05:25 +08:00
Tom Fong
438768d7b7 feat: support Ecosia as search engine 2022-10-15 12:51:19 +08:00
Tom Fong
f2bde77358
Update README.md 2022-10-15 12:31:39 +08:00
Tom Fong
570de3a8df fix: make save scan records safer 2022-10-15 12:29:00 +08:00
Tom Fong
0b7a624ba9 refactor 2022-10-15 11:23:18 +08:00
Tom Fong
3363600ea9 chore: regular update on dependencies 2022-10-15 11:17:47 +08:00
Tom Fong
521445c3e3 style: adjust tutorial modal sheet 2022-10-04 16:47:28 +08:00
Tom Fong
cf2cb349c9 refactor 2022-10-04 16:35:26 +08:00
Tom Fong
d3d154ea54 debug: remove SplashScreen.hide() on tabs.page 2022-10-04 10:13:03 +08:00
Tom Fong
df225cc67e style: change presentation of tutorials 2022-10-03 22:11:23 +08:00
Tom Fong
53034c6077 fix: set defaultHref for header back-button 2022-10-03 20:41:01 +08:00
Tom Fong
7f4c827772
Merge pull request #56 from tomfong/post-v3.0.0-dev
v3.0.1
2022-09-25 13:51:59 +08:00
Tom Fong
628ff95151 chore: bump version 2022-09-25 13:49:45 +08:00
Tom Fong
a6a73d21e9 style: splash screen adjustment (android) 2022-09-25 13:39:15 +08:00
Tom Fong
598536896c fix: open link button disappear under some settings 2022-09-25 12:46:40 +08:00
Tom Fong
f5f24ce3ff chore: remove deprecated devDependencies 2022-09-24 20:45:12 +08:00
Tom Fong
9be426a274 chore: install ionic cli 2022-09-24 20:43:05 +08:00
Tom Fong
1929325572 chore: angular.json modification 2022-09-24 20:39:58 +08:00
Tom Fong
fe1422a370 chore: remove devDependency codelyzer 2022-09-24 20:38:32 +08:00
Tom Fong
f9c5752fc9 chore: replace moment.js by date-fns 2022-09-24 20:34:01 +08:00
Tom Fong
5c6add7f3e chore: update @ng-bootstrap/ng-bootstrap 2022-09-24 09:03:39 +08:00
Tom Fong
07b99da79c
Merge pull request #54 from tomfong/post-v2.8.0-dev
3.0.0
2022-09-23 19:19:57 +08:00
Tom Fong
0ae5f16abc chore: remove write contacts permission on android 2022-09-23 19:15:24 +08:00
Tom Fong
e4b01640a4 doc: update readme and fastlane 2022-09-23 18:39:56 +08:00
Tom Fong
30e322f471 feat: show version number once after update 2022-09-23 18:30:52 +08:00
Tom Fong
8226ba0182 style: update patch notes for Android 2022-09-23 18:14:39 +08:00
Tom Fong
b84520df59 fix: removed read contact permission on android 2022-09-23 17:50:03 +08:00
Tom Fong
0ebd35c546 test 2022-09-23 17:10:50 +08:00
Tom Fong
4069ba5f83 chore: upgrade to angular 14 (material) 2022-09-23 17:09:23 +08:00
Tom Fong
57dff9e531 chore: upgrade to angular 14 (cli, core) 2022-09-23 17:06:42 +08:00
Tom Fong
031d4dc233 style: change on/off options from radio to toggle 2022-09-23 16:57:35 +08:00
Tom Fong
08702a4af2 feat: show number of records option 2022-09-23 16:27:31 +08:00
Tom Fong
87a4e8f0b9 style: add safe area on tutorial pages 2022-09-23 15:54:41 +08:00
Tom Fong
88d68e97a1 chore: upgrade to capacitor 4 2022-09-23 15:27:03 +08:00
Tom Fong
2a60bdff50 Revert "chore: upgrade capacitor" 2022-09-17 01:48:50 +08:00
Tom Fong
893b3b51c2 chore: update dependencies 2022-09-17 00:04:51 +08:00
Tom Fong
e294228ab4 chore: upgrade capacitor 2022-09-16 23:57:14 +08:00
Tom Fong
c357d5dc83 feat: support QS tile for android 2022-09-16 20:52:44 +08:00
152 changed files with 11875 additions and 18646 deletions

1
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1 @@

View file

@ -20,13 +20,13 @@
## About
Simple QR is a FOSS app to scan, create and store QR codes with a simple UI and experience. No backend service connected. No data collected. No ads.
Simple QR is an open-source app to scan, create and store QR codes with a simple UI and experience. No backend service connected. No data collected. No ads.
It's now available on the following platforms.
| App Store | Google Play | GitHub | IzzyOnDroid |
|:-:|:-:|:-:|:-:|
| [<img src="https://raw.githubusercontent.com/tomfong/simple-qr/main/.github/images/appstore-badge.png" height="50">](https://apps.apple.com/us/app/simple-qr-by-tom-fong/id1621121553) | [<img src="https://raw.githubusercontent.com/tomfong/simple-qr/main/.github/images/google-play-badge.png" height="50">](https://play.google.com/store/apps/details?id=com.tomfong.simpleqr) | [<img src="https://raw.githubusercontent.com/tomfong/simple-qr/main/.github/images/github-badge.png" height="50">](https://github.com/tomfong/simple-qr/releases/latest) | [<img src="https://raw.githubusercontent.com/tomfong/simple-qr/main/.github/images/IzzyOnDroid-badge.png" height="50">](https://apt.izzysoft.de/fdroid/index/apk/com.tomfong.simpleqr) |
| Google Play | GitHub | IzzyOnDroid |
|:-:|:-:|:-:|
| [<img src="https://raw.githubusercontent.com/tomfong/simple-qr/main/.github/images/google-play-badge.png" height="50">](https://play.google.com/store/apps/details?id=com.tomfong.simpleqr) | [<img src="https://raw.githubusercontent.com/tomfong/simple-qr/main/.github/images/github-badge.png" height="50">](https://github.com/tomfong/simple-qr/releases/latest) | [<img src="https://raw.githubusercontent.com/tomfong/simple-qr/main/.github/images/IzzyOnDroid-badge.png" height="50">](https://apt.izzysoft.de/fdroid/index/apk/com.tomfong.simpleqr) |
## Features
@ -36,7 +36,7 @@ By using the app, you can
2. Import image files and scan the QR Code on it.
3. Create QR code from templates, which includes Free Text, URL, vCard Contact, Phone Number, Message, Email and Wi-Fi.
3. Create QR code from templates, which includes Free Text, URL, vCard Contact, Phone Number, Message, Email, Wi-Fi and Geolocation.
4. Automatically log results that you scan, create or view again. These logged records can be bookmarked for quick access, and also backupable.
@ -46,11 +46,12 @@ By using the app, you can
* Execute base64 encoding/decoding on it.
* Use it as a content to generate a new shareable QR code.
* Do corresponding tasks if it is a
* URL: Browse website
* URL: Browse website / Open application
* vCard contact: Add contact
* Phone number: Phone call, add contact
* Message: Send message, add contact
* Email: Send email
* Geolocation: Open map
6. Customize the generated QR code, e.g. error correction level, color, margin and screen brightness.
@ -63,11 +64,13 @@ By using the app, you can
### Languages Supported
* English (en)
* Traditional Chinese 正體中文 (zh)
* Simplified Chinese 简体中文 (zh-CN)
* Chinese (Hong Kong) 中文 (香港) (zh-HK)
* Chinese (Simplified) 简体中文 (zh-CN)
* German Deutsch (de)
* French Français (fr)
* Italian Italiano (it)
* Portuguese (Brazil) (pt-BR)
* Russian Русский (ru)
You are welcomed to help translate the app into more languages (refer to this <a href="#how-to-help-translate">section</a>)
@ -75,10 +78,8 @@ You are welcomed to help translate the app into more languages (refer to this <a
* Sponsor the project.
<div>
<a href="https://linktr.ee/tomfonghk"><img src="https://raw.githubusercontent.com/tomfong/simple-qr/main/.github/images/Paypal-badge.png" width="170" alt="tomfong"/></a>
<a href="https://www.buymeacoffee.com/tomfong"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" height="50" width="180" alt="tomfong" /></a>
</div>
[![GitHub Sponsor](https://img.shields.io/badge/sponsor-30363D?style=flat&logo=GitHub-Sponsors&logoColor=#white)](https://github.com/sponsors/tomfong?frequency=one-time)
[![Buy me a Coffee](https://img.shields.io/badge/Buy_Me_A_Coffee-FFDD00?style=flat&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/tomfong)
* Star the project.
@ -86,7 +87,7 @@ You are welcomed to help translate the app into more languages (refer to this <a
* Open issues to report bugs or share any new ideas.
[![Issues](https://img.shields.io/bitbucket/issues-raw/tomfong/simple-qr?style=flat)](https://github.com/tomfong/simple-qr/issues)
[![Issues](https://img.shields.io/github/issues/tomfong/simple-qr?style=flat)](https://github.com/tomfong/simple-qr/issues)
* Translate the app into different languages.
@ -103,17 +104,10 @@ You are welcomed to help translate the app into more languages (refer to this <a
* <b>DO NOT</b> change the order.
4. Email the JSON to me (tomfong.dev@gmail.com) after you finish.
### Build the project (Android)
### Build the project
1. Run ```npm install``` to install all dependencies
2. Run ```npm run sync``` and ```npm run copy:an```
3. In ```android/app/src/main/res/values/styles.xml```, change
```xml
<style name="AppTheme.NoActionBarLaunch" parent="AppTheme.NoActionBar">
<item name="android:background">#00a5aa</item>
</style>
```
1. Run ```npm install``` to install all dependencies.
2. Run ```npm run build```
### Contributors
@ -123,24 +117,23 @@ Thank you the following contributors who have made the app better!
|:-:|:-:|:-:|
| mondstern | [mondlicht-und-sterne](https://github.com/mondlicht-und-sterne) | German language translation |
| Valentino Bocchetti | [luftmensch-luftmensch](https://github.com/luftmensch-luftmensch) | Italian language translation |
| Smooth-E | [Smooth-E](https://github.com/Smooth-E) | Russian language translation |
| Daniel Ribeiro | [drcsj](https://github.com/drcsj) | Portuguese (Brazil) language translation |
## Framework
```sh
Ionic CLI : 6.20.1
Ionic Framework : @ionic/angular 6.2.7
@angular-devkit/build-angular : 13.3.9
@angular-devkit/schematics : 13.3.9
@angular/cli : 13.3.9
@ionic/angular-toolkit : 6.1.0
Ionic CLI : 7.2.0
Ionic Framework : @ionic/angular 7.8.2
@angular-devkit/build-angular : 16.2.13
@angular-devkit/schematics : 16.2.13
@angular/cli : 16.2.13
@ionic/angular-toolkit : 9.0.0
Capacitor CLI : 3.8.0
@capacitor/android : 3.8.0
@capacitor/core : 3.8.0
@capacitor/ios : 3.8.0
NodeJS : v16.15.1
npm : 8.11.0
Capacitor CLI : 5.7.4
@capacitor/android : 5.7.4
@capacitor/core : 5.7.4
@capacitor/ios : 5.7.4
```
## Privacy Policy

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
<bytecodeTargetLevel target="17" />
</component>
</project>

13
android/.idea/misc.xml generated
View file

@ -1,6 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="DesignSurface">
<option name="filePathToZoomLevelMap">
<map>
<entry key="app/src/main/res/drawable/ic_baseline_qr_code_24.xml" value="0.2485" />
<entry key="app/src/main/res/drawable/ic_launcher_background.xml" value="0.2485" />
<entry key="app/src/main/res/drawable/splash_background.xml" value="0.2485" />
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml" value="0.2485" />
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.2485" />
</map>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View file

@ -1,13 +1,14 @@
apply plugin: 'com.android.application'
android {
namespace "com.tomfong.simpleqr"
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "com.tomfong.simpleqr"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2080000
versionName "2.8.0"
versionCode 4010000
versionName "4.1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
@ -30,6 +31,8 @@ repositories {
}
dependencies {
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
implementation project(':capacitor-android')

View file

@ -2,8 +2,8 @@
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
@ -19,6 +19,7 @@ dependencies {
implementation project(':capacitor-filesystem')
implementation project(':capacitor-haptics')
implementation project(':capacitor-keyboard')
implementation project(':capacitor-preferences')
implementation project(':capacitor-splash-screen')
implementation project(':capacitor-status-bar')
implementation project(':capacitor-toast')

View file

@ -0,0 +1,20 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.tomfong.simpleqr",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 4010000,
"versionName": "4.1.0",
"outputFile": "app-release.apk"
}
],
"elementType": "File"
}

View file

@ -1,7 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.tomfong.simpleqr">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" >
<application android:hardwareAccelerated="true" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" android:requestLegacyExternalStorage="true" android:largeHeap="true">
<application
android:hardwareAccelerated="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:requestLegacyExternalStorage="true"
android:largeHeap="true">
<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode" android:name="com.tomfong.simpleqr.MainActivity" android:label="@string/title_activity_main" android:theme="@style/AppTheme.NoActionBarLaunch" android:launchMode="singleTask" android:exported="true">
@ -15,13 +24,29 @@
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"></meta-data>
</provider>
<service
android:name=".MyQSTileService"
android:label="@string/app_name"
android:icon="@drawable/ic_baseline_qr_code_24"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:exported="true">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
</application>
<!-- Permissions -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
</manifest>

View file

@ -5,7 +5,7 @@
},
{
"pkg": "@capacitor-community/contacts",
"classpath": "ch.byrds.capacitor.contacts.Contacts"
"classpath": "getcapacitor.community.contacts.ContactsPlugin"
},
{
"pkg": "@capacitor-community/screen-brightness",
@ -39,6 +39,10 @@
"pkg": "@capacitor/keyboard",
"classpath": "com.capacitorjs.plugins.keyboard.KeyboardPlugin"
},
{
"pkg": "@capacitor/preferences",
"classpath": "com.capacitorjs.plugins.preferences.PreferencesPlugin"
},
{
"pkg": "@capacitor/splash-screen",
"classpath": "com.capacitorjs.plugins.splashscreen.SplashScreenPlugin"

View file

@ -2,4 +2,20 @@ package com.tomfong.simpleqr;
import com.getcapacitor.BridgeActivity;
public class MainActivity extends BridgeActivity {}
public class MainActivity extends BridgeActivity {
static boolean active = false;
@Override
public void onStart() {
super.onStart();
active = true;
}
@Override
public void onStop() {
super.onStop();
active = false;
}
}

View file

@ -0,0 +1,63 @@
package com.tomfong.simpleqr;
import android.content.Intent;
import android.os.Build;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import android.util.Log;
import androidx.annotation.RequiresApi;
@RequiresApi(api = Build.VERSION_CODES.N)
public class MyQSTileService extends TileService {
public MyQSTileService() {}
// Called when the user adds your tile.
@Override
public void onTileAdded() {
super.onTileAdded();
Log.println(Log.INFO,"MyQSTileService","onTileAdded");
}
// Called when your app can update your tile.
@Override
public void onStartListening() {
super.onStartListening();
Log.println(Log.INFO,"MyQSTileService","onStartListening");
Tile tile = this.getQsTile();
if (MainActivity.active) {
tile.setState(Tile.STATE_ACTIVE);
} else {
tile.setState(Tile.STATE_INACTIVE);
}
tile.updateTile();
}
// Called when your app can no longer update your tile.
@Override
public void onStopListening() {
super.onStopListening();
Log.println(Log.INFO,"MyQSTileService","onStopListening");
}
// Called when the user taps on your tile in an active or inactive state.
@Override
public void onClick() {
super.onClick();
Log.println(Log.INFO,"MyQSTileService","onClick");
Tile tile = this.getQsTile();
Intent intent = new Intent(this.getApplicationContext(), MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityAndCollapse(intent);
tile.setState(Tile.STATE_ACTIVE);
tile.updateTile();
}
// Called when the user removes your tile.
@Override
public void onTileRemoved() {
super.onTileRemoved();
Log.println(Log.INFO,"MyQSTileService","onTileRemoved");
}
}

View file

@ -0,0 +1,15 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M3,11h8V3H3V11zM5,5h4v4H5V5z"/>
<path android:fillColor="@android:color/white" android:pathData="M3,21h8v-8H3V21zM5,15h4v4H5V15z"/>
<path android:fillColor="@android:color/white" android:pathData="M13,3v8h8V3H13zM19,9h-4V5h4V9z"/>
<path android:fillColor="@android:color/white" android:pathData="M19,19h2v2h-2z"/>
<path android:fillColor="@android:color/white" android:pathData="M13,13h2v2h-2z"/>
<path android:fillColor="@android:color/white" android:pathData="M15,15h2v2h-2z"/>
<path android:fillColor="@android:color/white" android:pathData="M13,17h2v2h-2z"/>
<path android:fillColor="@android:color/white" android:pathData="M15,19h2v2h-2z"/>
<path android:fillColor="@android:color/white" android:pathData="M17,17h2v2h-2z"/>
<path android:fillColor="@android:color/white" android:pathData="M17,13h2v2h-2z"/>
<path android:fillColor="@android:color/white" android:pathData="M19,15h2v2h-2z"/>
</vector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/colorPrimary"/>
<item android:gravity="center" android:drawable="@drawable/splash"/>
</layer-list>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<color tools:ignore="UnusedResources" name="colorPrimary">#00a5aa</color>
<color tools:ignore="UnusedResources" name="colorPrimaryDark">#00a5aa</color>
<color tools:ignore="UnusedResources" name="colorAccent">#00a5aa</color>
</resources>

View file

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="app_name">Simple QR</string>
<string name="title_activity_main">Simple QR</string>
<string name="package_name">com.tomfong.simpleqr</string>
<string name="custom_url_scheme">com.tomfong.simpleqr</string>
</resources>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:background">@null</item>
</style>
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
<item name="android:background">@drawable/splash_background</item>
<item name="android:windowBackground">@drawable/splash_background</item>
</style>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<color tools:ignore="UnusedResources" name="colorPrimary">#00a5aa</color>
<color tools:ignore="UnusedResources" name="colorPrimaryDark">#00a5aa</color>
<color tools:ignore="UnusedResources" name="colorAccent">#00a5aa</color>
</resources>

View file

@ -9,14 +9,14 @@
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.NoActionBar">
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:background">@null</item>
</style>
<style name="AppTheme.NoActionBarLaunch" parent="AppTheme.NoActionBar">
<item name="android:background">#00a5aa</item>
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
<item name="android:background">@drawable/splash_background</item>
<item name="android:windowBackground">@drawable/splash_background</item>
</style>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<color tools:ignore="UnusedResources" name="colorPrimary">#00a5aa</color>
<color tools:ignore="UnusedResources" name="colorPrimaryDark">#00a5aa</color>
<color tools:ignore="UnusedResources" name="colorAccent">#00a5aa</color>
</resources>

View file

@ -9,14 +9,14 @@
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.NoActionBar">
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:background">@null</item>
</style>
<style name="AppTheme.NoActionBarLaunch" parent="AppTheme.NoActionBar">
<item name="android:background">#00a5aa</item>
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
<item name="android:background">@drawable/splash_background</item>
<item name="android:windowBackground">@drawable/splash_background</item>
</style>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<color tools:ignore="UnusedResources" name="colorPrimary">#00a5aa</color>
<color tools:ignore="UnusedResources" name="colorPrimaryDark">#00a5aa</color>
<color tools:ignore="UnusedResources" name="colorAccent">#00a5aa</color>
</resources>

View file

@ -9,14 +9,14 @@
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.NoActionBar">
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:background">@null</item>
</style>
<style name="AppTheme.NoActionBarLaunch" parent="AppTheme.NoActionBar">
<item name="android:background">#00a5aa</item>
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
<item name="android:background">@drawable/splash_background</item>
<item name="android:windowBackground">@drawable/splash_background</item>
</style>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<color tools:ignore="UnusedResources" name="colorPrimary">#00a5aa</color>
<color tools:ignore="UnusedResources" name="colorPrimaryDark">#00a5aa</color>
<color tools:ignore="UnusedResources" name="colorAccent">#00a5aa</color>
</resources>

View file

@ -9,14 +9,14 @@
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.NoActionBar">
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:background">@null</item>
</style>
<style name="AppTheme.NoActionBarLaunch" parent="AppTheme.NoActionBar">
<item name="android:background">#00a5aa</item>
</style>
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
<item name="android:background">@drawable/splash_background</item>
<item name="android:windowBackground">@drawable/splash_background</item>
</style>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<color tools:ignore="UnusedResources" name="colorPrimary">#00a5aa</color>
<color tools:ignore="UnusedResources" name="colorPrimaryDark">#00a5aa</color>
<color tools:ignore="UnusedResources" name="colorAccent">#00a5aa</color>
</resources>

View file

@ -9,14 +9,14 @@
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.NoActionBar">
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:background">@null</item>
</style>
<style name="AppTheme.NoActionBarLaunch" parent="AppTheme.NoActionBar">
<item name="android:background">#00a5aa</item>
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
<item name="android:background">@drawable/splash_background</item>
<item name="android:windowBackground">@drawable/splash_background</item>
</style>
</resources>

View file

@ -1,14 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.1'
classpath 'com.google.gms:google-services:4.3.5'
classpath 'com.android.tools.build:gradle:8.0.2'
classpath 'com.google.gms:google-services:4.3.15'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@ -20,10 +20,11 @@ apply from: "variables.gradle"
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View file

@ -32,6 +32,9 @@ project(':capacitor-haptics').projectDir = new File('../node_modules/@capacitor/
include ':capacitor-keyboard'
project(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor/keyboard/android')
include ':capacitor-preferences'
project(':capacitor-preferences').projectDir = new File('../node_modules/@capacitor/preferences/android')
include ':capacitor-splash-screen'
project(':capacitor-splash-screen').projectDir = new File('../node_modules/@capacitor/splash-screen/android')

View file

@ -20,5 +20,4 @@ org.gradle.jvmargs=-Xmx1536m
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true

View file

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View file

@ -1,14 +1,16 @@
ext {
minSdkVersion = 23
compileSdkVersion = 31
targetSdkVersion = 31
androidxActivityVersion = '1.2.0'
androidxAppCompatVersion = '1.2.0'
androidxCoordinatorLayoutVersion = '1.1.0'
androidxCoreVersion = '1.3.2'
androidxFragmentVersion = '1.3.0'
junitVersion = '4.13.1'
androidxJunitVersion = '1.1.2'
androidxEspressoCoreVersion = '3.3.0'
cordovaAndroidVersion = '7.0.0'
minSdkVersion = 22
compileSdkVersion = 33
targetSdkVersion = 33
androidxActivityVersion = '1.7.0'
androidxAppCompatVersion = '1.6.1'
androidxCoordinatorLayoutVersion = '1.2.0'
androidxCoreVersion = '1.10.0'
androidxFragmentVersion = '1.5.6'
junitVersion = '4.13.2'
androidxJunitVersion = '1.1.5'
androidxEspressoCoreVersion = '3.5.1'
cordovaAndroidVersion = '10.1.1'
coreSplashScreenVersion = '1.0.0'
androidxWebkitVersion = '1.6.1'
}

View file

@ -1,7 +1,6 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"defaultProject": "app",
"newProjectRoot": "projects",
"projects": {
"app": {
@ -31,7 +30,10 @@
"output": "./svg"
}
],
"styles": ["src/theme/variables.scss", "src/global.scss"],
"styles": [
"src/theme/variables.scss",
"src/global.scss"
],
"scripts": [],
"aot": false,
"vendorChunk": true,
@ -80,8 +82,7 @@
"production": {
"browserTarget": "app:build:production"
},
"ci": {
}
"ci": {}
}
},
"extract-i18n": {
@ -133,36 +134,15 @@
"devServerTarget": "app:serve:ci"
}
}
},
"ionic-cordova-build": {
"builder": "@ionic/angular-toolkit:cordova-build",
"options": {
"browserTarget": "app:build"
},
"configurations": {
"production": {
"browserTarget": "app:build:production"
}
}
},
"ionic-cordova-serve": {
"builder": "@ionic/angular-toolkit:cordova-serve",
"options": {
"cordovaBuildTarget": "app:ionic-cordova-build",
"devServerTarget": "app:serve"
},
"configurations": {
"production": {
"cordovaBuildTarget": "app:ionic-cordova-build:production",
"devServerTarget": "app:serve:production"
}
}
}
}
}
},
"cli": {
"defaultCollection": "@ionic/angular-toolkit"
"schematicCollections": [
"@ionic/angular-toolkit"
],
"analytics": false
},
"schematics": {
"@ionic/angular-toolkit:component": {
@ -172,4 +152,4 @@
"styleext": "scss"
}
}
}
}

View file

@ -6,7 +6,7 @@ Mit der App ist das möglich
* QR-Code und andere Barcodes in Sekundenschnelle zu scannen, einschließlich UPC, EAN, Code 39/128, ITF, Codabar, Aztec, Data Matrix, PDF417, MaxiCode und GS1 DataBar.
* Bilddateien zu importieren und den darauf befindlichen QR-Code zu scannen.
* QR-Codes aus Vorlagen,die Freitext, URL, vCard-Kontakt, Telefonnummer, Nachricht, E-Mail und WLAN enthalten, zu erstellen.
* QR-Codes aus Vorlagen,die Freitext, URL, vCard-Kontakt, Telefonnummer, Nachricht, Geolokalisierung, E-Mail und WLAN enthalten, zu erstellen.
* Gescannte Ergebnissen, die gescannt wurden, erstellt oder erneut angezeigt wurden zu protokollieren. Diese protokollierten Aufzeichnungen können für schnellen Zugriff mit einem Lesezeichen versehen und auch gesichert werden.
* Aufgaben auf dem Ergebnis mit einem Fingertipp zu erledigen, einschließlich, aber nicht beschränkt auf
* Verwenden als Schlüsselwort für die Websuche.
@ -19,5 +19,6 @@ Mit der App ist das möglich
* Telefonnummer: Telefonanruf, Kontakt hinzufügen
* Nachricht: Nachricht senden, Kontakt hinzufügen
* E-Mail: E-Mail senden
* Geolokalisierung: Karte öffnen
* Passen Sie den generierten QR-Code an, z. Fehlerkorrekturstufe, Farbe, Rand und Bildschirmhelligkeit.
* Die App anzupassen, z.B. App-Startseite, Sprache und Farbthema etc.

View file

@ -0,0 +1,4 @@
* Upgraded framework, improved performance and fixed known bugs.
* Support quick access tile in the Quick Settings panel.
* More customizable UI.
* Removed Read/Write Contacts permission.

View file

@ -0,0 +1 @@
* Bug fix

View file

@ -0,0 +1,3 @@
* Support Ecosia
* Support CSV export
* Bug fix

View file

@ -0,0 +1,2 @@
* Support Open Food Facts
* Bug fix

View file

@ -0,0 +1,3 @@
* Support Russian language
* Allow user to manually save a record if auto logging is off
* Minor update on UI

View file

@ -0,0 +1,4 @@
* Upgrade framework, improve performance and fix known bugs
* Support Brave Search
* Support open URL automatically
* Allow user to further edit QR code content

View file

@ -0,0 +1 @@
* Fix import image issue

View file

@ -0,0 +1,3 @@
* Support geolocation QR code
* Support Portuguese (Brazil) language
* Disable auto URL opening by default

View file

@ -1,12 +1,12 @@
<i>Simple QR</i> is a FOSS app to scan, create and store QR codes with a simple UI and experience. No backend service connected. No data collected. No ads.
<i>Simple QR</i> is an open-source app to scan, create and store QR codes with a simple UI and experience. No backend service connected. No data collected. No ads.
English, French, German, Itanlian, Traditional Chinese and Simplified Chinese are supported.
English, French, German, Itanlian, Russian, Traditional Chinese and Simplified Chinese are supported.
By using the app, you can:
* Scan QR Code and other barcodes in a second, including UPC, EAN, Code 39/128, ITF, Codabar, Aztec, Data Matrix, PDF417, MaxiCode and GS1 DataBar.
* Import image files and scan the QR Code on it.
* Create QR code from templates, which includes Free Text, URL, vCard Contact, Phone Number, Message, Email and Wi-Fi.
* Create QR code from templates, which includes Free Text, URL, vCard Contact, Phone Number, Message, Email, Wi-Fi and Geolocation.
* Automatically log results that you scan, create or view again. These logged records can be bookmarked for quick access, and also backupable.
* Do tasks on the result with a tap, including but not limited to
* Use it as a keyword to do web search.
@ -14,10 +14,11 @@ By using the app, you can:
* Execute base64 encoding/decoding on it.
* Generate a new shareable QR code by using it as the content.
* Do corresponding tasks if it is a
* URL: Browse website
* URL: Browse website / Open application
* vCard contact: Add contact
* Phone number: Phone call, add contact
* Message: Send message, add contact
* Email: Send email
* Geolocation: Open map
* Customize the generated QR code, e.g. error correction level, color, margin and screen brightness.
* Customize the app, e.g. app initial page, language and color theme etc.

2
ios/.gitignore vendored
View file

@ -1,9 +1,9 @@
App/build
App/Pods
App/Podfile.lock
App/App/public
DerivedData
xcuserdata
# Cordova plugins for Capacitor
capacitor-cordova-ios-plugins

View file

@ -19,6 +19,7 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
1B75189429375EAB00800D38 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = "<group>"; };
2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = "<group>"; };
50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = "<group>"; };
504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -160,6 +161,7 @@
de,
fr,
it,
ru,
);
mainGroup = 504EC2FB1FED79650016851F;
productRefGroup = 504EC3051FED79650016851F /* Products */;
@ -268,6 +270,7 @@
D26FE7F9283D1E6C002A61AE /* fr */,
D26FE7FA283D1E76002A61AE /* de */,
D29D2E2A2847C31D00566DFF /* it */,
1B75189429375EAB00800D38 /* ru */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
@ -324,7 +327,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -376,7 +379,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
@ -394,9 +397,9 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = G23992CVBU;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 2.8.0;
MARKETING_VERSION = 4.1.0;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = com.tomfong.simpleqr;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -417,9 +420,9 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = G23992CVBU;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 2.8.0;
MARKETING_VERSION = 4.1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.tomfong.simpleqr;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "Simple QR";

View file

@ -46,15 +46,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let statusBarRect = UIApplication.shared.statusBarFrame
guard let touchPoint = event?.allTouches?.first?.location(in: self.window) else { return }
if statusBarRect.contains(touchPoint) {
NotificationCenter.default.post(name: .capacitorStatusBarTapped, object: nil)
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -1,116 +1,14 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "AppIcon-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "AppIcon-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "AppIcon-29x29@2x-1.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "AppIcon-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "AppIcon-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "AppIcon-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "AppIcon-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "AppIcon-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "AppIcon-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "AppIcon-20x20@2x-1.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "AppIcon-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "AppIcon-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "AppIcon-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "AppIcon-40x40@2x-1.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "AppIcon-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "AppIcon-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "AppIcon-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "AppIcon-512@2x.png",
"scale" : "1x"
"images" : [
{
"filename" : "AppIcon-512@2x.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View file

@ -22,11 +22,6 @@
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSCameraUsageDescription</key>
<string>Simple QR uses Camera to scan barcodes</string>
<key>NSContactsUsageDescription</key>

View file

@ -0,0 +1,6 @@
"CFBundleDisplayName" = "Simple QR";
"CFBundleName" = "Simple QR";
"NSCameraUsageDescription" = "Simple QR использует камеру для сканирования QR-кода и штрих-кодов";
"NSContactsUsageDescription" = "Simple QR использует Контакты для добавления контакта";
"NSPhotoLibraryAddUsageDescription" = "Simple QR использует библиотеку фотографий для сохранения изображения QR-кода";
"NSPhotoLibraryUsageDescription" = "Simple QR использует библиотеку фотографий для импорта изображений и сканирования QR-кода.";

View file

@ -1,4 +1,6 @@
platform :ios, '12.0'
require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'
platform :ios, '13.0'
use_frameworks!
# workaround to avoid Xcode caching of Pods that requires
@ -19,6 +21,7 @@ def capacitor_pods
pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem'
pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
pod 'CapacitorPreferences', :path => '../../node_modules/@capacitor/preferences'
pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
pod 'CapacitorToast', :path => '../../node_modules/@capacitor/toast'
@ -29,3 +32,8 @@ target 'App' do
capacitor_pods
# Add your Pods here
end
post_install do |installer|
assertDeploymentTarget(installer)
end

112
ios/App/Podfile.lock Normal file
View file

@ -0,0 +1,112 @@
PODS:
- Capacitor (5.7.4):
- CapacitorCordova
- CapacitorApp (5.0.7):
- Capacitor
- CapacitorCamera (5.0.9):
- Capacitor
- CapacitorClipboard (5.0.7):
- Capacitor
- CapacitorCommunityBarcodeScanner (4.0.1):
- Capacitor
- CapacitorCommunityContacts (5.0.5):
- Capacitor
- CapacitorCommunityScreenBrightness (5.0.0):
- Capacitor
- CapacitorCordova (5.7.4)
- CapacitorDevice (5.0.7):
- Capacitor
- CapacitorFilesystem (5.2.1):
- Capacitor
- CapacitorHaptics (5.0.7):
- Capacitor
- CapacitorKeyboard (5.0.8):
- Capacitor
- CapacitorPreferences (5.0.7):
- Capacitor
- CapacitorSplashScreen (5.0.7):
- Capacitor
- CapacitorStatusBar (5.0.7):
- Capacitor
- CapacitorToast (5.0.7):
- Capacitor
- CordovaPlugins (5.7.4):
- CapacitorCordova
DEPENDENCIES:
- "Capacitor (from `../../node_modules/@capacitor/ios`)"
- "CapacitorApp (from `../../node_modules/@capacitor/app`)"
- "CapacitorCamera (from `../../node_modules/@capacitor/camera`)"
- "CapacitorClipboard (from `../../node_modules/@capacitor/clipboard`)"
- "CapacitorCommunityBarcodeScanner (from `../../node_modules/@capacitor-community/barcode-scanner`)"
- "CapacitorCommunityContacts (from `../../node_modules/@capacitor-community/contacts`)"
- "CapacitorCommunityScreenBrightness (from `../../node_modules/@capacitor-community/screen-brightness`)"
- "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
- "CapacitorDevice (from `../../node_modules/@capacitor/device`)"
- "CapacitorFilesystem (from `../../node_modules/@capacitor/filesystem`)"
- "CapacitorHaptics (from `../../node_modules/@capacitor/haptics`)"
- "CapacitorKeyboard (from `../../node_modules/@capacitor/keyboard`)"
- "CapacitorPreferences (from `../../node_modules/@capacitor/preferences`)"
- "CapacitorSplashScreen (from `../../node_modules/@capacitor/splash-screen`)"
- "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)"
- "CapacitorToast (from `../../node_modules/@capacitor/toast`)"
- CordovaPlugins (from `../capacitor-cordova-ios-plugins`)
EXTERNAL SOURCES:
Capacitor:
:path: "../../node_modules/@capacitor/ios"
CapacitorApp:
:path: "../../node_modules/@capacitor/app"
CapacitorCamera:
:path: "../../node_modules/@capacitor/camera"
CapacitorClipboard:
:path: "../../node_modules/@capacitor/clipboard"
CapacitorCommunityBarcodeScanner:
:path: "../../node_modules/@capacitor-community/barcode-scanner"
CapacitorCommunityContacts:
:path: "../../node_modules/@capacitor-community/contacts"
CapacitorCommunityScreenBrightness:
:path: "../../node_modules/@capacitor-community/screen-brightness"
CapacitorCordova:
:path: "../../node_modules/@capacitor/ios"
CapacitorDevice:
:path: "../../node_modules/@capacitor/device"
CapacitorFilesystem:
:path: "../../node_modules/@capacitor/filesystem"
CapacitorHaptics:
:path: "../../node_modules/@capacitor/haptics"
CapacitorKeyboard:
:path: "../../node_modules/@capacitor/keyboard"
CapacitorPreferences:
:path: "../../node_modules/@capacitor/preferences"
CapacitorSplashScreen:
:path: "../../node_modules/@capacitor/splash-screen"
CapacitorStatusBar:
:path: "../../node_modules/@capacitor/status-bar"
CapacitorToast:
:path: "../../node_modules/@capacitor/toast"
CordovaPlugins:
:path: "../capacitor-cordova-ios-plugins"
SPEC CHECKSUMS:
Capacitor: 4fe9adf012caceb4c71ffea2f1f4d005cdcbeea7
CapacitorApp: 17fecd0e6cb23feafac7eb0939417389038b0979
CapacitorCamera: 4892c5c392f60039d853dde78bc50ba19fbd113e
CapacitorClipboard: 45e5e25f2271f98712985d422776cdc5a779cca1
CapacitorCommunityBarcodeScanner: 7feb206489c8555a8ca0c74c57ddf49ead774eb8
CapacitorCommunityContacts: e8fbc4d669c9478a29f1e104818b4c16e158b2e0
CapacitorCommunityScreenBrightness: b2d9c6fffee6b684994cd69f727f2090e2f05c6d
CapacitorCordova: a6e87fccc0307dee7aec1560ec9398485f2b0ce7
CapacitorDevice: fc91bdb484dc0e70755e9b621cd557afe642613a
CapacitorFilesystem: 9f3e3c7fea2fff12f46dd5b07a2914f2103e4cfc
CapacitorHaptics: 7c7c206f0c96a628fed073830c96d28c4b2e772e
CapacitorKeyboard: aec619a578235c6ce279075009a2689c2cf5c42c
CapacitorPreferences: 77ac427e98db83bace772455f8ba447430382c4c
CapacitorSplashScreen: dd3de3f3644710fa2a697cfb91ec262eece4d242
CapacitorStatusBar: f390fbb49b82ffb754ea4b3cf71dc8b048baf3e7
CapacitorToast: c8bb89eeb59a23c1fc298f138cc06c8ff4d90ac1
CordovaPlugins: 5495649167d6829fea7bc7eacd2034646aee5bd1
PODFILE CHECKSUM: dc80e3587547d0d302dad43090af30e2a96d6c5a
COCOAPODS: 1.11.3

24909
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,12 @@
{
"name": "simple-qr",
"version": "2.8.0",
"version": "4.1.0",
"author": "Tom Fong",
"homepage": "https://tomfong.github.io",
"scripts": {
"ng": "ng",
"build:an": "ionic cap build android --prod",
"build:ios": "ionic cap build ios --prod",
"build:an": "ionic cap build android --prod --no-open",
"build:ios": "ionic cap build ios --prod --no-open",
"build": "ionic cap build android --prod --no-open && ionic cap build ios --prod --no-open",
"sync": "ionic cap sync --prod --no-build",
"copy:an": "ionic cap copy android --prod",
"copy:ios": "ionic cap copy ios --prod",
@ -16,91 +16,90 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^13.3.5",
"@angular/cdk": "^13.3.5",
"@angular/common": "^13.3.5",
"@angular/core": "^13.3.5",
"@angular/forms": "^13.3.11",
"@angular/localize": "^13.3.11",
"@angular/material": "^13.3.5",
"@angular/material-moment-adapter": "^13.3.9",
"@angular/platform-browser": "^13.3.5",
"@angular/platform-browser-dynamic": "^13.3.11",
"@angular/router": "^13.3.11",
"@awesome-cordova-plugins/aes-256": "^5.45.0",
"@awesome-cordova-plugins/chooser": "^5.45.0",
"@awesome-cordova-plugins/core": "^5.45.0",
"@awesome-cordova-plugins/screen-orientation": "^5.45.0",
"@awesome-cordova-plugins/sms": "^5.45.0",
"@awesome-cordova-plugins/social-sharing": "^5.45.0",
"@awesome-cordova-plugins/theme-detection": "^5.45.0",
"@capacitor-community/barcode-scanner": "^2.1.1",
"@capacitor-community/contacts": "^1.1.3",
"@capacitor-community/screen-brightness": "^1.0.1",
"@capacitor/android": "^3.8.0",
"@capacitor/app": "^1.1.1",
"@capacitor/camera": "^1.3.1",
"@capacitor/clipboard": "^1.0.8",
"@capacitor/core": "^3.8.0",
"@capacitor/device": "^1.1.2",
"@capacitor/filesystem": "^1.1.0",
"@capacitor/haptics": "^1.1.4",
"@capacitor/ios": "^3.8.0",
"@capacitor/keyboard": "^1.2.3",
"@capacitor/splash-screen": "^1.2.2",
"@capacitor/status-bar": "^1.0.8",
"@capacitor/toast": "^1.0.8",
"@ionic/angular": "^6.2.7",
"@ionic/storage": "^3.0.6",
"@ionic/storage-angular": "^3.0.6",
"@ng-bootstrap/ng-bootstrap": "^11.0.1",
"@ngx-translate/core": "^13.0.0",
"@ngx-translate/http-loader": "^6.0.0",
"@techiediaries/ngx-qrcode": "^9.1.0",
"bootstrap": "^4.6.2",
"@angular/animations": "^16.1.3",
"@angular/cdk": "^16.1.3",
"@angular/common": "^16.1.3",
"@angular/core": "^16.1.3",
"@angular/forms": "^16.2.12",
"@angular/localize": "^16.2.12",
"@angular/material": "^16.1.3",
"@angular/material-moment-adapter": "^16.2.14",
"@angular/platform-browser": "^16.1.3",
"@angular/platform-browser-dynamic": "^16.2.12",
"@angular/router": "^16.2.12",
"@awesome-cordova-plugins/aes-256": "^6.6.0",
"@awesome-cordova-plugins/chooser": "^6.6.0",
"@awesome-cordova-plugins/core": "^6.6.0",
"@awesome-cordova-plugins/screen-orientation": "^6.6.0",
"@awesome-cordova-plugins/sms": "^6.6.0",
"@awesome-cordova-plugins/social-sharing": "^6.6.0",
"@awesome-cordova-plugins/theme-detection": "^6.6.0",
"@capacitor-community/barcode-scanner": "^4.0.1",
"@capacitor-community/contacts": "^5.0.5",
"@capacitor-community/screen-brightness": "^5.0.0",
"@capacitor/android": "^5.7.4",
"@capacitor/app": "^5.0.7",
"@capacitor/camera": "^5.0.9",
"@capacitor/clipboard": "^5.0.7",
"@capacitor/core": "^5.7.4",
"@capacitor/device": "^5.0.7",
"@capacitor/filesystem": "^5.2.1",
"@capacitor/haptics": "^5.0.7",
"@capacitor/ios": "^5.7.4",
"@capacitor/keyboard": "^5.0.8",
"@capacitor/preferences": "^5.0.7",
"@capacitor/splash-screen": "^5.0.7",
"@capacitor/status-bar": "^5.0.7",
"@capacitor/toast": "^5.0.7",
"@ionic/angular": "^7.8.2",
"@ionic/storage": "^4.0.0",
"@ionic/storage-angular": "^4.0.0",
"@ng-bootstrap/ng-bootstrap": "^15.1.2",
"@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0",
"angularx-qrcode": "^16.0.2",
"bootstrap": "^5.3.3",
"cordova-plugin-aes256-encryption": "^2.0.1",
"cordova-plugin-chooser": "^1.3.2",
"cordova-plugin-screen-orientation": "^3.0.2",
"cordova-plugin-screen-orientation": "^3.0.4",
"cordova-plugin-theme-detection": "^1.3.0",
"cordova-plugin-x-socialsharing": "^6.0.4",
"cordova-sms-plugin": "^1.0.2",
"cordova-sms-plugin": "^1.0.3",
"date-fns": "2.29.3",
"es6-promise-plugin": "^4.2.2",
"human-signals": "^2.1.0",
"jsqr": "^1.4.0",
"material-design-icons": "^3.0.1",
"moment": "^2.29.4",
"osenv": "^0.1.5",
"properties-parser": "^0.3.1",
"rxjs": "^6.6.7",
"rxjs": "^7.8.1",
"strip-final-newline": "^2.0.0",
"tslib": "^2.4.0",
"tslib": "^2.6.2",
"uuid": "^8.3.2",
"zone.js": "^0.11.8"
"zone.js": "^0.13.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "^13.3.9",
"@angular/cli": "^13.3.9",
"@angular/compiler": "^13.3.5",
"@angular/compiler-cli": "^13.3.5",
"@angular/language-service": "^13.3.11",
"@capacitor/cli": "^3.8.0",
"@ionic/angular-toolkit": "^6.1.0",
"@types/jasmine": "^3.10.6",
"@types/jasminewd2": "^2.0.10",
"@angular-devkit/build-angular": "^16.2.13",
"@angular/cli": "^16.2.13",
"@angular/compiler": "^16.1.3",
"@angular/compiler-cli": "^16.1.3",
"@angular/language-service": "^16.2.12",
"@capacitor/cli": "^5.7.4",
"@ionic/angular-toolkit": "^9.0.0",
"@ionic/cli": "^7.2.0",
"@types/jasmine": "^3.10.18",
"@types/jasminewd2": "^2.0.13",
"@types/node": "^12.20.55",
"@types/uuid": "^8.3.4",
"codelyzer": "^6.0.2",
"jasmine-core": "~3.8.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "^6.4.0",
"karma-chrome-launcher": "^3.1.1",
"karma": "^6.4.3",
"karma-chrome-launcher": "^3.2.0",
"karma-coverage": "~2.0.3",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "^4.0.2",
"karma-jasmine-html-reporter": "^1.7.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.5.5"
"typescript": "^5.1.6"
}
}

View file

@ -48,6 +48,10 @@ const routes: Routes = [
path: 'setting-auto-brightness',
loadChildren: () => import('./pages/setting-auto-brightness/setting-auto-brightness.module').then(m => m.SettingAutoBrightnessPageModule)
},
{
path: 'setting-auto-open-url',
loadChildren: () => import('./pages/setting-auto-open-url/setting-auto-open-url.module').then(m => m.SettingAutoOpenUrlPageModule)
},
{
path: 'setting-start-page',
loadChildren: () => import('./pages/setting-start-page/setting-start-page.module').then(m => m.SettingStartPagePageModule)

View file

@ -1,4 +1,7 @@
import { Component } from '@angular/core';
import { App } from '@capacitor/app';
import { SplashScreen } from '@capacitor/splash-screen';
import { Toast } from '@capacitor/toast';
import { Platform } from '@ionic/angular';
import { TranslateService } from '@ngx-translate/core';
import { EnvService } from './services/env.service';
@ -17,5 +20,29 @@ export class AppComponent {
) {
translate.addLangs(this.env.languages);
translate.setDefaultLang('en');
if (this.platform.is('ios')) {
App.addListener('appStateChange', async ({ isActive }) => {
if (env.isDebugging) {
this.presentToast(`App state changed. Is active?: ${isActive}`, "short", "bottom");
}
if (isActive) {
setTimeout(async () => {
await SplashScreen.hide();
}, 300);
} else {
await SplashScreen.show({
autoHide: false
});
}
});
}
}
async presentToast(msg: string, duration: "short" | "long", pos: "top" | "center" | "bottom") {
await Toast.show({
text: msg,
duration: duration,
position: pos
});
}
}

View file

@ -19,14 +19,12 @@ import { ScreenOrientation } from '@awesome-cordova-plugins/screen-orientation/n
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatFormFieldModule } from '@angular/material/form-field';
import { HistoryTutorialPageModule } from './modals/history-tutorial/history-tutorial.module';
import { DatePipe } from '@angular/common';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatButtonModule } from '@angular/material/button';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { EnvService } from './services/env.service';
import { FormsModule } from '@angular/forms';
import { BookmarkTutorialPageModule } from './modals/bookmark-tutorial/bookmark-tutorial.module';
import { QrCodePageModule } from './modals/qr-code/qr-code.module';
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
@ -39,7 +37,7 @@ export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
],
imports: [
BrowserModule,
IonicModule.forRoot(),
IonicModule.forRoot({ innerHTMLTemplatesEnabled: true }),
AppRoutingModule,
HttpClientModule,
FormsModule,
@ -51,8 +49,6 @@ export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
}
}),
IonicStorageModule.forRoot(),
HistoryTutorialPageModule,
BookmarkTutorialPageModule,
QrCodePageModule,
BrowserAnimationsModule,
MatFormFieldModule,

View file

@ -1,40 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { BookmarkTutorialPage } from './bookmark-tutorial.page';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { HttpClient } from '@angular/common/http';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatSelectModule,
TranslateModule.forChild({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
],
declarations: [BookmarkTutorialPage]
})
export class BookmarkTutorialPageModule {}

View file

@ -1,53 +0,0 @@
<ion-content [color]="color" #content>
<ion-list-header class="mt-4 ml-3" style="font-size: x-large;">{{ 'TUTORIAL' | translate }}</ion-list-header>
<ion-item class="content-item ion-no-padding" lines="none" [color]="color">
<ion-icon class="ion-padding-horizontal" src="assets/icon/swipe-left.svg"
[color]="'primary'"></ion-icon>
<ion-label>
<p class="ion-padding">
<ion-text [color]="env.colorTheme === 'light'? 'dark' : 'light'" style="white-space: normal;">
{{ 'MSG.TUTORIAL_SWIPE_LEFT' | translate }}
</ion-text>
</p>
</ion-label>
</ion-item>
<ion-item class="content-item ion-no-padding" lines="none" [color]="color">
<ion-icon class="ion-padding-horizontal" src="assets/icon/swipe-right.svg"
[color]="'primary'"></ion-icon>
<ion-label>
<p class="ion-padding">
<ion-text [color]="env.colorTheme === 'light'? 'dark' : 'light'" style="white-space: normal;">
{{ 'MSG.BOOKMARK_TUTORIAL_SWIPE_RIGHT' | translate }}
</ion-text>
</p>
</ion-label>
</ion-item>
</ion-content>
<ion-footer>
<ion-item lines="none" [color]="color" style="--ripple-color: transparent;">
<ion-checkbox class="ion-margin-horizontal" [(ngModel)]="env.notShowBookmarkTutorial"
[color]="'primary'" (ionChange)="saveBookmarkTutorialShowing()"></ion-checkbox>
<ion-label>
<p class="ion-padding">
<ion-text [color]="env.colorTheme === 'light'? 'dark' : 'light'" style="white-space: normal;">
{{ 'MSG.TUTORIAL_NOT_SHOW_AGAIN' | translate }}
</ion-text>
</p>
</ion-label>
</ion-item>
<ion-toolbar [color]="color" style="border: none;">
<ion-row class="d-flex align-items-center justify-content-end">
<ion-button class="ion-margin-horizontal ion-margin-bottom" fill="clear" (click)="tapHaptic(); closeModal()" color="primary">
{{ 'OK' | translate }}
</ion-button>
</ion-row>
</ion-toolbar>
</ion-footer>

View file

@ -1,18 +0,0 @@
mat-form-field {
width: 100%;
}
.content-item {
padding-left: 16px;
padding-right: 16px;
}
ion-footer {
&.footer-md::before {
background-image: none;
}
}
.footer-ios ion-toolbar:first-of-type {
--border-width: 0 !important;
}

View file

@ -1,65 +0,0 @@
import { Component, ViewChild } from '@angular/core';
import { Haptics, ImpactStyle } from '@capacitor/haptics';
import { Toast } from '@capacitor/toast';
import { ModalController } from '@ionic/angular';
import { TranslateService } from '@ngx-translate/core';
import { EnvService } from 'src/app/services/env.service';
@Component({
selector: 'app-bookmark-tutorial',
templateUrl: './bookmark-tutorial.page.html',
styleUrls: ['./bookmark-tutorial.page.scss'],
})
export class BookmarkTutorialPage {
@ViewChild('content') contentEl: HTMLIonContentElement;
constructor(
public modalController: ModalController,
public translate: TranslateService,
public env: EnvService,
) {
setTimeout(
() => {
this.contentEl.scrollToBottom(500);
}, 750
);
}
async saveBookmarkTutorialShowing() {
if (this.env.notShowBookmarkTutorial === true) {
await this.env.storageSet("not-show-bookmark-tutorial", 'yes');
} else {
await this.env.storageSet("not-show-bookmark-tutorial", 'no');
}
}
async tapHaptic() {
if (this.env.vibration === 'on' || this.env.vibration === 'on-haptic') {
await Haptics.impact({ style: ImpactStyle.Medium })
.catch(async err => {
if (this.env.debugMode === 'on') {
await Toast.show({ text: 'Err when Haptics.impact: ' + JSON.stringify(err), position: "top", duration: "long" })
}
})
}
}
closeModal(): void {
this.modalController.dismiss();
}
get color() {
switch (this.env.colorTheme) {
case 'dark':
return 'dark';
case 'light':
return 'white';
case 'black':
return 'black';
default:
return 'white';
}
}
}

View file

@ -1,39 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { HistoryTutorialPage } from './history-tutorial.page';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { HttpClient } from '@angular/common/http';
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatSelectModule,
TranslateModule.forChild({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
],
declarations: [HistoryTutorialPage]
})
export class HistoryTutorialPageModule {}

View file

@ -1,53 +0,0 @@
<ion-content [color]="color" #content>
<ion-list-header class="mt-4 ml-3" style="font-size: x-large;">{{ 'TUTORIAL' | translate }}</ion-list-header>
<ion-item class="content-item ion-no-padding" lines="none" [color]="color">
<ion-icon class="ion-padding-horizontal" src="assets/icon/swipe-left.svg"
[color]="'primary'"></ion-icon>
<ion-label>
<p class="ion-padding">
<ion-text [color]="env.colorTheme === 'light'? 'dark' : 'light'" style="white-space: normal;">
{{ 'MSG.TUTORIAL_SWIPE_LEFT' | translate }}
</ion-text>
</p>
</ion-label>
</ion-item>
<ion-item class="content-item ion-no-padding" lines="none" [color]="color">
<ion-icon class="ion-padding-horizontal" src="assets/icon/swipe-right.svg"
[color]="'primary'"></ion-icon>
<ion-label>
<p class="ion-padding">
<ion-text [color]="env.colorTheme === 'light'? 'dark' : 'light'" style="white-space: normal;">
{{ 'MSG.TUTORIAL_SWIPE_RIGHT' | translate }}
</ion-text>
</p>
</ion-label>
</ion-item>
</ion-content>
<ion-footer>
<ion-item lines="none" [color]="color" style="--ripple-color: transparent;">
<ion-checkbox class="ion-margin-horizontal" [(ngModel)]="env.notShowHistoryTutorial"
[color]="'primary'" (ionChange)="saveHistoryTutorialShowing()"></ion-checkbox>
<ion-label>
<p class="ion-padding">
<ion-text [color]="env.colorTheme === 'light'? 'dark' : 'light'" style="white-space: normal;">
{{ 'MSG.TUTORIAL_NOT_SHOW_AGAIN' | translate }}
</ion-text>
</p>
</ion-label>
</ion-item>
<ion-toolbar [color]="color" style="border: none;">
<ion-row class="d-flex align-items-center justify-content-end">
<ion-button class="ion-margin-horizontal ion-margin-bottom" fill="clear" (click)="tapHaptic(); closeModal()" color="primary">
{{ 'OK' | translate }}
</ion-button>
</ion-row>
</ion-toolbar>
</ion-footer>

View file

@ -1,18 +0,0 @@
mat-form-field {
width: 100%;
}
.content-item {
padding-left: 16px;
padding-right: 16px;
}
ion-footer {
&.footer-md::before {
background-image: none;
}
}
.footer-ios ion-toolbar:first-of-type {
--border-width: 0 !important;
}

View file

@ -1,65 +0,0 @@
import { Component, ViewChild } from '@angular/core';
import { Haptics, ImpactStyle } from '@capacitor/haptics';
import { Toast } from '@capacitor/toast';
import { ModalController, ToastController } from '@ionic/angular';
import { TranslateService } from '@ngx-translate/core';
import { EnvService } from 'src/app/services/env.service';
@Component({
selector: 'app-history-tutorial',
templateUrl: './history-tutorial.page.html',
styleUrls: ['./history-tutorial.page.scss'],
})
export class HistoryTutorialPage {
@ViewChild('content') contentEl: HTMLIonContentElement;
constructor(
public modalController: ModalController,
public translate: TranslateService,
public env: EnvService,
) {
setTimeout(
() => {
this.contentEl.scrollToBottom(500);
}, 750
);
}
async saveHistoryTutorialShowing() {
if (this.env.notShowHistoryTutorial === true) {
await this.env.storageSet("not-show-history-tutorial", 'yes');
} else {
await this.env.storageSet("not-show-history-tutorial", 'no');
}
}
async tapHaptic() {
if (this.env.vibration === 'on' || this.env.vibration === 'on-haptic') {
await Haptics.impact({ style: ImpactStyle.Medium })
.catch(async err => {
if (this.env.debugMode === 'on') {
await Toast.show({ text: 'Err when Haptics.impact: ' + JSON.stringify(err), position: "top", duration: "long" })
}
})
}
}
closeModal(): void {
this.modalController.dismiss();
}
get color() {
switch (this.env.colorTheme) {
case 'dark':
return 'dark';
case 'light':
return 'white';
case 'black':
return 'black';
default:
return 'white';
}
}
}

View file

@ -8,11 +8,7 @@ import { QrCodePage } from './qr-code.page';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { HttpClient } from '@angular/common/http';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { NgxQRCodeModule } from '@techiediaries/ngx-qrcode';
import { QRCodeModule } from 'angularx-qrcode';
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
@ -23,7 +19,7 @@ export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
CommonModule,
FormsModule,
IonicModule,
NgxQRCodeModule,
QRCodeModule,
TranslateModule.forChild({
loader: {
provide: TranslateLoader,

View file

@ -11,10 +11,10 @@
<div class="d-flex align-items-center justify-content-top flex-column" style="height: 100%;">
<ion-row>
<ion-col class="ion-text-center ion-padding" style="max-width: 100% !important;">
<ngx-qrcode [elementType]="qrElementType" [value]="qrCodeContent" [width]="defaultWidth"
<qrcode [elementType]="qrElementType" [qrdata]="qrCodeContent" [width]="defaultWidth"
[errorCorrectionLevel]="errorCorrectionLevel" [colorDark]="qrColorDark" [colorLight]="qrColorLight"
[margin]="env.qrCodeMargin" #qrcode [hidden]="isSharing">
</ngx-qrcode>
</qrcode>
<div class="d-flex flex-column align-items-center" *ngIf="isSharing">
<ion-spinner class="my-3"></ion-spinner>
</div>

View file

@ -6,10 +6,11 @@ import { Haptics, ImpactStyle } from '@capacitor/haptics';
import { Toast } from '@capacitor/toast';
import { LoadingController, ModalController, Platform } from '@ionic/angular';
import { TranslateService } from '@ngx-translate/core';
import { NgxQrcodeElementTypes, NgxQrcodeErrorCorrectionLevels, QrcodeComponent } from '@techiediaries/ngx-qrcode';
import { EnvService } from 'src/app/services/env.service';
import { ScreenBrightness } from '@capacitor-community/screen-brightness';
import { rgbToHex } from 'src/app/utils/helpers';
import { Preferences } from '@capacitor/preferences';
import { QRCodeComponent, QRCodeElementType } from 'angularx-qrcode';
@Component({
selector: 'app-qr-code',
@ -20,11 +21,11 @@ export class QrCodePage {
modal: HTMLIonModalElement;
@ViewChild('qrcode') qrcodeElement: QrcodeComponent;
@ViewChild('qrcode') qrcodeElement: QRCodeComponent;
@Input() qrCodeContent: string;
qrElementType: NgxQrcodeElementTypes = NgxQrcodeElementTypes.CANVAS;
errorCorrectionLevel: NgxQrcodeErrorCorrectionLevels;
qrElementType: QRCodeElementType = "canvas";
errorCorrectionLevel: 'low' | 'medium' | 'quartile' | 'high' | 'L' | 'M' | 'Q' | 'H';
scale: number = 0.8;
readonly MAX_WIDTH = 350;
defaultWidth: number = window.innerHeight * 0.32 > this.MAX_WIDTH ? this.MAX_WIDTH : window.innerHeight * 0.32;
@ -63,7 +64,6 @@ export class QrCodePage {
if (this.qrcodeElement.width > this.MAX_WIDTH) {
this.qrcodeElement.width = this.MAX_WIDTH;
}
this.qrcodeElement.createQRCode();
}, 500)
}
}
@ -98,7 +98,6 @@ export class QrCodePage {
if (this.qrcodeElement.width > this.MAX_WIDTH) {
this.qrcodeElement.width = this.MAX_WIDTH;
}
this.qrcodeElement.createQRCode();
}
})
this.modal.onDidDismiss().then(
@ -131,25 +130,25 @@ export class QrCodePage {
setErrorCorrectionLevel() {
switch (this.env.errorCorrectionLevel) {
case 'L':
this.errorCorrectionLevel = NgxQrcodeErrorCorrectionLevels.LOW;
this.errorCorrectionLevel = 'low';
break;
case 'M':
this.errorCorrectionLevel = NgxQrcodeErrorCorrectionLevels.MEDIUM;
this.errorCorrectionLevel = 'medium';
break;
case 'Q':
this.errorCorrectionLevel = NgxQrcodeErrorCorrectionLevels.QUARTILE;
this.errorCorrectionLevel = 'quartile';
break;
case 'H':
this.errorCorrectionLevel = NgxQrcodeErrorCorrectionLevels.HIGH;
this.errorCorrectionLevel = 'high';
break;
default:
this.errorCorrectionLevel = NgxQrcodeErrorCorrectionLevels.MEDIUM;
this.errorCorrectionLevel = 'medium';
}
}
async onErrorCorrectionLevelChange() {
this.setErrorCorrectionLevel();
await this.env.storageSet("error-correction-level", this.env.errorCorrectionLevel);
await Preferences.set({ key: this.env.KEY_ERROR_CORRECTION_LEVEL, value: this.env.errorCorrectionLevel });
if (this.qrcodeElement != null) {
this.qrcodeElement.errorCorrectionLevel = this.errorCorrectionLevel;
} else {
@ -169,7 +168,6 @@ export class QrCodePage {
this.isSharing = true;
const currentWidth = this.qrcodeElement.width;
this.qrcodeElement.width = 1000;
this.qrcodeElement.createQRCode();
setTimeout(async () => {
const canvases = document.querySelectorAll("canvas") as NodeListOf<HTMLCanvasElement>;
const canvas = canvases[canvases.length - 1];
@ -182,7 +180,6 @@ export class QrCodePage {
await this.socialSharing.share(this.translate.instant('MSG.SHARE_QR'), this.translate.instant('SIMPLE_QR'), this.qrImageDataUrl, null).then(
_ => {
this.qrcodeElement.width = currentWidth;
this.qrcodeElement.createQRCode();
delete this.qrImageDataUrl;
this.isSharing = false;
loading2.dismiss();
@ -193,7 +190,6 @@ export class QrCodePage {
this.presentToast("Error when call SocialSharing.share: " + JSON.stringify(err), "long", "top");
}
this.qrcodeElement.width = currentWidth;
this.qrcodeElement.createQRCode();
delete this.qrImageDataUrl;
this.isSharing = false;
loading2.dismiss();
@ -241,7 +237,7 @@ export class QrCodePage {
async tapHaptic() {
if (this.env.vibration === 'on' || this.env.vibration === 'on-haptic') {
await Haptics.impact({ style: ImpactStyle.Medium })
await Haptics.impact({ style: ImpactStyle.Light })
.catch(async err => {
if (this.env.debugMode === 'on') {
await Toast.show({ text: 'Err when Haptics.impact: ' + JSON.stringify(err), position: "top", duration: "long" })

View file

@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar [color]="env.colorTheme === 'black'? 'black' : 'dark'">
<ion-buttons slot="start">
<ion-back-button text="">
<ion-back-button text="" defaultHref="tabs/setting">
</ion-back-button>
</ion-buttons>
<ion-title>{{ 'ABOUT' | translate }}</ion-title>

View file

@ -1,4 +1,5 @@
import { Component } from '@angular/core';
import { Preferences } from '@capacitor/preferences';
import { Toast } from '@capacitor/toast';
import { AlertController, Platform } from '@ionic/angular';
import { TranslateService } from '@ngx-translate/core';
@ -89,7 +90,7 @@ export class AboutPage {
if (this.env.debugMode != 'on') {
if (this.tapAppVersionTimes >= 5) {
this.env.debugMode = 'on';
await this.env.storageSet("debug-mode-on", 'on');
await Preferences.set({ key: this.env.KEY_DEBUG_MODE, value: 'on' });
await Toast.show({
text: this.translate.instant("MSG.DEBUG_MODE_ON"),
duration: "short",

View file

@ -25,20 +25,22 @@
<ion-row class="ion-padding-horizontal">
<ion-col>
<mat-form-field [class]="ngMatThemeClass" color="accent" appearance="legacy">
<mat-form-field [class]="ngMatThemeClass" color="accent" appearance="outline" [floatLabel]="'always'">
<mat-label>{{ 'CONTENT_TYPE' | translate}}</mat-label>
<mat-select [(ngModel)]="contentType">
<mat-select-trigger>
<div style="display: flex; align-items: center;">
<mat-icon style="margin-right: 16px; vertical-align: middle;">
{{ getIcon(contentType) }}
<div class="d-flex">
<mat-icon class="me-3" [fontIcon]="getIcon(contentType)">
</mat-icon>
<span>{{ getText(contentType) }}</span>
</div>
</mat-select-trigger>
<mat-option [value]="type.value" *ngFor="let type of contentTypes">
<mat-icon>{{ getIcon(type.value) }}</mat-icon>
<span>{{ type.text }}</span>
<div class="d-flex">
<mat-icon class="me-3" [fontIcon]="getIcon(type.value)">
</mat-icon>
<span>{{ type.text }}</span>
</div>
</mat-option>
</mat-select>
</mat-form-field>
@ -69,7 +71,7 @@
<mat-label>{{ 'EMAIL_RECIPIENT' | translate}}</mat-label>
<input matInput [(ngModel)]="toEmails[i]" type="email" maxlength="254" #emailToInput>
<mat-hint align="end">{{ emailToInput.value?.length || 0 }}/254</mat-hint>
<mat-icon matSuffix color="accent">alternate_email</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="alternate_email"></mat-icon>
<mat-hint>{{'MSG.EMAIL_MAX_LENGTH' | translate}}</mat-hint>
</mat-form-field>
</ion-col>
@ -98,7 +100,7 @@
<mat-label>{{ 'CC' | translate}}</mat-label>
<input matInput [(ngModel)]="ccEmails[i]" type="email" maxlength="254" #emailCcInput>
<mat-hint align="end">{{ emailCcInput.value?.length || 0 }}/254</mat-hint>
<mat-icon matSuffix color="accent">alternate_email</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="alternate_email"></mat-icon>
<mat-hint>{{'MSG.EMAIL_MAX_LENGTH' | translate}}</mat-hint>
</mat-form-field>
</ion-col>
@ -127,7 +129,7 @@
<mat-label>{{ 'BCC' | translate}}</mat-label>
<input matInput [(ngModel)]="bccEmails[i]" type="email" maxlength="254" #emailBccInput>
<mat-hint align="end">{{ emailBccInput.value?.length || 0 }}/254</mat-hint>
<mat-icon matSuffix color="accent">alternate_email</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="alternate_email"></mat-icon>
<mat-hint>{{'MSG.EMAIL_MAX_LENGTH' | translate}}</mat-hint>
</mat-form-field>
</ion-col>
@ -182,7 +184,7 @@
<mat-label>{{ 'EMAIL_RECIPIENT' | translate}}</mat-label>
<input matInput [(ngModel)]="toEmails[0]" type="email" maxlength="254" #emailToInput>
<mat-hint align="end">{{ emailToInput.value?.length || 0 }}/254</mat-hint>
<mat-icon matSuffix color="accent">alternate_email</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="alternate_email"></mat-icon>
<mat-hint>{{'MSG.EMAIL_MAX_LENGTH' | translate}}</mat-hint>
</mat-form-field>
</ion-col>
@ -213,13 +215,32 @@
</ng-container>
<ng-container *ngIf="contentType === 'geo'">
<ion-row class="ion-padding-horizontal" [@inAnimation]>
<ion-col>
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'LATITUDE' | translate}}</mat-label>
<input matInput [(ngModel)]="latitude" type="number" #latitudeInput>
</mat-form-field>
</ion-col>
</ion-row>
<ion-row class="ion-padding-horizontal" [@inAnimation]>
<ion-col>
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'LONGITUDE' | translate}}</mat-label>
<input matInput [(ngModel)]="longitude" type="number" #longitudeInput>
</mat-form-field>
</ion-col>
</ion-row>
</ng-container>
<ng-container *ngIf="contentType === 'phone'">
<ion-row class="ion-padding-horizontal" [@inAnimation]>
<ion-col>
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'PHONE_NUMBER' | translate}}</mat-label>
<input matInput [(ngModel)]="phoneNumber" type="tel" #phoneNumberInput>
<mat-icon matSuffix color="accent">call</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="call"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -231,7 +252,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'PHONE_NUMBER' | translate}}</mat-label>
<input matInput [(ngModel)]="phoneNumber" type="tel" #phoneNumberInput>
<mat-icon matSuffix color="accent">call</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="call"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -254,7 +275,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'URL' | translate}}</mat-label>
<input matInput [(ngModel)]="url" type="url" #urlInput maxlength="1817">
<mat-icon matSuffix color="accent">link</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="link"></mat-icon>
<mat-hint align="end">{{ (urlInput.value?.length) || 0 }}/1817</mat-hint>
<mat-hint>{{'MSG.CREATE_QRCODE_MAX_LENGTH' | translate}}</mat-hint>
</mat-form-field>
@ -278,7 +299,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'FIRST_NAME' | translate}}</mat-label>
<input matInput [(ngModel)]="firstName" #firstNameInput />
<mat-icon matSuffix color="accent">badge</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="badge"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -287,7 +308,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'LAST_NAME' | translate}}</mat-label>
<input matInput [(ngModel)]="lastName" #lastNameInput />
<mat-icon matSuffix color="accent">badge</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="badge"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -306,7 +327,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'MOBILE_PHONE_NUMBER' | translate}}</mat-label>
<input matInput [(ngModel)]="mobilePhoneNumber" #mobilePhoneInput type="tel" />
<mat-icon matSuffix color="accent">call</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="call"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -315,7 +336,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'HOME_PHONE_NUMBER' | translate}}</mat-label>
<input matInput [(ngModel)]="homePhoneNumber" #homePhoneInput type="tel" />
<mat-icon matSuffix color="accent">call</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="call"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -324,7 +345,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'WORK_PHONE_NUMBER' | translate}}</mat-label>
<input matInput [(ngModel)]="workPhoneNumber" #workPhoneInput type="tel" />
<mat-icon matSuffix color="accent">call</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="call"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -333,7 +354,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'FAX_NUMBER' | translate}}</mat-label>
<input matInput [(ngModel)]="faxNumber" #faxInput type="tel" />
<mat-icon matSuffix color="accent">print</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="print"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -342,7 +363,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'EMAIL_ADDRESS' | translate}}</mat-label>
<input matInput [(ngModel)]="email" #emailInput type="email" />
<mat-icon matSuffix color="accent">alternate_email</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="alternate_email"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -361,7 +382,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'ORGANIZATION' | translate}}</mat-label>
<input matInput [(ngModel)]="organization" #organizationInput type="text" />
<mat-icon matSuffix color="accent">business</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="business"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -370,7 +391,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'JOB_TITLE' | translate}}</mat-label>
<input matInput [(ngModel)]="jobTitle" #jobTitleInput type="text" />
<mat-icon matSuffix color="accent">business</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="business"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -389,7 +410,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'STREET' | translate}}</mat-label>
<input matInput [(ngModel)]="street" #streetInput type="text" />
<mat-icon matSuffix color="accent">home</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="home"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -398,7 +419,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'CITY' | translate}}</mat-label>
<input matInput [(ngModel)]="city" #cityInput type="text" />
<mat-icon matSuffix color="accent">home</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="home"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -407,7 +428,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'STATE' | translate}}</mat-label>
<input matInput [(ngModel)]="state" #stateInput type="text" />
<mat-icon matSuffix color="accent">home</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="home"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -416,7 +437,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'POSTAL_CODE' | translate}}</mat-label>
<input matInput [(ngModel)]="postalCode" #postalCodeInput type="text" />
<mat-icon matSuffix color="accent">home</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="home"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -425,7 +446,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'COUNTRY' | translate}}</mat-label>
<input matInput [(ngModel)]="country" #countryInput type="text" />
<mat-icon matSuffix color="accent">home</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="home"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -445,7 +466,7 @@
<mat-label>{{ 'DATE_OF_BIRTH' | translate}}</mat-label>
<input [ngModel]="birthday | date:'yyyy-MM-dd'" (ngModelChange)="birthday = $event" matInput type="date"
[max]="today">
<mat-icon matSuffix color="accent">cake</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="cake"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -466,7 +487,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'WEBSITE' | translate}}</mat-label>
<input matInput [(ngModel)]="personalUrl" type="url" #personalUrlInput>
<mat-icon matSuffix color="accent">link</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="link"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>
@ -478,7 +499,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'WIFI_SSID' | translate}}</mat-label>
<input matInput [(ngModel)]="ssid" #ssidInput maxlength="32">
<mat-icon matSuffix color="accent">wifi</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="wifi"></mat-icon>
<mat-hint align="end">{{ (ssidInput.value?.length) || 0 }}/32</mat-hint>
<mat-hint>{{'MSG.SSID_MAX_LENGTH' | translate}}</mat-hint>
</mat-form-field>
@ -489,7 +510,7 @@
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent">
<mat-label>{{ 'PASSWORD' | translate}}</mat-label>
<input matInput [(ngModel)]="wifiPassword" #wifiPasswordInput type="password">
<mat-icon matSuffix color="accent">lock</mat-icon>
<mat-icon matSuffix color="accent" fontIcon="lock"></mat-icon>
</mat-form-field>
</ion-col>
</ion-row>

View file

@ -4,8 +4,8 @@ import { Router } from '@angular/router';
import { Haptics, ImpactStyle, NotificationType } from '@capacitor/haptics';
import { AlertController, LoadingController, ToastController } from '@ionic/angular';
import { TranslateService } from '@ngx-translate/core';
import * as moment from 'moment';
import { EnvService } from 'src/app/services/env.service';
import { format } from 'date-fns';
import { EnvService, QrCreateContentTypeType } from 'src/app/services/env.service';
import { Toast } from '@capacitor/toast';
import { fadeIn } from 'src/app/utils/animations';
import { SplashScreen } from '@capacitor/splash-screen';
@ -23,6 +23,7 @@ export class GeneratePage {
freeTxtText: string = "Free Text";
urlText: string = "URL";
contactText: string = "vCard Contact";
geolocationText: string = "Geolocation";
phoneText: string = "Phone";
smsText: string = "Message";
emailW3CText: string = "Email (W3C Standard)";
@ -37,6 +38,9 @@ export class GeneratePage {
emailSubject: string = "";
emailBody: string = "";
latitude: number = 0;
longitude: number = 0;
phoneNumber: string = "";
smsMessage: string = "";
@ -57,7 +61,7 @@ export class GeneratePage {
state: string = "";
postalCode: string = "";
country: string = "";
birthday: Date;
birthday: string;
gender: "M" | "F" | "O" = "O";
personalUrl: string = "";
@ -84,17 +88,18 @@ export class GeneratePage {
{ text: this.wpaText, value: "WPA" },
]
contentTypes: { text: string, value: "freeText" | "url" | "contact" | "phone" | "sms" | "emailW3C" | "emailDocomo" | "wifi" }[] = [
contentTypes: { text: string, value: QrCreateContentTypeType }[] = [
{ text: this.freeTxtText, value: 'freeText' },
{ text: this.emailW3CText, value: 'emailW3C' },
{ text: this.emailDocomoText, value: 'emailDocomo' },
{ text: this.geolocationText, value: 'geo' },
{ text: this.phoneText, value: 'phone' },
{ text: this.smsText, value: 'sms' },
{ text: this.urlText, value: 'url' },
{ text: this.contactText, value: 'contact' },
{ text: this.wifiText, value: 'wifi' },
];
contentType: "freeText" | "url" | "contact" | "phone" | "sms" | "emailW3C" | "emailDocomo" | "wifi" = "freeText";
contentType: QrCreateContentTypeType = "freeText";
constructor(
public translate: TranslateService,
@ -106,9 +111,14 @@ export class GeneratePage {
async ionViewDidEnter() {
await SplashScreen.hide()
if (this.env.editingContent) {
this.qrCodeContent = this.env.resultContent;
this.env.editingContent = false;
}
this.freeTxtText = this.translate.instant("FREE_TEXT");
this.urlText = this.translate.instant("URL");
this.contactText = this.translate.instant("VCARD_CONTACT");
this.geolocationText = this.translate.instant("GEOLOCATION");
this.phoneText = this.translate.instant("PHONE_NO");
this.smsText = this.translate.instant("MESSAGE");
this.emailW3CText = this.translate.instant("EMAIL_W3C_STANDARD");
@ -118,6 +128,7 @@ export class GeneratePage {
{ text: this.freeTxtText, value: 'freeText' },
{ text: this.emailW3CText, value: 'emailW3C' },
{ text: this.emailDocomoText, value: 'emailDocomo' },
{ text: this.geolocationText, value: 'geo' },
{ text: this.phoneText, value: 'phone' },
{ text: this.smsText, value: 'sms' },
{ text: this.urlText, value: 'url' },
@ -191,6 +202,9 @@ export class GeneratePage {
this.emailSubject = "";
this.emailBody = "";
this.latitude = 0;
this.longitude = 0;
this.phoneNumber = "";
this.smsMessage = "";
@ -259,6 +273,9 @@ export class GeneratePage {
this.qrCodeContent = `MATMSG:TO:${this.toEmails[0]};SUB:${this.emailSubject};BODY:${this.emailBody};;`;
this.qrCodeContent = encodeURI(this.qrCodeContent);
break;
case "geo":
this.qrCodeContent = `geo:${this.latitude},${this.longitude}`;
break;
case "phone":
this.qrCodeContent = "tel:";
this.qrCodeContent += this.phoneNumber;
@ -295,8 +312,8 @@ export class GeneratePage {
}
async processQrCode(loading: HTMLIonLoadingElement): Promise<void> {
this.env.result = this.qrCodeContent;
this.env.resultFormat = "";
this.env.resultContent = this.qrCodeContent;
this.env.resultContentFormat = "";
this.qrCodeContent = '';
this.env.recordSource = "create";
this.env.detailedRecordSource = "create";
@ -320,9 +337,10 @@ export class GeneratePage {
vCard += `ORG:${this.organization.trim()}\n`;
vCard += `TITLE:${this.jobTitle.trim()}\n`;
vCard += `ADR:;;${this.street.trim()};${this.city.trim()};${this.state.trim()};${this.postalCode.trim()};${this.country.trim()}\n`;
console.log("birthday => " + this.birthday)
if (this.birthday && this.birthday !== null && this.birthday !== undefined) {
vCard += `BDAY:${moment(this.birthday).format('YYYYMMDD')}\n`;
if (this.birthday != null) {
const find = '-';
const re = new RegExp(find, 'g');
vCard += `BDAY:${this.birthday.replace(re, "")}\n`;
}
vCard += `URL:${this.personalUrl.trim()}\n`;
vCard += `GENDER:${this.gender}\n`;
@ -340,10 +358,10 @@ export class GeneratePage {
}
get today() {
return moment().format("YYYY-MM-DD");
return format(new Date(), "yyyy-MM-dd");
}
getIcon(type: "freeText" | "url" | "contact" | "phone" | "sms" | "emailW3C" | "emailDocomo" | "wifi"): string {
getIcon(type: QrCreateContentTypeType): string {
switch (type) {
case "freeText":
return "format_align_left";
@ -351,6 +369,8 @@ export class GeneratePage {
return "link";
case "contact":
return "contact_phone";
case "geo":
return "location_on";
case "phone":
return "call";
case "sms":
@ -366,7 +386,7 @@ export class GeneratePage {
}
}
getText(type: "freeText" | "url" | "contact" | "phone" | "sms" | "emailW3C" | "emailDocomo" | "wifi"): string {
getText(type: QrCreateContentTypeType): string {
switch (type) {
case "freeText":
return this.freeTxtText;
@ -374,6 +394,8 @@ export class GeneratePage {
return this.urlText;
case "contact":
return this.contactText;
case "geo":
return this.geolocationText;
case "phone":
return this.phoneText;
case "sms":
@ -442,7 +464,7 @@ export class GeneratePage {
async tapHaptic() {
if (this.env.vibration === 'on' || this.env.vibration === 'on-haptic') {
await Haptics.impact({ style: ImpactStyle.Medium })
await Haptics.impact({ style: ImpactStyle.Light })
.catch(async err => {
if (this.env.debugMode === 'on') {
await Toast.show({ text: 'Err when Haptics.impact: ' + JSON.stringify(err), position: "top", duration: "long" })

View file

@ -6,7 +6,7 @@
<ion-title>{{ 'SIMPLE_QR' | translate}}</ion-title>
<ion-buttons slot="end">
<ion-button (click)="tapHaptic(); goSetting()" fill="clear">
{{ 'SETTING' | translate }}
{{ 'MORE' | translate }}
</ion-button>
</ion-buttons>
</ion-toolbar>
@ -17,7 +17,7 @@
<ion-title *ngIf="segmentModel === 'bookmarks'">{{ 'BOOKMARKS' | translate }}</ion-title>
<ion-buttons slot="end">
<ion-button (click)="tapHaptic(); goSetting()" fill="clear" color="primary">
{{ 'SETTING' | translate }}
{{ 'MORE' | translate }}
</ion-button>
</ion-buttons>
</ion-toolbar>
@ -96,7 +96,7 @@
<ng-container
*ngIf="segmentModel === 'history' && env.viewingScanRecords && env.viewingScanRecords.length > 0 && !isLoading">
<ion-list>
<ion-list-header class="ion-padding-horizontal" lines="full">
<ion-list-header class="ion-padding-horizontal" lines="none" *ngIf="env.showNumberOfRecords == 'on'">
<div class="ion-padding-horizontal" *ngIf="env.recordsLimit !== -1">
{{ 'NUMBER_OF_RECORDS' | translate }}: {{ env.scanRecords.length }} / {{ denominator }}
</div>

View file

@ -2,14 +2,13 @@ import { ChangeDetectorRef, Component } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { AlertController, IonItemSliding, LoadingController, ModalController, PopoverController, ToastController } from '@ionic/angular';
import { EnvService } from 'src/app/services/env.service';
import * as moment from 'moment';
import { format, Locale } from 'date-fns';
import { de, enUS, fr, it, ptBR, ru, zhCN, zhHK } from 'date-fns/locale';
import { ScanRecord } from 'src/app/models/scan-record';
import { TranslateService } from '@ngx-translate/core';
import { Bookmark } from 'src/app/models/bookmark';
import { HistoryTutorialPage } from 'src/app/modals/history-tutorial/history-tutorial.page';
import { Haptics, ImpactStyle } from '@capacitor/haptics';
import { Toast } from '@capacitor/toast';
import { BookmarkTutorialPage } from 'src/app/modals/bookmark-tutorial/bookmark-tutorial.page';
import { fastFadeIn, flyOut } from 'src/app/utils/animations';
import { SplashScreen } from '@capacitor/splash-screen';
@ -99,19 +98,6 @@ export class HistoryPage {
async ionViewDidEnter() {
await SplashScreen.hide()
this.segmentModel = this.env.historyPageStartSegment;
if (this.segmentModel == 'history') {
if (this.env.notShowHistoryTutorial === false) {
this.env.notShowHistoryTutorial = true;
this.env.storageSet("not-show-history-tutorial", 'yes');
await this.showHistoryTutorial();
}
} else if (this.segmentModel == 'bookmarks') {
if (this.env.notShowBookmarkTutorial === false) {
this.env.notShowBookmarkTutorial = true;
this.env.storageSet("not-show-bookmark-tutorial", 'yes');
await this.showBookmarkTutorial();
}
}
}
ionViewWillLeave() {
@ -134,41 +120,46 @@ export class HistoryPage {
return bookmark.id;
}
async showHistoryTutorial() {
const modal = await this.modalController.create({
component: HistoryTutorialPage,
componentProps: {
}
});
modal.present();
}
async showBookmarkTutorial() {
const modal = await this.modalController.create({
component: BookmarkTutorialPage,
componentProps: {
}
});
modal.present();
}
maskDatetimeAndSource(date: Date, source: 'create' | 'view' | 'scan' | undefined): string {
if (!date) {
return "-";
}
const momentObj = moment(date);
if (this.env.language != 'en') {
momentObj.locale(this.env.language.toLowerCase());
let locale: Locale;
switch (this.env.language) {
case "de":
locale = de;
break;
case "en":
locale = enUS;
break;
case "fr":
locale = fr;
break;
case "it":
locale = it;
break;
case "pt-BR":
locale = ptBR;
break;
case "ru":
locale = ru;
break;
case "zh-CN":
locale = zhCN;
break;
case "zh-HK":
locale = zhHK;
break;
default:
locale = enUS;
}
switch (source) {
case 'create':
return `${this.translate.instant("CREATED")} ${this.translate.instant("AT")} ${momentObj.format("ll LTS")}`;
return `${this.translate.instant("CREATED")} ${this.translate.instant("AT")} ${format(date, "PP pp", { locale: locale })}`;
case 'view':
return `${this.translate.instant("VIEWED")} ${this.translate.instant("AT")} ${momentObj.format("ll LTS")}`;
return `${this.translate.instant("VIEWED")} ${this.translate.instant("AT")} ${format(date, "PP pp", { locale: locale })}`;
case 'scan':
return `${this.translate.instant("SCANNED")} ${this.translate.instant("AT")} ${momentObj.format("ll LTS")}`;
default:
return momentObj.format("ll LTS");
return `${this.translate.instant("SCANNED")} ${this.translate.instant("AT")} ${format(date, "PP pp", { locale: locale })}`;
}
}
@ -213,7 +204,7 @@ export class HistoryPage {
case "RSS_EXPANDED":
return this.translate.instant("BARCODE_TYPE.RSS").trim();
default:
return this.env.resultFormat;
return this.env.resultContentFormat;
}
}
@ -225,8 +216,8 @@ export class HistoryPage {
this.changeDetectorRef.detectChanges();
this.changeDetectorRef.reattach();
const loading = await this.presentLoading(this.translate.instant('PLEASE_WAIT'));
this.env.result = data;
this.env.resultFormat = "";
this.env.resultContent = data;
this.env.resultContentFormat = "";
this.env.recordSource = "view";
this.env.detailedRecordSource = source;
this.env.viewResultFrom = "/tabs/history";
@ -238,19 +229,6 @@ export class HistoryPage {
}
async segmentChanged(ev: any) {
if (ev?.detail?.value == 'history') {
if (this.env.notShowHistoryTutorial === false) {
this.env.notShowHistoryTutorial = true;
this.env.storageSet("not-show-history-tutorial", 'yes');
await this.showHistoryTutorial();
}
} else if (ev?.detail?.value == 'bookmarks') {
if (this.env.notShowBookmarkTutorial === false) {
this.env.notShowBookmarkTutorial = true;
this.env.storageSet("not-show-bookmark-tutorial", 'yes');
await this.showBookmarkTutorial();
}
}
this.firstLoadItems();
}
@ -530,7 +508,7 @@ export class HistoryPage {
async tapHaptic() {
if (this.env.vibration === 'on' || this.env.vibration === 'on-haptic') {
await Haptics.impact({ style: ImpactStyle.Medium })
await Haptics.impact({ style: ImpactStyle.Light })
.catch(async err => {
if (this.env.debugMode === 'on') {
await Toast.show({ text: 'Err when Haptics.impact: ' + JSON.stringify(err), position: "top", duration: "long" })

View file

@ -9,7 +9,6 @@ import { ImportImagePageRoutingModule } from './import-image-routing.module';
import { ImportImagePage } from './import-image.page';
import { HttpClient } from '@angular/common/http';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
@ -29,7 +28,6 @@ export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
}
}),
ImportImagePageRoutingModule,
MatButtonModule,
],
declarations: [ImportImagePage]
})

View file

@ -96,13 +96,21 @@ export class ImportImagePage {
});
await alert.present();
}
},
async err => {
getPictureLoading.dismiss();
if (this.env.debugMode === 'on') {
await Toast.show({ text: 'Err when Camera.requestPermissions: ' + JSON.stringify(err), position: "bottom", duration: "long" })
} else {
Toast.show({ text: 'Unknown Error', position: "bottom", duration: "short" })
}
}
);
}
async processQrCode(scannedData: string, loading: HTMLIonLoadingElement): Promise<void> {
this.env.result = scannedData;
this.env.resultFormat = "QR_CODE";
this.env.resultContent = scannedData;
this.env.resultContentFormat = "QR_CODE";
this.env.recordSource = "scan";
this.env.detailedRecordSource = "scan-image";
this.env.viewResultFrom = "/tabs/import-image";
@ -196,7 +204,7 @@ export class ImportImagePage {
async tapHaptic() {
if (this.env.vibration === 'on' || this.env.vibration === 'on-haptic') {
await Haptics.impact({ style: ImpactStyle.Medium })
await Haptics.impact({ style: ImpactStyle.Light })
.catch(async err => {
if (this.env.debugMode === 'on') {
await Toast.show({ text: 'Err when Haptics.impact: ' + JSON.stringify(err), position: "top", duration: "long" })

View file

@ -1,5 +1,6 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { Preferences } from '@capacitor/preferences';
import { AlertController, IonRouterOutlet, Platform } from '@ionic/angular';
import { TranslateService } from '@ngx-translate/core';
import { EnvService } from 'src/app/services/env.service';
@ -50,26 +51,45 @@ export class LandingPage {
}
async confirmExitApp(): Promise<void> {
const alert = await this.alertController.create({
header: this.translate.instant('EXIT_APP'),
message: this.translate.instant('MSG.EXIT_APP'),
cssClass: ['alert-bg'],
buttons: [
{
text: this.translate.instant('EXIT'),
handler: () => {
navigator['app'].exitApp();
if (this.env.showExitAppAlert == "on") {
const alert = await this.alertController.create({
header: this.translate.instant('EXIT_APP'),
message: this.translate.instant('MSG.EXIT_APP'),
inputs: [
{
type: "checkbox",
label: this.translate.instant("MSG.TUTORIAL_NOT_SHOW_AGAIN"),
checked: false,
handler: async (result) => {
if (result.checked) {
this.env.showExitAppAlert = "off";
} else {
this.env.showExitAppAlert = "on";
}
await Preferences.set({ key: this.env.KEY_SHOW_EXIT_APP_ALERT, value: this.env.showExitAppAlert });
}
}
},
{
text: this.translate.instant('RATE_THE_APP'),
handler: () => {
this.openGooglePlay();
],
cssClass: ['alert-bg', 'alert-input-no-border'],
buttons: [
{
text: this.translate.instant('EXIT'),
handler: () => {
navigator['app'].exitApp();
}
},
{
text: this.translate.instant('RATE_THE_APP'),
handler: () => {
this.openGooglePlay();
}
}
}
]
});
await alert.present();
]
});
await alert.present();
} else {
navigator['app'].exitApp();
}
}
}

View file

@ -7,7 +7,6 @@ import { IonicModule } from '@ionic/angular';
import { ResultPageRoutingModule } from './result-routing.module';
import { ResultPage } from './result.page';
import { NgxQRCodeModule } from '@techiediaries/ngx-qrcode';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { HttpClient } from '@angular/common/http';

View file

@ -18,8 +18,7 @@
<ion-col>
<ion-badge class="p-2" color="primary">
<div class="d-flex align-items-center">
<mat-icon *ngIf="contentTypeIcon != ''" style="margin-right: 16px;">
{{ contentTypeIcon }}
<mat-icon class="me-2" *ngIf="contentTypeIcon != ''" [fontIcon]="contentTypeIcon">
</mat-icon>
<span>{{ contentTypeText }}</span>
</div>
@ -28,7 +27,7 @@
</ion-row>
<ng-container *ngIf="qrCodeContent && qrCodeContent.trim().length > 0" [ngTemplateOutlet]="contentBlock"
[ngTemplateOutletContext]="{ label: barcodeFormat + ('CONTENT' | translate), content: qrCodeContent, hint: env.resultFormat }">
[ngTemplateOutletContext]="{ label: barcodeFormat + ('CONTENT' | translate), content: qrCodeContent, hint: env.resultContentFormat, showEdit: true }">
</ng-container>
<ng-container *ngIf="base64Encoded && qrCodeContent && qrCodeContent.trim().length > 0"
@ -97,6 +96,15 @@
[ngTemplateOutletContext]="{ label: 'HIDDEN_NETWORK_?' | translate, content: wifiHidden === true? ('YES' | translate) : ('NO' | translate) }">
</ng-container>
<ng-container *ngIf="contentType === 'geo' && qrCodeContent && qrCodeContent.trim().length > 0 && latitude != null"
[ngTemplateOutlet]="contentBlock" [ngTemplateOutletContext]="{ label: 'LATITUDE' | translate, content: latitude }">
</ng-container>
<ng-container *ngIf="contentType === 'geo' && qrCodeContent && qrCodeContent.trim().length > 0 && longitude != null"
[ngTemplateOutlet]="contentBlock"
[ngTemplateOutletContext]="{ label: 'LONGITUDE' | translate, content: longitude }">
</ng-container>
<div class="ion-padding-horizontal ion-margin-horizontal ion-padding-bottom">
</div>
@ -106,18 +114,34 @@
[ngStyle]="env.colorTheme === 'dark'? {'background-color': '#222428'} : (env.colorTheme === 'black'? {'background-color': '#000000'} : {'background-color': '#F0F0F0'})">
<ng-container *ngIf="env.resultPageButtons === 'icon-only'">
<ion-row *ngIf="contentType === 'url' && env.showBrowseButton === 'on' && isHttp" class="d-flex justify-content-center">
<ion-row *ngIf="contentType === 'freeText' && env.showOpenFoodFactsButton === 'on' && isValidEan"
class="d-flex justify-content-center">
<ion-button (click)="tapHaptic(); searchOpenFoodFacts()" [color]="'primary'" fill="clear">
<ion-icon name="fast-food"></ion-icon>
</ion-button>
</ion-row>
<ion-row *ngIf="contentType === 'url' && env.showBrowseButton === 'on' && isHttp"
class="d-flex justify-content-center">
<ion-button (click)="tapHaptic(); browseWebsite()" [color]="'primary'" fill="clear">
<ion-icon name="globe"></ion-icon>
</ion-button>
</ion-row>
<ion-row *ngIf="contentType === 'url' && env.showOpenUrlButton === 'on' && !isHttp" class="d-flex justify-content-center">
<ion-button *ngIf="" (click)="tapHaptic(); openLink()" [color]="'primary'" fill="clear">
<ion-row *ngIf="contentType === 'url' && env.showOpenUrlButton === 'on' && !isHttp"
class="d-flex justify-content-center">
<ion-button (click)="tapHaptic(); openLink()" [color]="'primary'" fill="clear">
<ion-icon name="open"></ion-icon>
</ion-button>
</ion-row>
<ion-row *ngIf="contentType === 'geo' && env.showOpenUrlButton === 'on'"
class="d-flex justify-content-center">
<ion-button (click)="tapHaptic(); openLink()" [color]="'primary'" fill="clear">
<ion-icon name="map"></ion-icon>
</ion-button>
</ion-row>
<ion-row *ngIf="contentType === 'contact' && env.showAddContactButton === 'on'"
class="d-flex justify-content-center">
<ion-button (click)="tapHaptic(); addContact()" [color]="'primary'" fill="clear">
@ -164,124 +188,156 @@
</ion-button>
</ion-row>
<ion-row class="ion-padding-horizontal justify-content-around">
<ion-button *ngIf="env.showSearchButton === 'on'" (click)="tapHaptic(); webSearch()"
[color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="clear"
[disabled]="!qrCodeContent || (qrCodeContent && qrCodeContent.trim().length <= 0)">
<ion-icon *ngIf="env.searchEngine === 'google'" slot="icon-only" name="logo-google">
</ion-icon>
<ion-icon *ngIf="env.searchEngine === 'bing'" slot="icon-only" src="assets/icon/microsoft-bing.svg">
</ion-icon>
<ion-icon *ngIf="env.searchEngine === 'yahoo'" slot="icon-only" src="assets/icon/yahoo.svg">
</ion-icon>
<ion-icon *ngIf="env.searchEngine === 'duckduckgo'" slot="icon-only" src="assets/icon/duck-duck-go.svg">
</ion-icon>
<ion-icon *ngIf="env.searchEngine === 'yandex'" slot="icon-only" src="assets/icon/yandex.svg">
</ion-icon>
</ion-button>
<ion-button *ngIf="env.showCopyButton === 'on'" (click)="tapHaptic(); copyText()"
[color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="clear"
[disabled]="!qrCodeContent || (qrCodeContent && qrCodeContent.trim().length <= 0)">
<ion-icon slot="icon-only" name="copy"></ion-icon>
</ion-button>
<ion-button *ngIf="env.showBase64Button === 'on'" (click)="tapHaptic(); base64()"
[color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="clear"
[disabled]="!qrCodeContent || (qrCodeContent && qrCodeContent.trim().length <= 0)">
<ion-icon slot="icon-only" name="code-working"></ion-icon>
</ion-button>
<ion-button *ngIf="env.showEnlargeButton === 'on'" (click)="tapHaptic(); enlarge()"
[color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="clear"
[disabled]="!qrCodeContent || (qrCodeContent && qrCodeContent.trim().length <= 0)">
<ion-icon slot="icon-only" name="qr-code-sharp"></ion-icon>
</ion-button>
<ion-button *ngIf="env.showBookmarkButton === 'on'" (click)="tapHaptic(); handleBookmark()"
[color]="!bookmarked? (env.colorTheme === 'light'? 'dark' : 'light') : 'warning'" fill="clear"
[disabled]="!qrCodeContent || (qrCodeContent && qrCodeContent.trim().length <= 0)">
<ion-icon slot="icon-only" name="bookmark"></ion-icon>
</ion-button>
<ion-row class="ion-padding-horizontal">
<div class="d-flex justify-content-between detailed-action-button-container">
<ion-button
*ngIf="!resultSaved && this.env.scanRecordLogging == 'off' && this.qrCodeContent != null && this.qrCodeContent != '' "
(click)="tapHaptic(); saveRecord()" [color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="clear">
<ion-icon slot="icon-only" src="assets/icon/history.svg"></ion-icon>
</ion-button>
<ion-button *ngIf="env.showSearchButton === 'on'" (click)="tapHaptic(); webSearch()"
[color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="clear"
[disabled]="!qrCodeContent || (qrCodeContent && qrCodeContent.trim().length <= 0)">
<ion-icon *ngIf="env.searchEngine === 'google'" slot="icon-only" name="logo-google">
</ion-icon>
<ion-icon *ngIf="env.searchEngine === 'bing'" slot="icon-only" src="assets/icon/microsoft-bing.svg">
</ion-icon>
<ion-icon *ngIf="env.searchEngine === 'yahoo'" slot="icon-only" src="assets/icon/yahoo.svg">
</ion-icon>
<ion-icon *ngIf="env.searchEngine === 'duckduckgo'" slot="icon-only" src="assets/icon/duck-duck-go.svg">
</ion-icon>
<ion-icon *ngIf="env.searchEngine === 'yandex'" slot="icon-only" src="assets/icon/yandex.svg">
</ion-icon>
<ion-icon *ngIf="env.searchEngine === 'ecosia'" slot="icon-only" src="assets/icon/ecosia.svg">
</ion-icon>
<ion-icon *ngIf="env.searchEngine === 'brave'" slot="icon-only" src="assets/icon/brave-search.svg">
</ion-icon>
</ion-button>
<ion-button *ngIf="env.showCopyButton === 'on'" (click)="tapHaptic(); copyText()"
[color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="clear"
[disabled]="!qrCodeContent || (qrCodeContent && qrCodeContent.trim().length <= 0)">
<ion-icon slot="icon-only" name="copy"></ion-icon>
</ion-button>
<ion-button *ngIf="env.showBase64Button === 'on'" (click)="tapHaptic(); base64()"
[color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="clear"
[disabled]="!qrCodeContent || (qrCodeContent && qrCodeContent.trim().length <= 0)">
<ion-icon slot="icon-only" name="code-working"></ion-icon>
</ion-button>
<ion-button *ngIf="env.showEnlargeButton === 'on'" (click)="tapHaptic(); enlarge()"
[color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="clear"
[disabled]="!qrCodeContent || (qrCodeContent && qrCodeContent.trim().length <= 0)">
<ion-icon slot="icon-only" name="qr-code-sharp"></ion-icon>
</ion-button>
<ion-button *ngIf="env.showBookmarkButton === 'on'" (click)="tapHaptic(); handleBookmark()"
[color]="!bookmarked? (env.colorTheme === 'light'? 'dark' : 'light') : 'warning'" fill="clear"
[disabled]="!qrCodeContent || (qrCodeContent && qrCodeContent.trim().length <= 0)">
<ion-icon slot="icon-only" name="bookmark"></ion-icon>
</ion-button>
</div>
</ion-row>
</ng-container>
<ng-container *ngIf="env.resultPageButtons === 'detailed'">
<ion-row class="ion-padding-horizontal py-2">
<div class="d-flex justify-content-between detailed-action-button-container">
<ion-button class="pr-1" *ngIf="contentType === 'url' && env.showBrowseButton === 'on' && isHttp"
<ion-button class="pe-1"
*ngIf="!resultSaved && this.env.scanRecordLogging == 'off' && this.qrCodeContent != null && this.qrCodeContent != '' "
(click)="tapHaptic(); saveRecord()" [color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="outline"
shape="round" [@inAnimation]>
<ion-icon class="pe-2" src="assets/icon/history.svg"></ion-icon>
<ion-label>{{ 'SAVE' | translate }}</ion-label>
</ion-button>
<ion-button class="pe-1"
*ngIf="contentType === 'freeText' && env.showOpenFoodFactsButton === 'on' && isValidEan"
(click)="tapHaptic(); searchOpenFoodFacts()" [color]="env.colorTheme === 'light'? 'dark' : 'light'"
fill="outline" shape="round" [@inAnimation]>
<ion-icon class="pe-2" name="fast-food"></ion-icon>
<ion-label>Facts</ion-label>
</ion-button>
<ion-button class="pe-1" *ngIf="contentType === 'url' && env.showBrowseButton === 'on' && isHttp"
(click)="tapHaptic(); browseWebsite()" [color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="outline"
shape="round" [@inAnimation]>
<ion-icon class="pr-2" name="globe"></ion-icon>
<ion-icon class="pe-2" name="globe"></ion-icon>
<ion-label>{{ 'BROWSE' | translate}}</ion-label>
</ion-button>
<ion-button class="pr-1" *ngIf="contentType === 'url' && env.showOpenUrlButton === 'on' && !isHttp"
<ion-button class="pe-1"
*ngIf="(contentType === 'url' || contentType === 'geo') && env.showOpenUrlButton === 'on' && !isHttp"
(click)="tapHaptic(); openLink()" [color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="outline"
shape="round" [@inAnimation]>
<ion-icon class="pr-2" name="open"></ion-icon>
<ion-icon class="pe-2" [name]="contentType === 'geo'? 'map' : 'open'"></ion-icon>
<ion-label>{{ 'OPEN' | translate}}</ion-label>
</ion-button>
<ion-button class="pr-1"
<ion-button class="pe-1"
*ngIf="(contentType === 'contact' || contentType === 'phone' || contentType === 'sms') && env.showAddContactButton === 'on'"
(click)="tapHaptic(); addContact()" [color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="outline"
shape="round" [@inAnimation]>
<ion-icon class="pr-2" name="person-add-sharp"></ion-icon>
<ion-icon class="pe-2" name="person-add-sharp"></ion-icon>
<ion-label>{{ 'ADD' | translate}}</ion-label>
</ion-button>
<ion-button class="pr-1" *ngIf="contentType === 'phone' && env.showCallButton === 'on'"
<ion-button class="pe-1" *ngIf="contentType === 'phone' && env.showCallButton === 'on'"
(click)="tapHaptic(); callPhone()" [color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="outline"
shape="round" [@inAnimation]>
<ion-icon class="pr-2" name="call"></ion-icon>
<ion-icon class="pe-2" name="call"></ion-icon>
<ion-label>{{ 'CALL' | translate}}</ion-label>
</ion-button>
<ion-button class="pr-1" *ngIf="contentType === 'sms' && smsContent && env.showSendMessageButton === 'on'"
<ion-button class="pe-1" *ngIf="contentType === 'sms' && smsContent && env.showSendMessageButton === 'on'"
(click)="tapHaptic(); sendSms()" [color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="outline"
shape="round" [@inAnimation]>
<ion-icon class="pr-2" name="send"></ion-icon>
<ion-icon class="pe-2" name="send"></ion-icon>
<ion-label>{{ 'SEND' | translate}}</ion-label>
</ion-button>
<ion-button class="pr-1"
<ion-button class="pe-1"
*ngIf="(contentType === 'emailW3C' || contentType === 'emailDocomo') && env.showSendEmailButton === 'on'"
(click)="tapHaptic(); sendEmail()" [color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="outline"
shape="round" [@inAnimation]>
<ion-icon class="pr-2" name="mail"></ion-icon>
<ion-icon class="pe-2" name="mail"></ion-icon>
<ion-label>{{ 'SEND' | translate}}</ion-label>
</ion-button>
<ion-button class="pr-1" *ngIf="env.showSearchButton === 'on'" (click)="tapHaptic(); webSearch()"
<ion-button class="pe-1" *ngIf="env.showSearchButton === 'on'" (click)="tapHaptic(); webSearch()"
[color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="outline" shape="round" [@inAnimation]
[disabled]="!qrCodeContent || (qrCodeContent && qrCodeContent.trim().length <= 0)">
<ion-icon class="pr-2" *ngIf="env.searchEngine === 'google'" slot="icon-only" name="logo-google">
<ion-icon class="pe-2" *ngIf="env.searchEngine === 'google'" slot="icon-only" name="logo-google">
</ion-icon>
<ion-icon class="pr-2" *ngIf="env.searchEngine === 'bing'" slot="icon-only"
<ion-icon class="pe-2" *ngIf="env.searchEngine === 'bing'" slot="icon-only"
src="assets/icon/microsoft-bing.svg">
</ion-icon>
<ion-icon class="pr-2" *ngIf="env.searchEngine === 'yahoo'" slot="icon-only" src="assets/icon/yahoo.svg">
<ion-icon class="pe-2" *ngIf="env.searchEngine === 'yahoo'" slot="icon-only" src="assets/icon/yahoo.svg">
</ion-icon>
<ion-icon class="pr-2" *ngIf="env.searchEngine === 'duckduckgo'" slot="icon-only"
<ion-icon class="pe-2" *ngIf="env.searchEngine === 'duckduckgo'" slot="icon-only"
src="assets/icon/duck-duck-go.svg">
</ion-icon>
<ion-icon class="pr-2" *ngIf="env.searchEngine === 'yandex'" slot="icon-only" src="assets/icon/yandex.svg">
<ion-icon class="pe-2" *ngIf="env.searchEngine === 'yandex'" slot="icon-only" src="assets/icon/yandex.svg">
</ion-icon>
<ion-icon class="pe-2" *ngIf="env.searchEngine === 'ecosia'" slot="icon-only" src="assets/icon/ecosia.svg">
</ion-icon>
<ion-icon class="pe-2" *ngIf="env.searchEngine === 'brave'" slot="icon-only"
src="assets/icon/brave-search.svg">
</ion-icon>
<ion-label>{{ 'SEARCH' | translate}}</ion-label>
</ion-button>
<ion-button class="pr-1" *ngIf="env.showCopyButton === 'on'" (click)="tapHaptic(); copyText()"
<ion-button class="pe-1" *ngIf="env.showCopyButton === 'on'" (click)="tapHaptic(); copyText()"
[color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="outline" shape="round" [@inAnimation]
[disabled]="!qrCodeContent || (qrCodeContent && qrCodeContent.trim().length <= 0)">
<ion-icon class="pr-2" slot="icon-only" name="copy"></ion-icon>
<ion-icon class="pe-2" slot="icon-only" name="copy"></ion-icon>
<ion-label>{{ 'COPY' | translate}}</ion-label>
</ion-button>
<ion-button class="pr-1" *ngIf="env.showBase64Button === 'on'" (click)="tapHaptic(); base64()"
<ion-button class="pe-1" *ngIf="env.showBase64Button === 'on'" (click)="tapHaptic(); base64()"
[color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="outline" shape="round" [@inAnimation]
[disabled]="!qrCodeContent || (qrCodeContent && qrCodeContent.trim().length <= 0)">
<ion-icon class="pr-2" slot="icon-only" name="code-working"></ion-icon>
<ion-icon class="pe-2" slot="icon-only" name="code-working"></ion-icon>
<ion-label>{{ 'BASE64' | translate}}</ion-label>
</ion-button>
<ion-button class="pr-1" *ngIf="env.showEnlargeButton === 'on'" (click)="tapHaptic(); enlarge()"
<ion-button class="pe-1" *ngIf="env.showEnlargeButton === 'on'" (click)="tapHaptic(); enlarge()"
[color]="env.colorTheme === 'light'? 'dark' : 'light'" fill="outline" shape="round" [@inAnimation]
[disabled]="!qrCodeContent || (qrCodeContent && qrCodeContent.trim().length <= 0)">
<ion-icon class="pr-2" slot="icon-only" name="qr-code-sharp"></ion-icon>
<ion-icon class="pe-2" slot="icon-only" name="qr-code-sharp"></ion-icon>
<ion-label>{{ 'SHOW' | translate}}</ion-label>
</ion-button>
<ion-button *ngIf="env.showBookmarkButton === 'on'" (click)="tapHaptic(); handleBookmark()"
[color]="!bookmarked? (env.colorTheme === 'light'? 'dark' : 'light') : 'warning'" fill="outline" shape="round"
[@inAnimation] [disabled]="!qrCodeContent || (qrCodeContent && qrCodeContent.trim().length <= 0)">
<ion-icon class="pr-2" slot="icon-only" name="bookmark"></ion-icon>
<ion-icon class="pe-2" slot="icon-only" name="bookmark"></ion-icon>
<ion-label *ngIf="!bookmarked">{{ 'BOOKMARK' | translate}}</ion-label>
<ion-label *ngIf="bookmarked">{{ 'BOOKMARKED' | translate}}</ion-label>
</ion-button>
@ -291,7 +347,7 @@
</ion-footer>
<ng-template #contentBlock let-label="label" let-content="content" let-hint="hint">
<ng-template #contentBlock let-label="label" let-content="content" let-hint="hint" let-showEdit="showEdit">
<ion-row class="ion-padding-horizontal" [@inAnimation]>
<ion-col>
<mat-form-field [class]="ngMatThemeClass" appearance="outline" color="accent" [floatLabel]="'always'"
@ -301,6 +357,10 @@
[cdkAutosizeMaxRows]="20" readonly>
</textarea>
<mat-hint *ngIf="hint && hint != ''" style="opacity: 0.5;">{{ hint }}</mat-hint>
<button mat-button color="primary" (click)="editContent()" *ngIf="showEdit" class="m-0 p-0 mt-3"
style="background-color: transparent !important; color: var(--ion-color-primary) !important;">
{{ 'EDIT' | translate }}
</button>
</mat-form-field>
</ion-col>
</ion-row>

View file

@ -1,19 +1,19 @@
import { Component, OnInit, QueryList, ViewChildren } from '@angular/core';
import { Component, QueryList, ViewChildren } from '@angular/core';
import { Clipboard } from '@capacitor/clipboard';
import { Contacts, ContactType, EmailAddress, NewContact, PhoneNumber } from '@capacitor-community/contacts'
import { ContactInput, Contacts, EmailInput, EmailType, PhoneInput, PhoneType } from '@capacitor-community/contacts';
import { SMS } from '@awesome-cordova-plugins/sms/ngx';
import { Haptics, ImpactStyle } from '@capacitor/haptics';
import { AlertController, LoadingController, ModalController, Platform } from '@ionic/angular';
import { TranslateService } from '@ngx-translate/core';
import { NgxQrcodeElementTypes, NgxQrcodeErrorCorrectionLevels } from '@techiediaries/ngx-qrcode';
import { VCardContact } from 'src/app/models/v-card-contact';
import { EnvService } from 'src/app/services/env.service';
import { EnvService, QrResultContentTypeType } from 'src/app/services/env.service';
import { Toast } from '@capacitor/toast';
import { MatFormField } from '@angular/material/form-field';
import { BarcodeScanner } from '@capacitor-community/barcode-scanner';
import { QrCodePage } from 'src/app/modals/qr-code/qr-code.page';
import { fadeIn } from 'src/app/utils/animations';
import { Router } from '@angular/router';
import { QRCodeElementType } from 'angularx-qrcode';
@Component({
selector: 'app-result',
@ -23,11 +23,11 @@ import { Router } from '@angular/router';
})
export class ResultPage {
contentType: "freeText" | "url" | "contact" | "phone" | "sms" | "emailW3C" | "emailDocomo" | "wifi" = "freeText";
contentType: QrResultContentTypeType = "freeText";
qrCodeContent: string;
qrElementType: NgxQrcodeElementTypes = NgxQrcodeElementTypes.CANVAS;
errorCorrectionLevel: NgxQrcodeErrorCorrectionLevels = NgxQrcodeErrorCorrectionLevels.LOW;
qrElementType: QRCodeElementType = "canvas";
errorCorrectionLevel: 'low' | 'medium' | 'quartile' | 'high' | 'L' | 'M' | 'Q' | 'H' = 'low';
qrMargin: number = 3;
phoneNumber: string;
@ -45,6 +45,9 @@ export class ResultPage {
wifiEncryption: 'NONE' | 'WEP' | 'WPA';
wifiHidden: boolean = false;
latitude: number;
longitude: number;
base64Encoded: boolean = false;
base64EncodedText: string = "";
base64Decoded: boolean = false;
@ -54,6 +57,8 @@ export class ResultPage {
showQrFirst: boolean = false;
resultSaved: boolean = false;
@ViewChildren(MatFormField) formFields: QueryList<MatFormField>;
constructor(
@ -81,18 +86,27 @@ export class ResultPage {
this.showQrFirst = true;
}
}
this.qrCodeContent = this.env.result;
this.qrCodeContent = this.env.resultContent;
this.setContentType();
}
async ionViewDidEnter(): Promise<void> {
if (this.showQrFirst) {
if (this.contentType == 'url' && this.env.autoOpenUrl == 'on' && this.env.recordSource == 'scan') {
setTimeout(() => {
this.presentToast(this.translate.instant("AUTO_OPEN_URL"), "short", "bottom");
if (this.isHttp) {
this.browseWebsite();
} else {
this.openLink();
}
}, 300);
} else if (this.showQrFirst) {
this.showQrFirst = false;
if (this.qrCodeContent && this.qrCodeContent.trim().length > 0) {
await this.enlarge();
}
}
if (this.env.scanRecordLogging == 'on') {
if (this.env.scanRecordLogging == 'on' && this.qrCodeContent != null && this.qrCodeContent != "") {
await this.env.saveScanRecord(this.qrCodeContent);
}
if (this.env.bookmarks.find(x => x.text == this.qrCodeContent)) {
@ -104,6 +118,14 @@ export class ResultPage {
this.reset();
}
async saveRecord() {
if (this.qrCodeContent != null && this.qrCodeContent != "") {
await this.env.saveScanRecord(this.qrCodeContent);
}
this.resultSaved = true;
this.presentToast(this.translate.instant("SAVED"), "short", "bottom");
}
reset() {
this.contentType = "freeText";
delete this.qrCodeContent;
@ -119,12 +141,15 @@ export class ResultPage {
delete this.wifiPassword
delete this.wifiEncryption
delete this.wifiHidden
delete this.latitude
delete this.longitude
this.base64Encoded = false;
this.base64EncodedText = "";
this.base64Decoded = false;
this.base64DecodedText = "";
this.bookmarked = false;
this.showQrFirst = false;
this.resultSaved = false;
delete this.env.recordSource;
delete this.env.detailedRecordSource;
delete this.env.viewResultFrom;
@ -137,6 +162,7 @@ export class ResultPage {
const emailW3CPrefix = "MAILTO:";
const emailDoconoPrefix = "MATMSG:";
const wifiPrefix = "WIFI:";
const geoPrefix = "GEO:";
const content0 = this.qrCodeContent.trim();
const tContent = this.qrCodeContent.trim().toUpperCase();
if (tContent.substr(0, contactPrefix.length) === contactPrefix) {
@ -163,6 +189,10 @@ export class ResultPage {
} else if (tContent.substr(0, wifiPrefix.length) === wifiPrefix) {
this.contentType = "wifi";
this.prepareWifi();
} else if (tContent.substring(0, geoPrefix.length) === geoPrefix) {
this.contentType = "geo";
this.latitude = +tContent.substring(geoPrefix.length, tContent.indexOf(","));
this.longitude = +tContent.substring(tContent.indexOf(",") + 1);
} else if (this.isValidUrl(content0)) {
this.contentType = "url";
} else {
@ -172,16 +202,16 @@ export class ResultPage {
private isValidUrl(text: string): boolean {
let url: URL;
try {
url = new URL(text);
} catch (_) {
return false;
return false;
}
return url.protocol != null && url.protocol.length > 0;
}
get qrColorDark(): string {
return "#222428";
}
@ -190,6 +220,15 @@ export class ResultPage {
return "#ffffff";
}
editContent() {
this.env.editingContent = true;
this.router.navigate(['tabs/generate'], { replaceUrl: true });
}
searchOpenFoodFacts() {
window.open(`https://world.openfoodfacts.org/product/${this.qrCodeContent}`, '_system', 'location=yes');
}
browseWebsite() {
window.open(this.qrCodeContent, '_system', 'location=yes');
}
@ -204,107 +243,140 @@ export class ResultPage {
const tContent = this.qrCodeContent.trim().toUpperCase();
if (tContent.substring(0, urlPrefix1.length) === urlPrefix1 || tContent.substring(0, urlPrefix2.length) === urlPrefix2) {
return true;
}
}
return false;
}
async addContact(): Promise<void> {
let newContact = null;
let contactInput: ContactInput = {};
if (this.contentType === "contact") {
const phoneNumbers = [];
const phoneNumbers: PhoneInput[] = [];
if (this.vCardContact?.defaultPhoneNumber != null) {
const phoneNumber = { number: this.vCardContact?.defaultPhoneNumber } as PhoneNumber;
const phoneNumber: PhoneInput = {
type: PhoneType.Mobile,
label: 'mobile',
number: this.vCardContact?.defaultPhoneNumber,
isPrimary: true,
};
phoneNumbers.push(phoneNumber);
}
if (this.vCardContact?.homePhoneNumber != null) {
const phoneNumber = { number: this.vCardContact?.homePhoneNumber } as PhoneNumber;
const phoneNumber: PhoneInput = {
type: PhoneType.Home,
label: 'home',
number: this.vCardContact?.homePhoneNumber,
};
phoneNumbers.push(phoneNumber);
}
if (this.vCardContact?.workPhoneNumber != null) {
const phoneNumber = { number: this.vCardContact?.homePhoneNumber } as PhoneNumber;
const phoneNumber: PhoneInput = {
type: PhoneType.Work,
label: 'work',
number: this.vCardContact?.workPhoneNumber,
};
phoneNumbers.push(phoneNumber);
}
if (this.vCardContact?.mobilePhoneNumber != null) {
const phoneNumber = { number: this.vCardContact?.mobilePhoneNumber } as PhoneNumber;
const phoneNumber: PhoneInput = {
type: PhoneType.Mobile,
label: 'mobile',
number: this.vCardContact?.mobilePhoneNumber,
};
phoneNumbers.push(phoneNumber);
}
const emails = [];
const emails: EmailInput[] = [];
if (this.vCardContact?.defaultEmail != null) {
const address = { address: this.vCardContact?.defaultEmail } as EmailAddress;
emails.push(address);
const emailInput: EmailInput = {
type: EmailType.Home,
label: 'home',
isPrimary: true,
address: this.vCardContact?.defaultEmail,
};
emails.push(emailInput);
}
if (this.vCardContact?.homeEmail != null) {
const address = { address: this.vCardContact?.homeEmail } as EmailAddress;
emails.push(address);
const emailInput: EmailInput = {
type: EmailType.Home,
label: 'home',
address: this.vCardContact?.homeEmail,
};
emails.push(emailInput);
}
if (this.vCardContact?.workEmail != null) {
const address = { address: this.vCardContact?.workEmail } as EmailAddress;
emails.push(address);
const emailInput: EmailInput = {
type: EmailType.Work,
label: 'work',
address: this.vCardContact?.workEmail,
};
emails.push(emailInput);
}
newContact = {
contactType: ContactType.Person,
givenName: this.vCardContact?.givenName ?? this.vCardContact?.fullName ?? '',
familyName: this.vCardContact?.familyName,
phoneNumbers: phoneNumbers,
emailAddresses: emails
} as NewContact;
contactInput.phones = phoneNumbers;
contactInput.emails = emails;
contactInput.name = {
given: this.vCardContact?.givenName ?? this.vCardContact?.fullName ?? '',
family: this.vCardContact?.familyName,
};
} else if (this.contentType === "sms" || this.contentType === "phone") {
const phoneNumbers = [];
const phoneNumber = { number: this.phoneNumber } as PhoneNumber;
phoneNumbers.push(phoneNumber);
newContact = {
contactType: ContactType.Person,
phoneNumbers: phoneNumbers
} as NewContact;
}
if (newContact != null) {
await Contacts.getPermissions().then(
async permission => {
if (permission.granted) {
await Contacts.saveContact(newContact).then(
_ => {
if (this.isIOS) {
this.presentToast(this.translate.instant('MSG.SAVED_CONTACT'), "short", "bottom");
} else {
this.presentToast(this.translate.instant('MSG.SAVING_CONTACT'), "short", "bottom");
}
}
)
.catch(
err => {
if (this.env.isDebugging) {
this.presentToast("Error when call Contacts.saveContact: " + JSON.stringify(err), "long", "top");
} else {
this.presentToast(this.translate.instant('MSG.FAILED_SAVING_CONTACT'), "short", "bottom");
}
}
)
} else {
const alert = await this.alertController.create({
header: this.translate.instant("PERMISSION_REQUIRED"),
message: this.translate.instant("MSG.CONTACT_PERMISSION"),
buttons: [
{
text: this.translate.instant("SETTING"),
handler: () => {
BarcodeScanner.openAppSettings();
return true;
}
},
{
text: this.translate.instant("CLOSE"),
handler: () => {
return true;
}
}
],
cssClass: ['alert-bg']
});
await alert.present();
}
const phones: PhoneInput[] = [
{
type: PhoneType.Mobile,
label: 'mobile',
number: this.phoneNumber,
isPrimary: true,
}
);
];
contactInput.phones = phones;
}
if (this.platform.is('ios')) {
// TODO: iOS contact handling
// await Contacts.checkPermissions().then(
// async permission => {
// if (permission.contacts == 'granted') {
// await this.saveContact(newContact);
// } else {
// const alert = await this.alertController.create({
// header: this.translate.instant("PERMISSION_REQUIRED"),
// message: this.translate.instant("MSG.CONTACT_PERMISSION"),
// buttons: [
// {
// text: this.translate.instant("SETTING"),
// handler: () => {
// BarcodeScanner.openAppSettings();
// return true;
// }
// },
// {
// text: this.translate.instant("CLOSE"),
// handler: () => {
// return true;
// }
// }
// ],
// cssClass: ['alert-bg']
// });
// await alert.present();
// }
// }
// );
} else { // Android doesn't need to get permission
await this.saveContact(contactInput);
}
}
private async saveContact(contactInput: ContactInput) {
await Contacts.createContact({ contact: contactInput }).then(
_ => {
this.presentToast(this.translate.instant('MSG.SAVED_CONTACT'), "short", "bottom");
}
).catch(
err => {
if (this.env.isDebugging) {
this.presentToast("Error when call Contacts.createContact: " + JSON.stringify(err), "long", "top");
} else {
this.presentToast(this.translate.instant('MSG.FAILED_SAVING_CONTACT'), "short", "bottom");
}
}
);
}
async callPhone(): Promise<void> {
@ -390,6 +462,12 @@ export class ResultPage {
case 'yandex':
searchUrl = this.env.YANDEX_SEARCH_URL;
break;
case 'ecosia':
searchUrl = this.env.ECOSIA_SEARCH_URL;
break;
case 'brave':
searchUrl = this.env.BRAVE_SEARCH_URL;
break;
default:
searchUrl = this.env.GOOGLE_SEARCH_URL;
break;
@ -571,7 +649,7 @@ export class ResultPage {
} else if (!failEncoded && failDecoded) {
await this.presentToast(this.translate.instant('MSG.NOT_BASE64_DE'), "short", "center");
}
setTimeout(() => this.formFields?.forEach(ff => ff.updateOutlineGap()), 100);
// setTimeout(() => this.formFields?.forEach(ff => ff.updateOutlineGap()), 100);
}
generateVCardContact(): void {
@ -606,7 +684,6 @@ export class ResultPage {
lines.forEach(
line => {
const tLine = line.trim();
console.log(tLine);
if (tLine.toUpperCase().substr(0, fullNameId1.length) === fullNameId1) {
this.vCardContact.fullName = tLine.substr(fullNameId1.length);
} else if (tLine.toUpperCase().substr(0, fullNameId2.length) === fullNameId2) {
@ -803,15 +880,6 @@ export class ResultPage {
await alert.present();
}
// async removeBookmark() {
// await this.env.deleteBookmark(this.qrCodeContent);
// if (this.env.bookmarks.find(x => x.text === this.qrCodeContent)) {
// this.bookmarked = true;
// } else {
// this.bookmarked = false;
// }
// }
get contentTypeText(): string {
switch (this.contentType) {
case 'freeText':
@ -822,6 +890,8 @@ export class ResultPage {
return this.translate.instant("EMAIL_W3C_STANDARD");
case 'emailDocomo':
return this.translate.instant("EMAIL_NTT_DOCOMO");
case 'geo':
return this.translate.instant("GEOLOCATION");
case 'phone':
return this.translate.instant("PHONE_NO");
case 'sms':
@ -843,6 +913,8 @@ export class ResultPage {
return "link";
case "contact":
return "contact_phone";
case 'geo':
return "location_on";
case "phone":
return "call";
case "sms":
@ -859,7 +931,7 @@ export class ResultPage {
}
get barcodeFormat(): string {
switch (this.env.resultFormat) {
switch (this.env.resultContentFormat) {
case "UPC_A":
return this.translate.instant("BARCODE_TYPE.UPC");
case "UPC_E":
@ -899,10 +971,36 @@ export class ResultPage {
case "RSS_EXPANDED":
return this.translate.instant("BARCODE_TYPE.RSS");
default:
return this.env.resultFormat;
return this.env.resultContentFormat;
}
}
get isValidEan(): boolean {
if (this.qrCodeContent == null) {
return false;
}
const isValidLength = this.qrCodeContent.length === 18 || this.qrCodeContent.length === 14 || this.qrCodeContent.length === 13 || this.qrCodeContent.length === 8 || this.qrCodeContent.length === 5;
return isValidLength && /^\d+$/.test(this.qrCodeContent) && this.testEanChecksum(this.qrCodeContent);
}
private testEanChecksum(text: string): boolean {
const digits = text.slice(0, -1);
const checkDigit = parseInt(text.slice(-1));
if (isNaN(checkDigit)) {
return false;
}
let sum = 0;
for (let i = digits.length - 1; i >= 0; i--) {
const digit = parseInt(digits.charAt(i));
if (isNaN(digit)) {
return false;
}
sum += (digit * (1 + (2 * (i % 2)))) | 0;
}
sum = (10 - (sum % 10)) % 10;
return sum === checkDigit;
}
get finalContactName(): string {
if (!this.vCardContact) {
return '';
@ -961,7 +1059,7 @@ export class ResultPage {
async tapHaptic() {
if (this.env.vibration === 'on' || this.env.vibration === 'on-haptic') {
await Haptics.impact({ style: ImpactStyle.Medium })
await Haptics.impact({ style: ImpactStyle.Light })
.catch(async err => {
if (this.env.debugMode === 'on') {
await Toast.show({ text: 'Err when Haptics.impact: ' + JSON.stringify(err), position: "top", duration: "long" })

View file

@ -109,7 +109,7 @@ export class ScanPage {
async (result: ScanResult) => {
if (result.hasContent) {
const text = result.content;
if (text === undefined || text === null || (text && text.trim().length <= 0) || text === "") {
if (text == null || text?.trim()?.length <= 0 || text == "") {
this.presentToast(this.translate.instant('MSG.QR_CODE_VALUE_NOT_EMPTY'), "short", "center");
this.scanQr();
return;
@ -137,8 +137,8 @@ export class ScanPage {
}
async processQrCode(scannedData: string, format: string, loading: HTMLIonLoadingElement): Promise<void> {
this.env.result = scannedData;
this.env.resultFormat = format;
this.env.resultContent = scannedData;
this.env.resultContentFormat = format;
this.env.recordSource = "scan";
this.env.detailedRecordSource = "scan-camera";
this.env.viewResultFrom = "/tabs/scan";
@ -205,7 +205,7 @@ export class ScanPage {
async tapHaptic() {
if (this.env.vibration === 'on' || this.env.vibration === 'on-haptic') {
await Haptics.impact({ style: ImpactStyle.Medium })
await Haptics.impact({ style: ImpactStyle.Light })
.catch(async err => {
if (this.env.debugMode === 'on') {
await Toast.show({ text: 'Err when Haptics.impact: ' + JSON.stringify(err), position: "top", duration: "long" })

View file

@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar [color]="env.colorTheme === 'black'? 'black' : 'dark'">
<ion-buttons slot="start">
<ion-back-button text="">
<ion-back-button text="" defaultHref="tabs/setting">
</ion-back-button>
</ion-buttons>
<ion-title>{{ 'AUTO_MAX_BRIGHTNESS' | translate }}</ion-title>
@ -21,29 +21,17 @@
</ion-label>
</ion-item>
<ion-radio-group [(ngModel)]="env.autoMaxBrightness" (ionChange)="saveAutoMaxBrightness()">
<ion-item class="ion-no-padding ripple-parent" detail="false" lines="none">
<ion-label class="ion-padding-start">
<p class="ion-padding pre-line">
<ion-text [color]="env.colorTheme === 'light'? 'dark' : 'light'" style="font-size: large;">
{{ 'TURN_ON' | translate }}
</ion-text>
</p>
</ion-label>
<ion-radio class="ion-margin-end" slot="end" [value]="'on'" [color]="'primary'">
</ion-radio>
</ion-item>
<ion-item class="ion-no-padding ripple-parent" detail="false" lines="none">
<ion-label class="ion-padding-start">
<p class="ion-padding pre-line">
<ion-text [color]="env.colorTheme === 'light'? 'dark' : 'light'" style="font-size: large;">
{{ 'TURN_OFF' | translate }}
</ion-text>
</p>
</ion-label>
<ion-radio class="ion-margin-end" slot="end" [value]="'off'" [color]="'primary'">
</ion-radio>
</ion-item>
</ion-radio-group>
<ion-item class="ion-no-padding ripple-parent" detail="false" lines="none">
<ion-label class="ion-padding-start">
<p class="ion-padding pre-line">
<ion-text [color]="env.colorTheme === 'light'? 'dark' : 'light'" style="font-size: large;">
{{ (env.autoMaxBrightness == 'on'? 'TURNED_ON' : 'TURNED_OFF') | translate }}
</ion-text>
</p>
</ion-label>
<ion-toggle class="ion-padding-end" slot="end" [ngModel]="env.autoMaxBrightness == 'on'? true : false"
(ngModelChange)="onAutoMaxBrightnessChange($event)">
</ion-toggle>
</ion-item>
</ion-content>

View file

@ -1,5 +1,7 @@
import { Component } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { Haptics, ImpactStyle } from '@capacitor/haptics';
import { Preferences } from '@capacitor/preferences';
import { Toast } from '@capacitor/toast';
import { EnvService } from 'src/app/services/env.service';
@Component({
@ -13,8 +15,20 @@ export class SettingAutoBrightnessPage {
public env: EnvService,
) { }
async saveAutoMaxBrightness() {
await this.env.storageSet("auto-max-brightness", this.env.autoMaxBrightness);
async onAutoMaxBrightnessChange(ev: any) {
this.env.autoMaxBrightness = ev ? 'on' : 'off';
await Preferences.set({ key: this.env.KEY_AUTO_MAX_BRIGHTNESS, value: this.env.autoMaxBrightness });
await this.tapHaptic();
}
async tapHaptic() {
if (this.env.vibration === 'on' || this.env.vibration === 'on-haptic') {
await Haptics.impact({ style: ImpactStyle.Light })
.catch(async err => {
if (this.env.debugMode === 'on') {
await Toast.show({ text: 'Err when Haptics.impact: ' + JSON.stringify(err), position: "top", duration: "long" })
}
})
}
}
}

View file

@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar [color]="env.colorTheme === 'black'? 'black' : 'dark'">
<ion-buttons slot="start">
<ion-back-button text="">
<ion-back-button text="" defaultHref="tabs/setting">
</ion-back-button>
</ion-buttons>
<ion-title>{{ 'AUTO_KILL_BACKGROUND' | translate }}</ion-title>

View file

@ -1,4 +1,5 @@
import { Component } from '@angular/core';
import { Preferences } from '@capacitor/preferences';
import { EnvService } from 'src/app/services/env.service';
@Component({
@ -13,6 +14,6 @@ export class SettingAutoExitPage{
) { }
async saveAutoExitAppMin() {
await this.env.storageSet("autoExitAppMin", this.env.autoExitAppMin);
await Preferences.set({ key: this.env.KEY_AUTO_EXIT_MIN, value: JSON.stringify(this.env.autoExitAppMin) });
}
}

Some files were not shown because too many files have changed in this diff Show more