diff --git a/README.md b/README.md index 78ab8c6..6e97046 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,160 @@ -# rogapp - -A new Flutter project. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +# gifunavi + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + +# 更新履歴 + +0. flutter_compass は pub.dev cache で 34 に変更。キャッシュをクリアしたら修正が必要。 + +1. 2024-8-17 + ・新規にプロジェクトを作り直し。 + ・ユーザー登録で13歳以上でないと登録できない様にした。 + ・生年月日の選択カレンダーを導入。 + ・エントリーカテゴリはメンバー構成によって自動修正できる様にした。(修正中) + ・イベントの締め切りフィールドを追加し、この日以降のイベントの修正はできない様にした。 + ・エントリーに参加し、ゴールしたら、イベントの修正はできない様にする。必然的にチーム構成を変更する際には、エントリーが終了していれば、変更ができない様にして、新規追加を促す。 +2. + + +#Server +# API仕様変更案 + +## 1. イベントAPI + +### GET /api/events +- 変更点: レスポンスに `deadline_datetime` フィールドを追加 +- レスポンス例: + ```json + { + "id": 1, + "event_name": "サマーマラソン2024", + "start_datetime": "2024-07-01T09:00:00Z", + "end_datetime": "2024-07-01T17:00:00Z", + "deadline_datetime": "2024-06-15T23:59:59Z" + } + ``` + +### POST /api/events +- 変更点: リクエストボディに `deadline_datetime` フィールドを追加 +- リクエスト例: + ```json + { + "event_name": "ウィンターラン2025", + "start_datetime": "2025-01-15T08:00:00Z", + "end_datetime": "2025-01-15T16:00:00Z", + "deadline_datetime": "2025-01-01T23:59:59Z" + } + ``` + +## 2. エントリーAPI + +### PUT /api/entries/{id} +- 変更点: + 1. エントリー更新前にイベントの締め切り日時をチェック + 2. 締め切りを過ぎている場合、400 Bad Requestを返す +- エラーレスポンス例: + ```json + { + "error": "entry_update_closed", + "message": "エントリーの締め切りが過ぎているため、更新できません。", + "deadline": "2024-06-15T23:59:59Z" + } + ``` + +## 3. チームAPI + +### GET /api/teams/{id}/entries +- 新規エンドポイント: 指定されたチームの全エントリーを取得 +- レスポンス例: + ```json + [ + { + "id": 1, + "team_id": 5, + "event": { + "id": 1, + "event_name": "サマーマラソン2024", + "start_datetime": "2024-07-01T09:00:00Z", + "end_datetime": "2024-07-01T17:00:00Z", + "deadline_datetime": "2024-06-15T23:59:59Z" + }, + "category": { + "id": 3, + "category_name": "一般-5時間" + }, + "date": "2024-07-01T09:00:00Z", + "zekken_number": "A-123" + } + ] + ``` + +### PUT /api/teams/{id} +- 変更点: チーム更新時に関連するエントリーの締め切りをチェック +- エラーレスポンス例: + ```json + { + "error": "team_update_restricted", + "message": "締め切りを過ぎたエントリーが存在するため、チームの更新が制限されています。", + "closed_entries": [1, 2, 3] + } + ``` + +## 4. 共通エラーレスポンス + +- すべてのエンドポイントで、より詳細なエラー情報を提供 +- エラーレスポンス構造: + + { + "error": "error_code", + "message": "人間が読める詳細なエラーメッセージ", + "details": { + ## エラーに関する追加情報(オプション) + } + } + + +## 5. 認証・認可 + +- すべてのエンドポイントで適切な認証・認可チェックを実施 +- 権限不足の場合は403 Forbiddenを返す + +マニュアル編集が必要な部分: +1. flutter_compass プラグインの build.gradle ファイルを直接編集します: +ファイルパス: /Volumes/PortableSSD1TB/main/flutter/.pub-cache/hosted/pub.dev/flutter_compass-0.8.0/android/build.gradle +このファイルを開き、jcenter() を mavenCentral() に置き換えます: + $ ./gradlew clean + $ ./gradlew clean build +2. image_gallery_saver の build.gradle ファイルを編集 + /Volumes/PortableSSD1TB/main/flutter/.pub-cache/git/image_gallery_saver-24fd8207a4491c42ed907060bb5bf40c2430131f/android/build.gradle ファイル + ext.kotlin_version = '1.8.22' #'1.7.10' + $ ./gradlew clean + $ ./gradlew clean build +3. qr_code_scanner の問題解決: + /Volumes/PortableSSD1TB/main/flutter/.pub-cache/hosted/pub.dev/qr_code_scanner-[version]/android/build.gradle + ext.kotlin_version = '1.8.22' //'1.7.10' +4. flutter_keyboard_visibility: + vi /Volumes/PortableSSD1TB/main/flutter/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility-6.0.0/android/build.gradle + minSDK = 21 ,SDKversion=34 にする。 +5. device_info_plus は使用不可 +6. geolocator_android: + vi /Volumes/PortableSSD1TB/main/flutter/.pub-cache/hosted/pub.dev/geolocator_android-4.6.1/android/build.gradle + minSdkVersion 21 + + +テスト用位置情報: +大垣駅: 35.36701369466119, 136.61783662683948 +大垣城: 35.36182698266251, 136.61558088722234 +関ケ原駅:35.36365422752628, 136.47061844402452 \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml index a7acf24..0d29021 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,29 +1,28 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml - -linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. - rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore index 5d99765..55afd91 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -1,13 +1,13 @@ -gradle-wrapper.jar -/.gradle -/captures/ -/gradlew -/gradlew.bat -/local.properties -GeneratedPluginRegistrant.java - -# Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app -key.properties -**/*.keystore -**/*.jks +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/.gradle/config.properties b/android/app/.gradle/config.properties new file mode 100644 index 0000000..5910486 --- /dev/null +++ b/android/app/.gradle/config.properties @@ -0,0 +1,2 @@ +#Thu Aug 22 09:42:29 JST 2024 +java.home=/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home diff --git a/android/app/build.gradle b/android/app/build.gradle index 2795483..34bd3ed 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,87 +1,144 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -def keystoreProperties = new Properties() -def keystorePropertiesFile = rootProject.file('key.properties') -if (keystorePropertiesFile.exists()) { - keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) -} - -android { - - compileSdkVersion 33 - - lintOptions { - checkReleaseBuilds false - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.dvox.gifunavi" - minSdkVersion 23 - targetSdkVersion flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - multiDexEnabled true - } - - signingConfigs { - release { - keyAlias keystoreProperties['keyAlias'] - keyPassword keystoreProperties['keyPassword'] - storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null - storePassword keystoreProperties['storePassword'] - } - } - buildTypes { - release { - signingConfig signingConfigs.release - } - } - -} - -flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} +plugins { + id "com.android.application" + id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id "dev.flutter.flutter-gradle-plugin" +} + +// added ここから +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +def keystoreProperties = new Properties() +def keystorePropertiesFile = rootProject.file('key.properties') +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) +} +// added ここまで + +android { + namespace = "com.dvox.gifunavi" + compileSdk 34 + ndkVersion = '27.0.12077973' + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' //JavaVersion.VERSION_1_8 + } + + // Added : Add this block to force all libraries to use the same Kotlin version + configurations.all { + resolutionStrategy { + //force "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + force "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + //force "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + force 'com.google.android.gms:play-services-location:21.0.1' + } + } + // added ここまで + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.dvox.gifunavi" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = 21 + targetSdk 34// 19 //flutter.minSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + multiDexEnabled true // Added + } + + // added for release + signingConfigs { + if (keystoreProperties['storeFile'] != null) { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile file(keystoreProperties['storeFile']) + storePassword keystoreProperties['storePassword'] + } + } + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.debug + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + minifyEnabled true + shrinkResources true + } + } + + lint { + baseline = file("lint-baseline.xml") + } + + lintOptions { + disable 'MissingPermission' + } + + task wrapper(type: Wrapper){ + gradleVersion = '8.2.1' + } + + task prepareKotlinBuildScriptModel { + + } + + +} + +flutter { + source = "../.." +} + + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" // added + //implementation project(':flutter') // added - 2 + implementation 'com.google.android.gms:play-services-location:21.0.1' //18.0.0' // このバージョンは最新のものにしてください + //implementation 'androidx.core:core-ktx:1.13.1' // added + implementation 'androidx.core:core-ktx:1.10.1' // added + implementation 'androidx.multidex:multidex:2.0.1' // added + implementation 'com.google.android.material:material:1.5.0' + + // Update AndroidX libraries + // implementation "androidx.core:core-ktx:1.12.0" + implementation "androidx.appcompat:appcompat:1.6.1" + implementation "androidx.fragment:fragment-ktx:1.7.0" + implementation "androidx.activity:activity-ktx:1.8.2" + implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.7.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.7.0" +} + +// Force all Kotlin dependencies to use the same version +configurations.all { + resolutionStrategy.eachDependency { DependencyResolveDetails details -> + if (details.requested.group == 'org.jetbrains.kotlin') { + details.useVersion kotlin_version + } + } +} \ No newline at end of file diff --git a/android/app/gradle/wrapper/gradle-wrapper.properties b/android/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..229531a --- /dev/null +++ b/android/app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/android/app/gradlew b/android/app/gradlew new file mode 100755 index 0000000..fcb6fca --- /dev/null +++ b/android/app/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/android/app/gradlew.bat b/android/app/gradlew.bat new file mode 100644 index 0000000..6689b85 --- /dev/null +++ b/android/app/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/app/lint-baseline.xml b/android/app/lint-baseline.xml new file mode 100644 index 0000000..2f84a0f --- /dev/null +++ b/android/app/lint-baseline.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/local.properties b/android/app/local.properties new file mode 100644 index 0000000..f5a3417 --- /dev/null +++ b/android/app/local.properties @@ -0,0 +1,8 @@ +## This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Thu Aug 22 08:15:46 JST 2024 +sdk.dir=/Users/akira/Library/Android/sdk diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index a42e013..399f698 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -1,7 +1,7 @@ - - - - + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e30c45d..7b4b2db 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,42 +1,68 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/dvox/gifunavi/LocationService.kt b/android/app/src/main/kotlin/com/dvox/gifunavi/LocationService.kt new file mode 100644 index 0000000..3594ebb --- /dev/null +++ b/android/app/src/main/kotlin/com/dvox/gifunavi/LocationService.kt @@ -0,0 +1,250 @@ +package com.dvox.gifunavi + + +import android.location.Location +import android.Manifest +import android.annotation.TargetApi +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.Service +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.location.LocationManager +import android.os.Build +import android.os.IBinder +import android.util.Log +import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat +import com.google.android.gms.location.FusedLocationProviderClient +import com.google.android.gms.location.LocationCallback +import com.google.android.gms.location.LocationRequest +import com.google.android.gms.location.LocationResult +import com.google.android.gms.location.LocationServices +import com.google.android.gms.location.Priority +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale +import android.app.Notification + + +data class GpsData( + val id: Int, + val teamName: String, + val eventCode: String, + val lat: Double, + val lon: Double, + val isCheckin: Int, + val createdAt: Long +) + +class GpsDatabaseHelper(private val context: Context) { + fun insertGps(gpsData: GpsData) { + Log.d("LocationService", "Android: insertGps.") + + // ここにデータベースへの挿入処理を実装する + } + + companion object { + fun getInstance(context: Context): GpsDatabaseHelper { + Log.d("LocationService", "Android: GpsDatabaseHelper.") + return GpsDatabaseHelper(context) + } + } +} + +class LocationService : Service() { + private lateinit var fusedLocationClient: FusedLocationProviderClient + private lateinit var gpsDatabaseHelper: GpsDatabaseHelper + + override fun onBind(intent: Intent?): IBinder? { + return null + } + + override fun onCreate() { + super.onCreate() + Log.d("LocationService", "Android: onCreate.") + + fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) + gpsDatabaseHelper = GpsDatabaseHelper.getInstance(applicationContext) + + // 位置情報の権限チェックとGPS有効化の確認を行う + if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && + ContextCompat.checkSelfPermission(this, Manifest.permission.FOREGROUND_SERVICE) == PackageManager.PERMISSION_GRANTED) { + //ContextCompat.checkSelfPermission(this, Manifest.permission.FOREGROUND_SERVICE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + Log.d("LocationService", "Android: onCreate : 位置情報の権限チェックとGPS有効化の確認") + + val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager + if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { + val locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 10000) + .setMinUpdateIntervalMillis(5000) + .build() +/* +val locationRequest = LocationRequest.create().apply { + priority = LocationRequest.PRIORITY_HIGH_ACCURACY + interval = 10000 + fastestInterval = 5000 +} + */ + + fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null) + + // フォアグラウンドサービスの設定 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channel = NotificationChannel(CHANNEL_ID, "Location", NotificationManager.IMPORTANCE_DEFAULT) + val notificationManager = getSystemService(NotificationManager::class.java) + notificationManager?.createNotificationChannel(channel) + } + val notification = NotificationCompat.Builder(this, CHANNEL_ID) + .setContentTitle("Tracking location...") + .setContentText("Location: null") + .setSmallIcon(android.R.drawable.ic_menu_mylocation) + .setOngoing(true) + .build() + + startForeground(NOTIFICATION_ID, notification) + } else { + Log.d("LocationService", "GPS is disabled.") + // GPSが無効の場合の処理を追加する(例: ユーザーにGPSを有効にするように促すなど) + stopSelf() // サービスを停止する + } + } else { + Log.d("LocationService", "Location permission or Foreground service location permission is not granted.") + // 位置情報の権限またはフォアグラウンドサービスの位置情報の権限が許可されていない場合の処理を追加する + stopSelf() // サービスを停止する + } + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + return START_STICKY + } + + @TargetApi(Build.VERSION_CODES.N) + override fun onDestroy() { + super.onDestroy() + Log.d("LocationService", "Android: onDestroy.") + fusedLocationClient.removeLocationUpdates(locationCallback) + stopForeground(STOP_FOREGROUND_REMOVE) + stopSelf() + } + + companion object { + private const val NOTIFICATION_ID = 1 + private const val CHANNEL_ID = "location_service_channel" + } + + private fun createNotification(): Notification { + Log.d("LocationService", "Android: createNotification Notification.") + val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID) + .setContentTitle("Location Service") + .setContentText("Running...") + .setSmallIcon(android.R.drawable.ic_menu_mylocation) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channelName = "Location Service Channel" + val channelDescription = "Channel for Location Service" + val importance = NotificationManager.IMPORTANCE_DEFAULT + val channel = NotificationChannel(CHANNEL_ID, channelName, importance).apply { + description = channelDescription + } + val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } + + return notificationBuilder.build() + } + + private fun createNotificationChannel() { + Log.d("LocationService", "Android: createNotificationChannel.") + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channel = NotificationChannel( + CHANNEL_ID, + "Location Service Channel", + NotificationManager.IMPORTANCE_DEFAULT + ) + val manager = getSystemService(NotificationManager::class.java) + manager.createNotificationChannel(channel) + } + } + + private var lastLocation: Location? = null + + private val locationCallback = object : LocationCallback() { + override fun onLocationResult(locationResult: LocationResult) { + val currentLocation = locationResult.lastLocation + if (currentLocation != null) { + val accuracy = currentLocation.accuracy + if (accuracy <= 30) { + var lastLocation = lastLocation + if (lastLocation == null || currentLocation.distanceTo(lastLocation) >= 10) { + val lat = currentLocation.latitude + val lon = currentLocation.longitude + val currentTime = System.currentTimeMillis() + + // GPSデータをデバッグ用に表示 + val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) + val formattedTime = sdf.format(Date(currentTime)) + //Log.d("LocationService", "Android: {$formattedTime}") + + + // GPSデータをデータベースに保存 + GlobalScope.launch(Dispatchers.IO) { + addGPStoDB(lat, lon, currentTime) + } + + lastLocation = currentLocation + } + } else { + Log.d("LocationService", "Android: GPS accuracy is above 30m. Skipping data saving.") + } + } else { + Log.d("LocationService", "Android: No GPS signal received.") + } + } + } + + + /* + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + Log.d("LocationService", "Android: onStartCommand.") + + // Foregroundサービスを開始 + startForeground(NOTIFICATION_ID, createNotification()) + + return START_STICKY + } + */ + + private suspend fun addGPStoDB(lat: Double, lng: Double, timestamp: Long, isCheckin: Int = 0) { + try { + val context = applicationContext + val preferences = context.getSharedPreferences("RogPreferences", Context.MODE_PRIVATE) + val teamName = preferences.getString("team_name", "") ?: "" + val eventCode = preferences.getString("event_code", "") ?: "" + + if (teamName.isNotEmpty() && eventCode.isNotEmpty()) { + val gpsData = GpsData( + id = 0, + teamName = teamName, + eventCode = eventCode, + lat = lat, + lon = lng, + isCheckin = isCheckin, + createdAt = timestamp + ) + + gpsDatabaseHelper.insertGps(gpsData) + Log.d("LocationService", "Android: addGPStoDB.") + } + } catch (e: Exception) { + Log.e("LocationService", "Error adding GPS data to DB", e) + // エラーメッセージをユーザーに表示するなどの処理を追加 + } + } +} + diff --git a/android/app/src/main/kotlin/com/dvox/gifunavi/MainActivity.kt b/android/app/src/main/kotlin/com/dvox/gifunavi/MainActivity.kt new file mode 100644 index 0000000..62f4dfe --- /dev/null +++ b/android/app/src/main/kotlin/com/dvox/gifunavi/MainActivity.kt @@ -0,0 +1,142 @@ +package com.dvox.gifunavi + +import android.Manifest +import android.app.ActivityManager +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import android.os.Bundle +import android.widget.Toast +import androidx.annotation.NonNull +import androidx.core.content.ContextCompat +import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodChannel +import android.util.Log + +class MainActivity: FlutterActivity() { + private val CHANNEL = "location" + + override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + MethodChannel( + flutterEngine.dartExecutor.binaryMessenger, + CHANNEL + ).setMethodCallHandler { call, result -> + when (call.method) { + "startLocationService" -> { + Log.d("MainActivity", "Android: called startLocationService.") + //val intent = Intent(this, LocationService::class.java) + startLocationService() + result.success(null) + } + + "stopLocationService" -> { + Log.d("MainActivity", "Android: called stopLocationService.") + //val intent = Intent(this, LocationService::class.java) + stopLocationService() + result.success(null) + } + + "isLocationServiceRunning" -> { + Log.d("MainActivity", "Android: called isLocationServiceRunnung.") + val isRunning = isServiceRunning(LocationService::class.java) + result.success(isRunning) + } + + else -> { + result.notImplemented() + } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Log.d("MainActivity", "Android: onCreate.") + + // 位置情報の権限をリクエストする==> main() の前にコールされるので除外 2024-7-19 + /* + if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), PERMISSION_REQUEST_CODE) + } else { + // startLocationService() // アプリ起動時にLocationServiceを開始する ==> main.dartで制御する。 + } + */ + } + + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (requestCode == PERMISSION_REQUEST_CODE) { + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // startLocationService() + + Log.d("MainActivity", "Android: PERMISSION_GRANTED.") + } else { + // 位置情報の権限が拒否された場合の処理 + Toast.makeText(this, "Location permission denied.", Toast.LENGTH_SHORT).show() + } + } + } + + companion object { + private const val PERMISSION_REQUEST_CODE = 1 + } + + private fun startLocationService() { + if (ContextCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED && + ContextCompat.checkSelfPermission( + this, + Manifest.permission.FOREGROUND_SERVICE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + ) { + if (!isServiceRunning(LocationService::class.java)) { + val intent = Intent(this, LocationService::class.java) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Log.d("MainActivity", "startForegroundService") + startForegroundService(intent) + } else { + Log.d("MainActivity", "startService") + startService(intent) + } + } else { + Log.d("MainActivity", "Location service is already running.") + } + } else { + Log.d( + "MainActivity", + "Location permission or Foreground service location permission is not granted." + ) + // 位置情報の権限またはフォアグラウンドサービスの位置情報の権限が許可されていない場合の処理を追加する + // 例: ユーザーに権限の必要性を説明し、許可を求めるダイアログを表示するなど + } + } + + private fun stopLocationService() { + if (isServiceRunning(LocationService::class.java)) { + val intent = Intent(this, LocationService::class.java) + stopService(intent) + } else { + Log.d("MainActivity", "Location service is not running.") + } + } + + private fun isServiceRunning(serviceClass: Class<*>): Boolean { + val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + for (service in manager.getRunningServices(Int.MAX_VALUE)) { + if (serviceClass.name == service.service.className) { + return true + } + } + return false + } +} diff --git a/android/app/src/main/kotlin/com/example/rogapp/MainActivity.kt b/android/app/src/main/kotlin/com/example/rogapp/MainActivity.kt deleted file mode 100644 index 9c7fd0b..0000000 --- a/android/app/src/main/kotlin/com/example/rogapp/MainActivity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.example.rogapp - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { -} diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml index 1cb7aa2..f74085f 100644 --- a/android/app/src/main/res/drawable-v21/launch_background.xml +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -1,12 +1,12 @@ - - - - - - - - + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml index 8403758..304732f 100644 --- a/android/app/src/main/res/drawable/launch_background.xml +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -1,12 +1,12 @@ - - - - - - - - + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index 899012d..db77bb4 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/launcher_icon.png b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png new file mode 100644 index 0000000..899012d Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 94993e4..17987b7 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/launcher_icon.png b/android/app/src/main/res/mipmap-mdpi/launcher_icon.png new file mode 100644 index 0000000..94993e4 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 0415a95..09d4391 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png new file mode 100644 index 0000000..0415a95 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index c94b6f2..d5f1c8d 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png new file mode 100644 index 0000000..c94b6f2 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 39d5d8e..4d6372e 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png new file mode 100644 index 0000000..39d5d8e Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml index 6b2369f..06952be 100644 --- a/android/app/src/main/res/values-night/styles.xml +++ b/android/app/src/main/res/values-night/styles.xml @@ -1,18 +1,18 @@ - - - - - - - + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 3e29b54..cb1ef88 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -1,18 +1,18 @@ - - - - - - - + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml index a42e013..399f698 100644 --- a/android/app/src/profile/AndroidManifest.xml +++ b/android/app/src/profile/AndroidManifest.xml @@ -1,7 +1,7 @@ - - - - + + + + diff --git a/android/build.gradle b/android/build.gradle index f7eb7f6..728b34c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,15 +1,16 @@ +// added buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.8.22' // Use the latest version repositories { google() mavenCentral() } - dependencies { - classpath 'com.android.tools.build:gradle:7.3.0' + classpath 'com.android.tools.build:gradle:8.3.0' //8.3.0' //7.3.0' // Use the version compatible with your Android Studio classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } +// added ここまで allprojects { repositories { @@ -18,14 +19,16 @@ allprojects { } } -rootProject.buildDir = '../build' +rootProject.buildDir = "../build" subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" } subprojects { - project.evaluationDependsOn(':app') + project.evaluationDependsOn(":app") } tasks.register("clean", Delete) { delete rootProject.buildDir } + + diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/Flutter.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/Flutter.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/Flutter.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/MTBBarcodeScanner.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/MTBBarcodeScanner.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/MTBBarcodeScanner.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/Pods-Runner.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/Pods-Runner.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/Pods-Runner.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/Pods-RunnerTests.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/Pods-RunnerTests.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/Pods-RunnerTests.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/camera_avfoundation-camera_avfoundation_privacy.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/camera_avfoundation-camera_avfoundation_privacy.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/camera_avfoundation-camera_avfoundation_privacy.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/camera_avfoundation.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/camera_avfoundation.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/camera_avfoundation.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/connectivity_plus-connectivity_plus_privacy.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/connectivity_plus-connectivity_plus_privacy.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/connectivity_plus-connectivity_plus_privacy.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/connectivity_plus.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/connectivity_plus.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/connectivity_plus.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/device_info_plus-device_info_plus_privacy.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/device_info_plus-device_info_plus_privacy.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/device_info_plus-device_info_plus_privacy.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/device_info_plus.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/device_info_plus.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/device_info_plus.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/flutter_compass.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/flutter_compass.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/flutter_compass.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/flutter_keyboard_visibility.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/flutter_keyboard_visibility.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/flutter_keyboard_visibility.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/geolocator_apple-geolocator_apple_privacy.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/geolocator_apple-geolocator_apple_privacy.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/geolocator_apple-geolocator_apple_privacy.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/geolocator_apple.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/geolocator_apple.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/geolocator_apple.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/image_gallery_saver.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/image_gallery_saver.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/image_gallery_saver.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/image_picker_ios-image_picker_ios_privacy.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/image_picker_ios-image_picker_ios_privacy.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/image_picker_ios-image_picker_ios_privacy.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/image_picker_ios.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/image_picker_ios.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/image_picker_ios.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/package_info_plus-package_info_plus_privacy.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/package_info_plus-package_info_plus_privacy.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/package_info_plus-package_info_plus_privacy.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/package_info_plus.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/package_info_plus.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/package_info_plus.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/path_provider_foundation-path_provider_foundation_privacy.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/path_provider_foundation-path_provider_foundation_privacy.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/path_provider_foundation-path_provider_foundation_privacy.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/path_provider_foundation.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/path_provider_foundation.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/path_provider_foundation.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/permission_handler_apple-permission_handler_apple_privacy.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/permission_handler_apple-permission_handler_apple_privacy.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/permission_handler_apple-permission_handler_apple_privacy.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/permission_handler_apple.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/permission_handler_apple.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/permission_handler_apple.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/pointer_interceptor_ios-pointer_interceptor_ios_privacy.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/pointer_interceptor_ios-pointer_interceptor_ios_privacy.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/pointer_interceptor_ios-pointer_interceptor_ios_privacy.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/pointer_interceptor_ios.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/pointer_interceptor_ios.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/pointer_interceptor_ios.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/qr_code_scanner.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/qr_code_scanner.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/qr_code_scanner.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/shared_preferences_foundation-shared_preferences_foundation_privacy.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/shared_preferences_foundation-shared_preferences_foundation_privacy.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/shared_preferences_foundation-shared_preferences_foundation_privacy.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/shared_preferences_foundation.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/shared_preferences_foundation.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/shared_preferences_foundation.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/sqflite-sqflite_darwin_privacy.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/sqflite-sqflite_darwin_privacy.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/sqflite-sqflite_darwin_privacy.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/sqflite.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/sqflite.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/sqflite.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/url_launcher_ios-url_launcher_ios_privacy.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/url_launcher_ios-url_launcher_ios_privacy.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/url_launcher_ios-url_launcher_ios_privacy.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/url_launcher_ios.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/url_launcher_ios.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/url_launcher_ios.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/webview_flutter_wkwebview-webview_flutter_wkwebview_privacy.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/webview_flutter_wkwebview-webview_flutter_wkwebview_privacy.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/webview_flutter_wkwebview-webview_flutter_wkwebview_privacy.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/webview_flutter_wkwebview.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/webview_flutter_wkwebview.build/dgph new file mode 100644 index 0000000..aff52a3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/webview_flutter_wkwebview.build/dgph differ diff --git a/android/gradle.properties b/android/gradle.properties index 46c1f16..253486f 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,5 @@ -org.gradle.jvmargs=-Xmx1536M -android.useAndroidX=true -android.enableJetifier=true +org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError +#org.gradle.jvmargs=-Xmx6G -XX:MaxMetaspaceSize=3G -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true +kotlin.code.style=official \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 3c472b9..9f4197d 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 33f0745..b40caf8 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,11 +1,39 @@ -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.4.2" apply false + //id "org.jetbrains.kotlin.android" version "1.9.0" apply false + //id "org.jetbrains.kotlin.android" version "1.7.0" apply false + id "org.jetbrains.kotlin.android" version "1.8.22" apply false +} + +include ":app" + +// added +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +// added ここまで diff --git a/assets/customIcon/SVG/gps_signal_high.svg b/assets/customIcon/SVG/gps_signal_high.svg new file mode 100644 index 0000000..f218fb8 --- /dev/null +++ b/assets/customIcon/SVG/gps_signal_high.svg @@ -0,0 +1,262 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/customIcon/SVG/gps_signal_low.svg b/assets/customIcon/SVG/gps_signal_low.svg new file mode 100644 index 0000000..366fade --- /dev/null +++ b/assets/customIcon/SVG/gps_signal_low.svg @@ -0,0 +1,330 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/customIcon/SVG/gps_signal_middle.svg b/assets/customIcon/SVG/gps_signal_middle.svg new file mode 100644 index 0000000..d7ec422 --- /dev/null +++ b/assets/customIcon/SVG/gps_signal_middle.svg @@ -0,0 +1,223 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/customIcon/gps_signal.psd b/assets/customIcon/gps_signal.psd new file mode 100644 index 0000000..f769c9f Binary files /dev/null and b/assets/customIcon/gps_signal.psd differ diff --git a/assets/customIcon/gps_signal_high.png b/assets/customIcon/gps_signal_high.png new file mode 100644 index 0000000..b2ea965 Binary files /dev/null and b/assets/customIcon/gps_signal_high.png differ diff --git a/assets/customIcon/gps_signal_low.png b/assets/customIcon/gps_signal_low.png new file mode 100644 index 0000000..104c76f Binary files /dev/null and b/assets/customIcon/gps_signal_low.png differ diff --git a/assets/customIcon/gps_signal_middle.png b/assets/customIcon/gps_signal_middle.png new file mode 100644 index 0000000..e206146 Binary files /dev/null and b/assets/customIcon/gps_signal_middle.png differ diff --git a/assets/customIcon/icomoon-old/Read Me.txt b/assets/customIcon/icomoon-old/Read Me.txt new file mode 100644 index 0000000..723a49e --- /dev/null +++ b/assets/customIcon/icomoon-old/Read Me.txt @@ -0,0 +1,7 @@ +Open *demo.html* to see a list of all the glyphs in your font along with their codes/ligatures. + +To use the generated font in desktop programs, you can install the TTF font. In order to copy the character associated with each icon, refer to the text box at the bottom right corner of each glyph in demo.html. The character inside this text box may be invisible; but it can still be copied. See this guide for more info: https://icomoon.io/docs/#local-fonts + +You won't need any of the files located under the *demo-files* directory when including the generated font in your own projects. + +You can import *selection.json* back to the IcoMoon app using the *Import Icons* button (or via Main Menu → Manage Projects) to retrieve your icon selection. diff --git a/assets/customIcon/icomoon-old/demo-files/demo.css b/assets/customIcon/icomoon-old/demo-files/demo.css new file mode 100644 index 0000000..39b8991 --- /dev/null +++ b/assets/customIcon/icomoon-old/demo-files/demo.css @@ -0,0 +1,152 @@ +body { + padding: 0; + margin: 0; + font-family: sans-serif; + font-size: 1em; + line-height: 1.5; + color: #555; + background: #fff; +} +h1 { + font-size: 1.5em; + font-weight: normal; +} +small { + font-size: .66666667em; +} +a { + color: #e74c3c; + text-decoration: none; +} +a:hover, a:focus { + box-shadow: 0 1px #e74c3c; +} +.bshadow0, input { + box-shadow: inset 0 -2px #e7e7e7; +} +input:hover { + box-shadow: inset 0 -2px #ccc; +} +input, fieldset { + font-family: sans-serif; + font-size: 1em; + margin: 0; + padding: 0; + border: 0; +} +input { + color: inherit; + line-height: 1.5; + height: 1.5em; + padding: .25em 0; +} +input:focus { + outline: none; + box-shadow: inset 0 -2px #449fdb; +} +.glyph { + font-size: 16px; + width: 15em; + padding-bottom: 1em; + margin-right: 4em; + margin-bottom: 1em; + float: left; + overflow: hidden; +} +.liga { + width: 80%; + width: calc(100% - 2.5em); +} +.talign-right { + text-align: right; +} +.talign-center { + text-align: center; +} +.bgc1 { + background: #f1f1f1; +} +.fgc1 { + color: #999; +} +.fgc0 { + color: #000; +} +p { + margin-top: 1em; + margin-bottom: 1em; +} +.mvm { + margin-top: .75em; + margin-bottom: .75em; +} +.mtn { + margin-top: 0; +} +.mtl, .mal { + margin-top: 1.5em; +} +.mbl, .mal { + margin-bottom: 1.5em; +} +.mal, .mhl { + margin-left: 1.5em; + margin-right: 1.5em; +} +.mhmm { + margin-left: 1em; + margin-right: 1em; +} +.mls { + margin-left: .25em; +} +.ptl { + padding-top: 1.5em; +} +.pbs, .pvs { + padding-bottom: .25em; +} +.pvs, .pts { + padding-top: .25em; +} +.unit { + float: left; +} +.unitRight { + float: right; +} +.size1of2 { + width: 50%; +} +.size1of1 { + width: 100%; +} +.clearfix:before, .clearfix:after { + content: " "; + display: table; +} +.clearfix:after { + clear: both; +} +.hidden-true { + display: none; +} +.textbox0 { + width: 3em; + background: #f1f1f1; + padding: .25em .5em; + line-height: 1.5; + height: 1.5em; +} +#testDrive { + display: block; + padding-top: 24px; + line-height: 1.5; +} +.fs0 { + font-size: 16px; +} +.fs1 { + font-size: 32px; +} + diff --git a/assets/customIcon/icomoon-old/demo-files/demo.js b/assets/customIcon/icomoon-old/demo-files/demo.js new file mode 100644 index 0000000..6f45f1c --- /dev/null +++ b/assets/customIcon/icomoon-old/demo-files/demo.js @@ -0,0 +1,30 @@ +if (!('boxShadow' in document.body.style)) { + document.body.setAttribute('class', 'noBoxShadow'); +} + +document.body.addEventListener("click", function(e) { + var target = e.target; + if (target.tagName === "INPUT" && + target.getAttribute('class').indexOf('liga') === -1) { + target.select(); + } +}); + +(function() { + var fontSize = document.getElementById('fontSize'), + testDrive = document.getElementById('testDrive'), + testText = document.getElementById('testText'); + function updateTest() { + testDrive.innerHTML = testText.value || String.fromCharCode(160); + if (window.icomoonLiga) { + window.icomoonLiga(testDrive); + } + } + function updateSize() { + testDrive.style.fontSize = fontSize.value + 'px'; + } + fontSize.addEventListener('change', updateSize, false); + testText.addEventListener('input', updateTest, false); + testText.addEventListener('change', updateTest, false); + updateSize(); +}()); diff --git a/assets/customIcon/icomoon-old/demo.html b/assets/customIcon/icomoon-old/demo.html new file mode 100644 index 0000000..edf3669 --- /dev/null +++ b/assets/customIcon/icomoon-old/demo.html @@ -0,0 +1,80 @@ + + + + + IcoMoon Demo + + + + + + + Font Name: icomoon (Glyphs: 3) + + + Grid Size: Unknown + + + + icon-gps_signal_middle + + + + + + + liga: + + + + + + + icon-gps_signal_low + + + + + + + liga: + + + + + + + icon-gps_signal_high + + + + + + + liga: + + + + + + + + Font Test Drive + + Font Size: + px + + + + + + + + Generated by IcoMoon + + + + + diff --git a/assets/customIcon/icomoon-old/fonts/icomoon.eot b/assets/customIcon/icomoon-old/fonts/icomoon.eot new file mode 100644 index 0000000..74b0f40 Binary files /dev/null and b/assets/customIcon/icomoon-old/fonts/icomoon.eot differ diff --git a/assets/customIcon/icomoon-old/fonts/icomoon.svg b/assets/customIcon/icomoon-old/fonts/icomoon.svg new file mode 100644 index 0000000..02cb400 --- /dev/null +++ b/assets/customIcon/icomoon-old/fonts/icomoon.svg @@ -0,0 +1,39 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/customIcon/icomoon-old/fonts/icomoon.woff b/assets/customIcon/icomoon-old/fonts/icomoon.woff new file mode 100644 index 0000000..351d4f9 Binary files /dev/null and b/assets/customIcon/icomoon-old/fonts/icomoon.woff differ diff --git a/assets/customIcon/icomoon-old/selection.json b/assets/customIcon/icomoon-old/selection.json new file mode 100644 index 0000000..ceea3de --- /dev/null +++ b/assets/customIcon/icomoon-old/selection.json @@ -0,0 +1 @@ +{"IcoMoonType":"selection","icons":[{"icon":{"paths":["M476.771 35.229c199.666-13.278 355.46 61.878 467.376 225.468 108.605 206.743 88.252 399.332-61.064 577.761-127.939 124.994-280.599 169.618-457.982 133.872-231.133-67.546-353.261-221.776-366.385-462.679 9.428-217.043 114.333-366.572 314.716-448.587 34.697-10.24 69.143-18.852 103.339-25.835z","M504.954 218.422c110.963-8.694 205.688 25.752 284.183 103.339 4.721 17.168-1.541 28.129-18.789 32.881-30.823-26.775-64.489-49.478-100.991-68.11-81.37-34.44-162.788-34.44-244.257 0-35.044 21.006-68.708 43.709-100.991 68.11-11.273-4.217-18.319-12.046-21.138-23.486 29.204-38.979 66.782-67.945 112.734-86.899 29.725-10.931 59.477-19.542 89.248-25.835z","M514.349 302.972c80.403-7.114 148.513 17.937 204.33 75.156 6.247 23.475-2.367 33.652-25.835 30.532-96.19-87.544-193.264-89.11-291.229-4.697-18.12 11.933-30.646 8.019-37.578-11.743 2.069-12.747 8.332-22.925 18.789-30.532 39.091-31.31 82.932-50.882 131.523-58.716z","M260.697 453.284c184.765-0.783 369.523 0 554.275 2.349 26.431 4.496 44.436 19.371 54.018 44.624 4.462 91.056 2.894 181.873-4.697 272.44-9.131 15.191-22.443 25.365-39.927 30.532-191.023 3.133-382.043 3.133-573.064 0-26.722-3.227-43.163-18.098-49.321-44.624-3.132-84.55-3.132-169.101 0-253.651 8.958-30.138 28.53-47.362 58.716-51.67z","M237.211 467.376c197.291-0.783 394.574 0 591.853 2.349 15.294 7.455 25.473 19.198 30.532 35.229 3.133 84.55 3.133 169.101 0 253.651-6.017 17.554-17.76 29.297-35.229 35.229-192.587 3.133-385.174 3.133-577.761 0-17.469-5.933-29.212-17.676-35.229-35.229-3.132-86.115-3.132-172.234 0-258.349 7.828-11.724 16.439-22.683 25.835-32.881z","M345.248 519.046c28.354-0.658 56.537 0.907 84.55 4.697 0 14.092 0 28.183 0 42.275-4.942 0.7-9.639-0.080-14.092-2.349-12.498-31.307-34.419-40.702-65.761-28.183-25.779 24.083-36.739 53.835-32.881 89.248-3.754 35.765 7.207 65.513 32.881 89.248 49.484 5.209 65.924-15.928 49.321-63.413-4.239-5.641-9.72-9.559-16.44-11.743 20.355-3.133 40.71-3.133 61.064 0-5.643 4.242-9.558 9.719-11.743 16.44-1.566 23.486-3.132 46.972-4.697 70.459-1.566 1.564-3.132 3.133-4.697 4.697-6.167-5.040-13.213-8.173-21.138-9.394-77.32 18.3-116.464-13.016-117.431-93.945-1.181-48.762 19.174-84.776 61.064-108.037z","M472.073 523.743c30.471-5.421 61.788-6.205 93.945-2.349 45.267 6.67 61.708 32.505 49.321 77.505-5.482 18.005-17.225 29.748-35.229 35.229-19.977 0.939-39.551 3.288-58.716 7.046-3.133 21.922-3.133 43.839 0 65.761 5.702 7.276 12.748 12.753 21.138 16.44-23.486 3.133-46.972 3.133-70.459 0 5.068-4.491 10.545-8.403 16.44-11.743 3.133-57.931 3.133-115.867 0-173.798-4.88-5.684-10.362-10.381-16.44-14.092z","M692.844 519.046c27.066-1.649 53.68 0.7 79.853 7.046-4.434 12.772-5.998 26.079-4.697 39.927-4.697 0-9.394 0-14.092 0-7.765-33.707-28.118-43.886-61.064-30.532-18.939 21.626-17.375 41.979 4.697 61.064 22.326 11.546 43.464 24.858 63.413 39.927 24.599 57.264 5.81 88.581-56.367 93.945-17.925-3.791-35.934-6.924-54.018-9.394-0.77-12.622 0.014-25.149 2.349-37.578 1.945-2.147 4.293-3.716 7.046-4.697 6.656 15.285 17.615 27.028 32.881 35.229 43.637 7.619 60.078-10.386 49.321-54.018-22.984-18.277-47.254-34.717-72.807-49.321-24.567-39.56-16.741-70.092 23.486-91.596z","M519.046 533.138c58.077-5.369 79.214 19.681 63.413 75.156-17.779 16.271-38.917 22.537-63.413 18.789 0-31.317 0-62.628 0-93.945z"],"attrs":[{"fill":"rgb(255, 254, 11)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(0, 0, 0)"},{"fill":"rgb(5, 5, 5)"},{"fill":"rgb(254, 255, 0)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(3, 3, 3)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(254, 255, 0)"}],"width":1071,"isMulticolor":true,"isMulticolor2":false,"grid":0,"tags":["gps_signal_middle"],"colorPermutations":{"1101010111111415721312215821312221254255012552541112556537133315551":[{"f":9},{"f":0},{"f":1},{"f":4},{"f":8},{"f":2},{"f":3},{"f":2},{"f":8}]}},"attrs":[{"fill":"rgb(255, 254, 11)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(0, 0, 0)"},{"fill":"rgb(5, 5, 5)"},{"fill":"rgb(254, 255, 0)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(3, 3, 3)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(254, 255, 0)"}],"properties":{"order":2,"id":2,"name":"gps_signal_middle","prevSize":32,"code":59648,"codes":[59648,59649,59650,59651,59652,59653,59654,59655,59656]},"setIdx":0,"setId":2,"iconIdx":0},{"icon":{"paths":["M472.073 16.44c-62.2 9.509-121.698 29.081-178.495 58.716-183.411 111.384-267.179 275.005-251.303 490.862 33.091 203.748 146.608 340.752 340.55 411.009 213.466 56.080 392.746 2.062 537.835-162.055 123.012-171.919 138.667-353.547 46.972-544.881-113.96-181.861-279.148-266.412-495.56-253.651zM669.358 340.55c-67.809-41.065-139.837-48.111-216.073-21.138-25.823 10.547-49.309 24.639-70.459 42.275-14.050 10.059-19.531 23.368-16.44 39.927 3.777 6.128 9.257 9.259 16.44 9.394 24.303-17.197 49.355-33.637 75.156-49.321 62.177-27.915 123.242-25.567 183.193 7.046-26.619 28.183-53.234 56.367-79.853 84.55-100.22-0.783-200.428 0-300.624 2.349-30.974 2.785-50.545 19.226-58.716 49.321-3.132 84.55-3.132 169.101 0 253.651 5.542 11.086 11.023 22.044 16.44 32.881-6.072 8.436-13.118 16.267-21.138 23.486-117.899-133.054-150.78-284.93-98.642-455.633 79.099-195.473 224.714-298.813 436.844-310.018 117.098 2.714 218.089 43.424 302.972 122.128-54.901 58.031-111.268 114.398-169.101 169.101zM260.697 805.578c187.896 0.784 375.785 0 563.67-2.349 26.244-6.384 41.12-22.824 44.624-49.321 3.133-84.55 3.133-169.101 0-253.651-9.582-25.252-27.587-40.128-54.018-44.624-67.307-2.348-134.637-3.131-201.982-2.349 18.789-21.921 39.142-42.275 61.064-61.064 9.254 13.214 21.781 18.694 37.578 16.44 14.026-18.322 10.898-33.196-9.394-44.624 53.135-56.268 107.938-111.070 164.404-164.404 57.809 59.207 96.171 129.666 115.083 211.376 34.623 240.701-56.973 413.715-274.789 519.046-142.665 52.083-280.449 41.12-413.358-32.881-24.842-17.004-48.328-35.793-70.459-56.367 11.913-12.706 24.439-24.449 37.578-35.229zM542.532 472.073c-17.225 15.656-34.445 31.317-51.67 46.972-6.449-0.737-12.715 0.047-18.789 2.349 2.555 3.368 5.688 5.717 9.394 7.046-50.104 53.234-101.774 104.904-155.009 155.009-14.272-44.69-11.923-88.534 7.046-131.523 32.897-33.454 60.297-29.541 82.202 11.743 4.453 2.269 9.15 3.049 14.092 2.349 0-14.092 0-28.183 0-42.275-90.584-23.622-139.122 10.822-145.615 103.339 3.54 15.393 5.889 31.049 7.046 46.972 1.991 11.861 7.471 21.255 16.44 28.183-25.052 28.183-51.67 54.803-79.853 79.853-6.123-6.985-11.603-14.815-16.44-23.486-3.132-86.115-3.132-172.234 0-258.349 6.848-11.555 15.459-21.734 25.835-30.532 102.575-3.896 204.349-3.113 305.321 2.349zM598.899 467.376c76.739-0.783 153.459 0 230.165 2.349 9.977 5.27 18.587 12.316 25.835 21.138 7.647 88.942 9.216 178.19 4.697 267.743-6.017 17.554-17.76 29.297-35.229 35.229-184.752 2.349-369.51 3.133-554.275 2.349 21.921-25.050 45.407-48.537 70.459-70.459 20.407 4.105 40.762 2.541 61.064-4.697 7.925 1.221 14.971 4.354 21.138 9.394 6.026-24.224 9.157-49.274 9.394-75.156 2.185-6.722 6.1-12.199 11.743-16.44-4.453-2.269-9.15-3.049-14.092-2.349 18.789-21.922 39.144-42.275 61.064-61.064 0.78 45.432 0 90.84-2.349 136.22-5.895 3.34-11.372 7.253-16.44 11.743 23.486 3.133 46.972 3.133 70.459 0-8.389-3.687-15.435-9.164-21.138-16.44-3.133-21.922-3.133-43.839 0-65.761 19.165-3.758 38.738-6.106 58.716-7.046 33.999-17.168 45.742-44.567 35.229-82.202-15.665-24.379-38.372-35.342-68.11-32.881 15.656-18.789 32.881-36.014 51.67-51.67zM533.138 533.138c42.172-0.892 60.176 19.461 54.018 61.064-1.625 11.029-7.107 19.639-16.44 25.835-16.722 5.81-33.947 8.159-51.67 7.046 0-26.619 0-53.234 0-79.853 3.133-6.261 7.83-10.959 14.092-14.092zM401.615 664.661c7.172 39.809-10.052 57.034-51.67 51.67 15.657-18.789 32.881-36.014 51.67-51.67z","M669.358 340.55c-7.83 10.96-17.225 20.355-28.183 28.183-59.951-32.613-121.015-34.961-183.193-7.046-25.801 15.684-50.853 32.124-75.156 49.321-7.184-0.135-12.664-3.267-16.44-9.394-3.090-16.559 2.39-29.868 16.44-39.927 21.15-17.636 44.636-31.728 70.459-42.275 76.236-26.973 148.264-19.928 216.073 21.138z","M702.239 364.037c20.292 11.427 23.42 26.302 9.394 44.624-15.797 2.254-28.324-3.227-37.578-16.44 7.83-10.96 17.225-20.355 28.183-28.183z","M561.321 453.284c-4.697 7.829-10.959 14.092-18.789 18.789-100.973-5.462-202.746-6.245-305.321-2.349-10.376 8.798-18.987 18.977-25.835 30.532-3.132 86.115-3.132 172.234 0 258.349 4.837 8.671 10.317 16.501 16.44 23.486-1.566 4.697-4.697 7.83-9.394 9.394-5.418-10.837-10.898-21.795-16.44-32.881-3.132-84.55-3.132-169.101 0-253.651 8.17-30.095 27.742-46.537 58.716-49.321 100.196-2.349 200.404-3.131 300.624-2.349z","M612.991 453.284c67.344-0.783 134.675 0 201.982 2.349 26.431 4.496 44.436 19.371 54.018 44.624 3.133 84.55 3.133 169.101 0 253.651-3.504 26.497-18.38 42.938-44.624 49.321-187.885 2.349-375.773 3.133-563.67 2.349 1.566-4.697 4.697-7.83 9.394-9.394 184.765 0.784 369.523 0 554.275-2.349 17.469-5.933 29.212-17.676 35.229-35.229 4.519-89.553 2.95-178.801-4.697-267.743-7.248-8.821-15.858-15.867-25.835-21.138-76.706-2.348-153.426-3.131-230.165-2.349 3.133-6.263 7.83-10.96 14.092-14.092z","M326.459 683.45c-4.697 7.83-10.96 14.092-18.789 18.789-8.969-6.928-14.45-16.323-16.44-28.183-1.157-15.924-3.506-31.58-7.046-46.972 6.493-92.517 55.031-126.962 145.615-103.339 0 14.092 0 28.183 0 42.275-4.942 0.7-9.639-0.080-14.092-2.349-21.905-41.284-49.305-45.197-82.202-11.743-18.969 42.989-21.318 86.833-7.046 131.523z","M490.862 519.046c-1.564 4.697-4.697 7.83-9.394 9.394-3.706-1.329-6.839-3.678-9.394-7.046 6.074-2.302 12.34-3.086 18.789-2.349z","M547.229 519.046c29.738-2.461 52.445 8.502 68.11 32.881 10.512 37.634-1.231 65.033-35.229 82.202-19.977 0.939-39.551 3.288-58.716 7.046-3.133 21.922-3.133 43.839 0 65.761 5.702 7.276 12.748 12.753 21.138 16.44-23.486 3.133-46.972 3.133-70.459 0 5.068-4.491 10.545-8.403 16.44-11.743 2.349-45.38 3.128-90.788 2.349-136.22 7.83-10.959 17.225-20.353 28.183-28.183 0 26.619 0 53.234 0 79.853 17.723 1.113 34.948-1.235 51.67-7.046 9.333-6.196 14.815-14.806 16.44-25.835 6.158-41.604-11.846-61.957-54.018-61.064 3.133-6.261 7.83-10.959 14.092-14.092z","M692.844 519.046c27.066-1.649 53.68 0.7 79.853 7.046-4.434 12.772-5.998 26.079-4.697 39.927-4.697 0-9.394 0-14.092 0-7.765-33.707-28.118-43.886-61.064-30.532-18.939 21.626-17.375 41.979 4.697 61.064 22.326 11.546 43.464 24.858 63.413 39.927 24.599 57.264 5.81 88.581-56.367 93.945-17.925-3.791-35.934-6.924-54.018-9.394-0.77-12.622 0.014-25.149 2.349-37.578 1.945-2.147 4.293-3.716 7.046-4.697 6.656 15.285 17.615 27.028 32.881 35.229 43.637 7.619 60.078-10.386 49.321-54.018-22.984-18.277-47.254-34.717-72.807-49.321-24.567-39.56-16.741-70.092 23.486-91.596z","M429.798 636.477c4.942-0.7 9.639 0.080 14.092 2.349-5.643 4.242-9.558 9.719-11.743 16.44-0.237 25.882-3.369 50.932-9.394 75.156-6.167-5.040-13.213-8.173-21.138-9.394-20.302 7.238-40.657 8.803-61.064 4.697 1.566-4.697 4.697-7.83 9.394-9.394 41.618 5.364 58.841-11.861 51.67-51.67 7.829-10.959 17.223-20.353 28.183-28.183z"],"attrs":[{"fill":"rgb(255, 65, 37)"},{"fill":"rgb(0, 0, 0)"},{"fill":"rgb(0, 0, 0)"},{"fill":"rgb(10, 10, 10)"},{"fill":"rgb(3, 3, 3)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(5, 5, 5)"},{"fill":"rgb(3, 3, 3)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(2, 2, 2)"}],"width":1071,"isMulticolor":true,"isMulticolor2":false,"grid":0,"tags":["gps_signal_low"],"colorPermutations":{"1101010111111415721312215821312221254255012552541112556537133315551":[{"f":10},{"f":1},{"f":1},{"f":5},{"f":3},{"f":2},{"f":4},{"f":3},{"f":2},{"f":2}]}},"attrs":[{"fill":"rgb(255, 65, 37)"},{"fill":"rgb(0, 0, 0)"},{"fill":"rgb(0, 0, 0)"},{"fill":"rgb(10, 10, 10)"},{"fill":"rgb(3, 3, 3)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(5, 5, 5)"},{"fill":"rgb(3, 3, 3)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(2, 2, 2)"}],"properties":{"order":3,"id":1,"name":"gps_signal_low","prevSize":32,"code":59657,"codes":[59657,59658,59659,59660,59661,59662,59663,59664,59665,59666]},"setIdx":0,"setId":2,"iconIdx":1},{"icon":{"paths":["M476.771 30.532c203.992-13.476 361.35 64.811 472.073 234.862 84.55 161.272 84.55 322.546 0 483.817-130.461 195.927-311.305 268.734-542.532 218.422-195.946-64.418-311.029-198.29-345.248-401.615-15.798-232.312 80.496-399.065 288.881-500.257 41.888-15.544 84.163-27.287 126.826-35.229z","M519.046 119.78c131.297-2.777 241.683 42.629 331.156 136.22 3.119 23.469-7.055 32.081-30.532 25.835-147.423-132.183-308.694-150.972-483.817-56.367-22.969 13.877-43.324 31.1-61.064 51.67-30.235 15.35-41.978 6.738-35.229-25.835 77.581-77.549 170.743-121.39 279.486-131.523z","M504.954 218.422c110.963-8.694 205.688 25.752 284.183 103.339 4.721 17.168-1.541 28.129-18.789 32.881-30.823-26.775-64.489-49.478-100.991-68.11-81.37-34.44-162.788-34.44-244.257 0-35.044 21.006-68.708 43.709-100.991 68.11-11.273-4.217-18.319-12.046-21.138-23.486 29.204-38.979 66.782-67.945 112.734-86.899 29.725-10.931 59.477-19.542 89.248-25.835z","M514.349 302.972c80.403-7.114 148.513 17.937 204.33 75.156 6.247 23.475-2.367 33.652-25.835 30.532-96.19-87.544-193.264-89.11-291.229-4.697-18.12 11.933-30.646 8.019-37.578-11.743 2.069-12.747 8.332-22.925 18.789-30.532 39.091-31.31 82.932-50.882 131.523-58.716z","M260.697 453.284c184.765-0.783 369.523 0 554.275 2.349 26.431 4.496 44.436 19.371 54.018 44.624 4.462 91.056 2.894 181.873-4.697 272.44-9.131 15.191-22.443 25.365-39.927 30.532-191.023 3.133-382.043 3.133-573.064 0-26.722-3.227-43.163-18.098-49.321-44.624-3.132-84.55-3.132-169.101 0-253.651 8.958-30.138 28.53-47.362 58.716-51.67z","M237.211 467.376c197.291-0.783 394.574 0 591.853 2.349 15.294 7.455 25.473 19.198 30.532 35.229 3.133 84.55 3.133 169.101 0 253.651-6.017 17.554-17.76 29.297-35.229 35.229-192.587 3.133-385.174 3.133-577.761 0-17.469-5.933-29.212-17.676-35.229-35.229-3.132-86.115-3.132-172.234 0-258.349 7.828-11.724 16.439-22.683 25.835-32.881z","M345.248 519.046c28.354-0.658 56.537 0.907 84.55 4.697 0 14.092 0 28.183 0 42.275-4.942 0.7-9.639-0.080-14.092-2.349-12.498-31.307-34.419-40.702-65.761-28.183-25.779 24.083-36.739 53.835-32.881 89.248-3.754 35.765 7.207 65.513 32.881 89.248 49.484 5.209 65.924-15.928 49.321-63.413-4.239-5.641-9.72-9.559-16.44-11.743 20.355-3.133 40.71-3.133 61.064 0-5.643 4.242-9.558 9.719-11.743 16.44-1.566 23.486-3.132 46.972-4.697 70.459-1.566 1.564-3.132 3.133-4.697 4.697-6.167-5.040-13.213-8.173-21.138-9.394-77.32 18.3-116.464-13.016-117.431-93.945-1.181-48.762 19.174-84.776 61.064-108.037z","M472.073 523.743c30.471-5.421 61.788-6.205 93.945-2.349 45.267 6.67 61.708 32.505 49.321 77.505-5.482 18.005-17.225 29.748-35.229 35.229-19.977 0.939-39.551 3.288-58.716 7.046-3.133 21.922-3.133 43.839 0 65.761 5.702 7.276 12.748 12.753 21.138 16.44-23.486 3.133-46.972 3.133-70.459 0 5.068-4.491 10.545-8.403 16.44-11.743 3.133-57.931 3.133-115.867 0-173.798-4.88-5.684-10.362-10.381-16.44-14.092z","M692.844 519.046c27.066-1.649 53.68 0.7 79.853 7.046-4.434 12.772-5.998 26.079-4.697 39.927-4.697 0-9.394 0-14.092 0-7.765-33.707-28.118-43.886-61.064-30.532-18.939 21.626-17.375 41.979 4.697 61.064 22.326 11.546 43.464 24.858 63.413 39.927 24.599 57.264 5.81 88.581-56.367 93.945-17.925-3.791-35.934-6.924-54.018-9.394-0.77-12.622 0.014-25.149 2.349-37.578 1.945-2.147 4.293-3.716 7.046-4.697 6.656 15.285 17.615 27.028 32.881 35.229 43.637 7.619 60.078-10.386 49.321-54.018-22.984-18.277-47.254-34.717-72.807-49.321-24.567-39.56-16.741-70.092 23.486-91.596z","M519.046 533.138c58.077-5.369 79.214 19.681 63.413 75.156-17.779 16.271-38.917 22.537-63.413 18.789 0-31.317 0-62.628 0-93.945z"],"attrs":[{"fill":"rgb(22, 158, 213)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(0, 0, 0)"},{"fill":"rgb(5, 5, 5)"},{"fill":"rgb(14, 157, 213)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(3, 3, 3)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(14, 157, 213)"}],"width":1071,"isMulticolor":true,"isMulticolor2":false,"grid":0,"tags":["gps_signal_high"],"colorPermutations":{"1101010111111415721312215821312221254255012552541112556537133315551":[{"f":7},{"f":0},{"f":0},{"f":1},{"f":4},{"f":6},{"f":2},{"f":3},{"f":2},{"f":6}]}},"attrs":[{"fill":"rgb(22, 158, 213)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(0, 0, 0)"},{"fill":"rgb(5, 5, 5)"},{"fill":"rgb(14, 157, 213)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(3, 3, 3)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(14, 157, 213)"}],"properties":{"order":4,"id":0,"name":"gps_signal_high","prevSize":32,"code":59667,"codes":[59667,59668,59669,59670,59671,59672,59673,59674,59675,59676]},"setIdx":0,"setId":2,"iconIdx":2}],"height":1024,"metadata":{"name":"icomoon"},"preferences":{"showGlyphs":true,"showQuickUse":true,"showQuickUse2":true,"showSVGs":true,"fontPref":{"prefix":"icon-","metadata":{"fontFamily":"icomoon"},"metrics":{"emSize":1024,"baseline":6.25,"whitespace":50},"embed":false},"imagePref":{"prefix":"icon-","png":true,"useClassSelector":true,"color":0,"bgColor":16777215,"classSelector":".icon"},"historySize":50,"showCodes":false,"gridSize":16,"showLiga":true}} \ No newline at end of file diff --git a/assets/customIcon/icomoon-old/style.css b/assets/customIcon/icomoon-old/style.css new file mode 100644 index 0000000..20b696c --- /dev/null +++ b/assets/customIcon/icomoon-old/style.css @@ -0,0 +1,169 @@ +@font-face { + font-family: 'icomoon'; + src: url('fonts/icomoon.eot?tueyzw'); + src: url('fonts/icomoon.eot?tueyzw#iefix') format('embedded-opentype'), + url('fonts/icomoon.ttf?tueyzw') format('truetype'), + url('fonts/icomoon.woff?tueyzw') format('woff'), + url('fonts/icomoon.svg?tueyzw#icomoon') format('svg'); + font-weight: normal; + font-style: normal; + font-display: block; +} + +[class^="icon-"], [class*=" icon-"] { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: 'icomoon' !important; + speak: never; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-gps_signal_middle .path1:before { + content: "\e900"; + color: rgb(255, 254, 11); +} +.icon-gps_signal_middle .path2:before { + content: "\e901"; + margin-left: -1.0458984375em; + color: rgb(1, 1, 1); +} +.icon-gps_signal_middle .path3:before { + content: "\e902"; + margin-left: -1.0458984375em; + color: rgb(0, 0, 0); +} +.icon-gps_signal_middle .path4:before { + content: "\e903"; + margin-left: -1.0458984375em; + color: rgb(5, 5, 5); +} +.icon-gps_signal_middle .path5:before { + content: "\e904"; + margin-left: -1.0458984375em; + color: rgb(254, 255, 0); +} +.icon-gps_signal_middle .path6:before { + content: "\e905"; + margin-left: -1.0458984375em; + color: rgb(2, 2, 2); +} +.icon-gps_signal_middle .path7:before { + content: "\e906"; + margin-left: -1.0458984375em; + color: rgb(3, 3, 3); +} +.icon-gps_signal_middle .path8:before { + content: "\e907"; + margin-left: -1.0458984375em; + color: rgb(2, 2, 2); +} +.icon-gps_signal_middle .path9:before { + content: "\e908"; + margin-left: -1.0458984375em; + color: rgb(254, 255, 0); +} +.icon-gps_signal_low .path1:before { + content: "\e909"; + color: rgb(255, 65, 37); +} +.icon-gps_signal_low .path2:before { + content: "\e90a"; + margin-left: -1.0458984375em; + color: rgb(0, 0, 0); +} +.icon-gps_signal_low .path3:before { + content: "\e90b"; + margin-left: -1.0458984375em; + color: rgb(0, 0, 0); +} +.icon-gps_signal_low .path4:before { + content: "\e90c"; + margin-left: -1.0458984375em; + color: rgb(10, 10, 10); +} +.icon-gps_signal_low .path5:before { + content: "\e90d"; + margin-left: -1.0458984375em; + color: rgb(3, 3, 3); +} +.icon-gps_signal_low .path6:before { + content: "\e90e"; + margin-left: -1.0458984375em; + color: rgb(2, 2, 2); +} +.icon-gps_signal_low .path7:before { + content: "\e90f"; + margin-left: -1.0458984375em; + color: rgb(5, 5, 5); +} +.icon-gps_signal_low .path8:before { + content: "\e910"; + margin-left: -1.0458984375em; + color: rgb(3, 3, 3); +} +.icon-gps_signal_low .path9:before { + content: "\e911"; + margin-left: -1.0458984375em; + color: rgb(2, 2, 2); +} +.icon-gps_signal_low .path10:before { + content: "\e912"; + margin-left: -1.0458984375em; + color: rgb(2, 2, 2); +} +.icon-gps_signal_high .path1:before { + content: "\e913"; + color: rgb(22, 158, 213); +} +.icon-gps_signal_high .path2:before { + content: "\e914"; + margin-left: -1.0458984375em; + color: rgb(1, 1, 1); +} +.icon-gps_signal_high .path3:before { + content: "\e915"; + margin-left: -1.0458984375em; + color: rgb(1, 1, 1); +} +.icon-gps_signal_high .path4:before { + content: "\e916"; + margin-left: -1.0458984375em; + color: rgb(0, 0, 0); +} +.icon-gps_signal_high .path5:before { + content: "\e917"; + margin-left: -1.0458984375em; + color: rgb(5, 5, 5); +} +.icon-gps_signal_high .path6:before { + content: "\e918"; + margin-left: -1.0458984375em; + color: rgb(14, 157, 213); +} +.icon-gps_signal_high .path7:before { + content: "\e919"; + margin-left: -1.0458984375em; + color: rgb(2, 2, 2); +} +.icon-gps_signal_high .path8:before { + content: "\e91a"; + margin-left: -1.0458984375em; + color: rgb(3, 3, 3); +} +.icon-gps_signal_high .path9:before { + content: "\e91b"; + margin-left: -1.0458984375em; + color: rgb(2, 2, 2); +} +.icon-gps_signal_high .path10:before { + content: "\e91c"; + margin-left: -1.0458984375em; + color: rgb(14, 157, 213); +} diff --git a/assets/customIcon/icomoon.zip b/assets/customIcon/icomoon.zip new file mode 100644 index 0000000..f09032b Binary files /dev/null and b/assets/customIcon/icomoon.zip differ diff --git a/assets/customIcon/icomoon/Read Me.txt b/assets/customIcon/icomoon/Read Me.txt new file mode 100644 index 0000000..723a49e --- /dev/null +++ b/assets/customIcon/icomoon/Read Me.txt @@ -0,0 +1,7 @@ +Open *demo.html* to see a list of all the glyphs in your font along with their codes/ligatures. + +To use the generated font in desktop programs, you can install the TTF font. In order to copy the character associated with each icon, refer to the text box at the bottom right corner of each glyph in demo.html. The character inside this text box may be invisible; but it can still be copied. See this guide for more info: https://icomoon.io/docs/#local-fonts + +You won't need any of the files located under the *demo-files* directory when including the generated font in your own projects. + +You can import *selection.json* back to the IcoMoon app using the *Import Icons* button (or via Main Menu → Manage Projects) to retrieve your icon selection. diff --git a/assets/customIcon/icomoon/demo-files/demo.css b/assets/customIcon/icomoon/demo-files/demo.css new file mode 100644 index 0000000..39b8991 --- /dev/null +++ b/assets/customIcon/icomoon/demo-files/demo.css @@ -0,0 +1,152 @@ +body { + padding: 0; + margin: 0; + font-family: sans-serif; + font-size: 1em; + line-height: 1.5; + color: #555; + background: #fff; +} +h1 { + font-size: 1.5em; + font-weight: normal; +} +small { + font-size: .66666667em; +} +a { + color: #e74c3c; + text-decoration: none; +} +a:hover, a:focus { + box-shadow: 0 1px #e74c3c; +} +.bshadow0, input { + box-shadow: inset 0 -2px #e7e7e7; +} +input:hover { + box-shadow: inset 0 -2px #ccc; +} +input, fieldset { + font-family: sans-serif; + font-size: 1em; + margin: 0; + padding: 0; + border: 0; +} +input { + color: inherit; + line-height: 1.5; + height: 1.5em; + padding: .25em 0; +} +input:focus { + outline: none; + box-shadow: inset 0 -2px #449fdb; +} +.glyph { + font-size: 16px; + width: 15em; + padding-bottom: 1em; + margin-right: 4em; + margin-bottom: 1em; + float: left; + overflow: hidden; +} +.liga { + width: 80%; + width: calc(100% - 2.5em); +} +.talign-right { + text-align: right; +} +.talign-center { + text-align: center; +} +.bgc1 { + background: #f1f1f1; +} +.fgc1 { + color: #999; +} +.fgc0 { + color: #000; +} +p { + margin-top: 1em; + margin-bottom: 1em; +} +.mvm { + margin-top: .75em; + margin-bottom: .75em; +} +.mtn { + margin-top: 0; +} +.mtl, .mal { + margin-top: 1.5em; +} +.mbl, .mal { + margin-bottom: 1.5em; +} +.mal, .mhl { + margin-left: 1.5em; + margin-right: 1.5em; +} +.mhmm { + margin-left: 1em; + margin-right: 1em; +} +.mls { + margin-left: .25em; +} +.ptl { + padding-top: 1.5em; +} +.pbs, .pvs { + padding-bottom: .25em; +} +.pvs, .pts { + padding-top: .25em; +} +.unit { + float: left; +} +.unitRight { + float: right; +} +.size1of2 { + width: 50%; +} +.size1of1 { + width: 100%; +} +.clearfix:before, .clearfix:after { + content: " "; + display: table; +} +.clearfix:after { + clear: both; +} +.hidden-true { + display: none; +} +.textbox0 { + width: 3em; + background: #f1f1f1; + padding: .25em .5em; + line-height: 1.5; + height: 1.5em; +} +#testDrive { + display: block; + padding-top: 24px; + line-height: 1.5; +} +.fs0 { + font-size: 16px; +} +.fs1 { + font-size: 32px; +} + diff --git a/assets/customIcon/icomoon/demo-files/demo.js b/assets/customIcon/icomoon/demo-files/demo.js new file mode 100644 index 0000000..6f45f1c --- /dev/null +++ b/assets/customIcon/icomoon/demo-files/demo.js @@ -0,0 +1,30 @@ +if (!('boxShadow' in document.body.style)) { + document.body.setAttribute('class', 'noBoxShadow'); +} + +document.body.addEventListener("click", function(e) { + var target = e.target; + if (target.tagName === "INPUT" && + target.getAttribute('class').indexOf('liga') === -1) { + target.select(); + } +}); + +(function() { + var fontSize = document.getElementById('fontSize'), + testDrive = document.getElementById('testDrive'), + testText = document.getElementById('testText'); + function updateTest() { + testDrive.innerHTML = testText.value || String.fromCharCode(160); + if (window.icomoonLiga) { + window.icomoonLiga(testDrive); + } + } + function updateSize() { + testDrive.style.fontSize = fontSize.value + 'px'; + } + fontSize.addEventListener('change', updateSize, false); + testText.addEventListener('input', updateTest, false); + testText.addEventListener('change', updateTest, false); + updateSize(); +}()); diff --git a/assets/customIcon/icomoon/demo.html b/assets/customIcon/icomoon/demo.html new file mode 100644 index 0000000..81d22fa --- /dev/null +++ b/assets/customIcon/icomoon/demo.html @@ -0,0 +1,80 @@ + + + + + IcoMoon Demo + + + + + + + Font Name: icomoon (Glyphs: 3) + + + Grid Size: Unknown + + + + icon-gps_signal_low + + + + + + + liga: + + + + + + + icon-gps_signal_middle + + + + + + + liga: + + + + + + + icon-gps_signal_high + + + + + + + liga: + + + + + + + + Font Test Drive + + Font Size: + px + + + + + + + + Generated by IcoMoon + + + + + diff --git a/assets/customIcon/icomoon/fonts/icomoon.eot b/assets/customIcon/icomoon/fonts/icomoon.eot new file mode 100644 index 0000000..ef25f06 Binary files /dev/null and b/assets/customIcon/icomoon/fonts/icomoon.eot differ diff --git a/assets/customIcon/icomoon/fonts/icomoon.svg b/assets/customIcon/icomoon/fonts/icomoon.svg new file mode 100644 index 0000000..aeaaed4 --- /dev/null +++ b/assets/customIcon/icomoon/fonts/icomoon.svg @@ -0,0 +1,50 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/customIcon/icomoon/fonts/icomoon.ttf b/assets/customIcon/icomoon/fonts/icomoon.ttf new file mode 100644 index 0000000..f2e8bb4 Binary files /dev/null and b/assets/customIcon/icomoon/fonts/icomoon.ttf differ diff --git a/assets/customIcon/icomoon/fonts/icomoon.woff b/assets/customIcon/icomoon/fonts/icomoon.woff new file mode 100644 index 0000000..4ed8b4e Binary files /dev/null and b/assets/customIcon/icomoon/fonts/icomoon.woff differ diff --git a/assets/customIcon/icomoon/selection.json b/assets/customIcon/icomoon/selection.json new file mode 100644 index 0000000..de89dcd --- /dev/null +++ b/assets/customIcon/icomoon/selection.json @@ -0,0 +1 @@ +{"IcoMoonType":"selection","icons":[{"icon":{"paths":["M4.697 371.083c0-123.694 0-245.040 0-366.385 356.991-0 713.982-0 1070.972-0 0 341.333 0 682.667 0 1024-356.991 0-713.982 0-1070.972 0-0-218.422-0-436.844-0-657.615zM54.676 595.488c8.121 29.927 13.467 60.954 24.814 89.603 73.114 184.603 210.166 292.309 405.758 315.852 266.402 32.066 503.041-151.136 541.779-416.589 26.568-182.062-36.314-334.611-177.010-450.896-110.474-91.306-241.173-124.942-383.508-103.957-127.29 18.767-232.431 79.646-312.212 180.371-88.664 111.94-121.433 240.139-99.62 385.616z","M54.001 593.523c-21.138-143.512 11.63-271.711 100.295-383.651 79.781-100.726 184.922-161.605 312.212-180.371 142.335-20.985 273.035 12.651 383.508 103.957 140.696 116.285 203.578 268.833 177.010 450.896-38.737 265.453-275.376 448.655-541.779 416.589-195.592-23.543-332.644-131.249-405.758-315.852-11.347-28.649-16.693-59.676-25.488-91.568zM493.216 582.955c0 0-1.199 1.237-4.108 3.501-15.138 15.638-30.275 31.276-47.497 49.623-6.517 6.27-13.034 12.539-22.199 19.893-20.165 20.664-40.33 41.329-62.409 64.715-4.767 4.63-9.535 9.261-17.027 14.967-17.098 17.578-34.196 35.156-53.412 55.506-3.397 3.166-6.794 6.333-12.766 10.681-15.251 15.398-30.503 30.797-45.735 46.176 161.781 159.584 456.337 177.301 643.164-16.003 193.416-200.12 149.585-488.401 5.346-626.396-56.661 54.941-113.278 109.839-171.898 167.468-9.587 9.377-19.174 18.754-31.401 29.287-17.031 17.498-34.062 34.995-53.17 55.23-4.967 4.743-9.934 9.485-17.489 15.395-18.525 19.010-37.051 38.020-57.67 59.748-6.536 6.316-13.073 12.632-21.576 19.475-1.581 2.108-3.163 4.216-6.521 9.011-6.080 5.961-12.16 11.922-20.487 18.182-1.455 0.761-2.314 1.951-2.211 3.625 0 0 0.034 0.37-0.935-0.085zM457.349 571.913c0 0-0.712 0.977 2.256-1.469 31.315-31.739 62.63-63.477 97.020-96.279 4.189-4.859 8.378-9.717 15.274-15.85 25.278-25.754 50.555-51.508 78.782-78.569 8.838-9.388 17.677-18.776 29.227-29.44 57.003-56.719 114.006-113.437 173.154-172.291-127.777-109.386-271.643-148.812-430.725-105.427-177.884 48.513-292.038 167.261-331.848 348.107-33.559 152.449 8.712 287.734 114.173 407.397 13.331-16.326 24.48-29.98 38.474-44.797 3.774-2.991 7.548-5.982 14.040-10.238 18.934-19.42 37.868-38.839 59.863-59.322 4.17-4.831 8.339-9.662 15.251-15.732 41.798-42.129 83.596-84.258 125.060-126.090z","M647.731 381.052c-25.278 25.754-50.555 51.508-78.41 76.23-85.383-1.219-168.198-2.262-250.989-1.289-54.246 0.638-104.545 39.561-115.433 92.715-6.635 32.394-3.208 66.74-5.864 100.084-4.093 51.377 6.421 97.585 43.256 135.665-11.149 13.654-22.298 27.308-35.629 43.635-105.46-119.663-147.732-254.948-114.173-407.397 39.81-180.846 153.964-299.594 331.848-348.107 159.081-43.385 302.948-3.959 430.725 105.427-59.149 58.854-116.151 115.572-175.322 171.472-52.719-33.54-106.686-44.272-167.197-35.86-49.736 6.914-90.483 28.306-128.083 58.454-11.706 9.386-24.523 22.034-10.979 38.809 12.334 15.278 25.445 6.171 35.552-3.391 47.505-44.942 103.381-65.857 167.97-54.371 25.104 4.464 48.535 18.337 72.729 27.924z","M706.678 370.357c56.617-54.898 113.234-109.796 169.895-164.737 144.239 137.995 188.070 426.275-5.346 626.396-186.827 193.303-481.382 175.587-643.164 16.003 15.232-15.379 30.484-30.777 48.077-45.472 16.687 3.827 31.019 9.601 45.377 9.666 148.48 0.671 296.967 0.883 445.447 0.224 69.745-0.309 124.21-55.525 125.408-125.287 0.577-33.594 0.331-67.208 0.085-100.81-0.56-76.639-54.843-130.298-131.996-130.73-46.094-0.258-92.187-0.496-138.281-0.743 17.031-17.498 34.062-34.995 53.601-52.013 6.812 3.35 10.774 7.623 15.497 8.774 7.82 1.905 21.142 5.075 23.229 1.883 4.929-7.54 6.615-18.719 5.65-28.014-0.567-5.454-8.732-10.12-13.479-15.14z","M288.683 788.389c17.098-17.578 34.196-35.156 54.055-52.173 25.691 0.936 49.255 4.88 71.414 0.931 43.88-7.82 43.56-9.622 43.559-53.704-0-12.444 1.298-25.097-0.649-37.229-0.782-4.871-8.698-8.597-13.367-12.844 15.138-15.638 30.275-31.276 47.258-47.226 1.845-0.312 3.424-1.259 3.58 1.081 0.156 51.265 0.156 100.191 0.156 149.031 10.116 0 17.547 0 26.374 0 0-12.799 0-24.391 0-35.982 0-12.228 0-24.456 0-35.717 22.424-2.396 42.894-2.377 62.18-7.144 31.628-7.817 48.024-32.983 47.054-66.477-0.906-31.284-17.309-50.117-50.913-57.192-10.573-2.226-21.551-2.53-32.344-3.714 18.525-19.010 37.051-38.020 58.45-56.409 55.319 0.696 107.77 0.285 160.207 0.974 60.482 0.795 106.686 46.886 107.85 107.17 0.605 31.3 0.184 62.62 0.12 93.931-0.152 73.981-44.235 118.075-118.264 118.112-138.552 0.068-277.104 0.207-415.655-0.22-17.027-0.052-34.044-3.385-51.065-5.197zM685.514 536.493c-9.389 11.917-22.983 22.491-27.307 36.027-9.514 29.78 6.364 55.983 37.202 65.543 19.283 5.977 39.588 8.571 59.224 13.52 14.668 3.697 25.308 11.705 25.972 28.854 0.681 17.589-8.526 28.124-23.865 33.383-8.692 2.98-18.715 5.807-27.408 4.348-24.887-4.178-49.4-10.58-78.679-17.143 5.071 9.826 7.662 24.504 15.266 27.874 29.575 13.11 61.062 18.449 93.416 8.941 29.759-8.745 48.729-31.477 48.58-58.593-0.153-27.827-15.741-45.238-49.196-53.785-15.864-4.053-32.090-6.666-48.039-10.411-17.044-4.002-31.058-15.2-27.148-32.21 2.68-11.657 15.84-25.504 27.342-29.37 14.379-4.833 32.251-2.436 47.889 0.59 13.633 2.638 26.26 10.48 39.331 16.022 1.731-1.056 3.462-2.112 5.193-3.168-4.75-9.855-7.118-25.43-14.664-28.573-32.18-13.401-65.977-19.331-103.108-1.85z","M553.549 475.228c-31.315 31.739-62.63 63.477-95.517 94.277 4.048-19.731-3.166-30.462-22.079-34.436-11.364-2.388-22.431-6.401-33.866-8.2-45.115-7.097-87.371 11.223-106.921 45.699-22.276 39.283-18.799 104.031 7.602 135.059 2.749 3.23 7.425 4.821 11.207 7.172-18.934 19.42-37.868 38.839-58.357 57.246-21.783-25.020-39.474-50.197-39.474-83.5 0.001-36.655-0.838-73.334 0.222-109.958 1.634-56.438 47.027-102.894 103.262-103.688 77.956-1.101 155.944 0.109 233.919 0.329z","M287.624 789.775c18.081 0.427 35.098 3.759 52.124 3.812 138.55 0.427 277.103 0.289 415.655 0.22 74.029-0.037 118.112-44.131 118.264-118.112 0.064-31.311 0.485-62.632-0.12-93.931-1.164-60.285-47.368-106.376-107.85-107.17-52.437-0.689-104.888-0.278-158.913-1.557 3.386-5.947 8.354-10.689 14.359-16.801 47.132-1.122 93.226-0.884 139.32-0.626 77.153 0.432 131.437 54.090 131.996 130.73 0.245 33.602 0.492 67.216-0.085 100.81-1.198 69.762-55.663 124.978-125.408 125.287-148.48 0.659-296.967 0.447-445.447-0.224-14.358-0.065-28.691-5.839-44.090-10.257 2.342-4.461 5.739-7.628 10.196-12.179z","M649.206 380.399c-25.668-8.934-49.099-22.807-74.203-27.271-64.589-11.486-120.465 9.429-167.97 54.371-10.107 9.562-23.218 18.669-35.552 3.391-13.543-16.775-0.727-29.423 10.979-38.809 37.6-30.148 78.346-51.54 128.083-58.454 60.511-8.412 114.478 2.32 165.841 36.498-8.027 10.846-16.865 20.234-27.178 30.275z","M457.684 571.617c-41.798 42.129-83.596 84.258-127.487 125.542-26.108-30.178-27.734-63.581-17.936-98.031 8.9-31.292 33.578-51.725 65.771-48.569 26.979 2.645 53.133 13.717 79.652 21.058z","M555.087 474.696c-79.512 0.311-157.501-0.898-235.457 0.203-56.235 0.794-101.629 47.25-103.262 103.688-1.060 36.624-0.222 73.304-0.222 109.958-0.001 33.303 17.69 58.48 38.115 84.132-3.578 4.636-7.352 7.627-12.548 11.199-38.258-37.499-48.771-83.707-44.678-135.084 2.656-33.344-0.771-67.69 5.864-100.084 10.887-53.154 61.187-92.077 115.433-92.715 82.792-0.973 165.607 0.070 249.637 1.926-2.965 6.528-7.154 11.386-12.881 16.777z","M545.992 531.388c11.84-0.175 22.818 0.129 33.391 2.355 33.604 7.075 50.007 25.908 50.913 57.192 0.97 33.494-15.425 58.66-47.054 66.477-19.286 4.767-39.756 4.748-62.18 7.144 0 11.261 0 23.489 0 35.717 0 11.592 0 23.183 0 35.982-8.826 0-16.258 0-26.374 0 0-48.84 0-97.766-0.001-149.426-0.001-2.734-0.505-3.42-0.505-3.42s-0.034-0.37 1.059-0.011c1.85-1.188 2.607-2.735 3.365-4.282 6.080-5.961 12.16-11.922 20.248-17.625 2.008 25.676 2.008 51.094 2.008 77.932 17.043-0.788 31.148-0.143 44.792-2.347 26.442-4.272 39.396-20.134 38.472-44.438-0.849-22.312-15.605-35.775-42.153-38.187-12.196-1.108-24.425-1.849-36.638-2.756 6.536-6.316 13.073-12.632 20.656-20.308z","M457.516 571.765c-26.352-7.489-52.505-18.561-79.484-21.206-32.194-3.157-56.871 17.277-65.771 48.569-9.799 34.449-8.172 67.852 16.564 98.65-3.448 6.296-7.618 11.127-13.318 16.489-5.312-1.82-9.989-3.41-12.737-6.64-26.401-31.028-29.878-95.776-7.602-135.059 19.55-34.476 61.806-52.796 106.921-45.699 11.434 1.799 22.502 5.812 33.866 8.2 18.913 3.974 26.127 14.705 20.595 35.66 0.088 2.163 0.8 1.185 0.968 1.037z","M524.353 551.959c13.198 0.644 25.427 1.385 37.622 2.493 26.548 2.411 41.304 15.875 42.153 38.187 0.924 24.304-12.030 40.166-38.472 44.438-13.644 2.204-27.749 1.559-44.792 2.347 0-26.838 0-52.255-1.119-79.275 0.462-3.71 2.043-5.818 4.608-8.19z","M442.653 634.725c5.711 2.892 13.627 6.618 14.409 11.489 1.947 12.132 0.649 24.785 0.649 37.229 0.001 44.083 0.322 45.885-43.559 53.704-22.158 3.949-45.723 0.005-70.051-1.469 3.368-5.73 8.136-10.36 15.853-16.702 19.909-2.381 37.037-1.881 53.745-4.292 6.746-0.974 18.282-9.024 17.833-12.635-1.964-15.793 11.32-35.329-9.472-47.16 6.517-6.27 13.034-12.539 20.593-20.164z","M420.736 655.43c22.116 11.289 8.832 30.825 10.796 46.618 0.449 3.61-11.087 11.661-17.833 12.635-16.708 2.411-33.836 1.911-52.788 2.931 18.172-20.313 38.337-40.978 59.825-62.184z","M705.677 371.722c5.749 3.655 13.914 8.32 14.481 13.775 0.965 9.296-0.721 20.474-5.65 28.014-2.087 3.192-15.409 0.021-23.229-1.883-4.723-1.15-8.685-5.423-14.177-9.351 8.399-10.435 17.986-19.812 28.575-30.555z","M497.484 579.266c0.366 1.398-0.391 2.945-2.425 4.106-1.012-2.006-0.153-3.196 2.425-4.106z","M493.7 583.183c0.484 0.227 0.988 0.913 0.833 1.308s-1.735 1.341-2.125 0.521c-0.39-0.82 0.808-2.057 1.292-1.829z","M687.197 535.787c35.449-16.776 69.246-10.846 101.426 2.555 7.546 3.142 9.914 18.717 14.664 28.573-1.731 1.056-3.462 2.112-5.193 3.168-13.071-5.543-25.698-13.385-39.331-16.022-15.639-3.026-33.51-5.423-47.889-0.59-11.501 3.866-24.662 17.713-27.342 29.37-3.91 17.009 10.104 28.208 27.148 32.21 15.949 3.745 32.176 6.358 48.039 10.411 33.454 8.548 49.042 25.958 49.196 53.785 0.149 27.116-18.82 49.848-48.58 58.593-32.354 9.507-63.842 4.168-93.416-8.941-7.604-3.37-10.195-18.049-15.266-27.874 29.28 6.563 53.793 12.965 78.679 17.143 8.693 1.459 18.716-1.368 27.408-4.348 15.339-5.259 24.545-15.794 23.865-33.383-0.664-17.149-11.304-25.157-25.972-28.854-19.636-4.949-39.941-7.543-59.224-13.52-30.839-9.56-46.717-35.762-37.202-65.543 4.324-13.535 17.918-24.109 28.99-36.732z"],"attrs":[{"fill":"rgb(255, 253, 253)"},{"fill":"rgb(255, 37, 4)"},{"fill":"rgb(255, 253, 253)"},{"fill":"rgb(255, 252, 252)"},{"fill":"rgb(251, 251, 251)"},{"fill":"rgb(252, 250, 250)"},{"fill":"rgb(5, 5, 5)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(250, 248, 248)"},{"fill":"rgb(6, 6, 6)"},{"fill":"rgb(10, 10, 10)"},{"fill":"rgb(12, 12, 12)"},{"fill":"rgb(251, 251, 251)"},{"fill":"rgb(10, 10, 10)"},{"fill":"rgb(250, 238, 238)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(113, 106, 106)"},{"fill":"rgb(113, 106, 106)"},{"fill":"rgb(12, 12, 12)"}],"width":1071,"isMulticolor":true,"isMulticolor2":false,"grid":0,"tags":["gps_signal_low"]},"attrs":[{"fill":"rgb(255, 253, 253)"},{"fill":"rgb(255, 37, 4)"},{"fill":"rgb(255, 253, 253)"},{"fill":"rgb(255, 252, 252)"},{"fill":"rgb(251, 251, 251)"},{"fill":"rgb(252, 250, 250)"},{"fill":"rgb(5, 5, 5)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(250, 248, 248)"},{"fill":"rgb(6, 6, 6)"},{"fill":"rgb(10, 10, 10)"},{"fill":"rgb(12, 12, 12)"},{"fill":"rgb(251, 251, 251)"},{"fill":"rgb(10, 10, 10)"},{"fill":"rgb(250, 238, 238)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(113, 106, 106)"},{"fill":"rgb(113, 106, 106)"},{"fill":"rgb(12, 12, 12)"}],"properties":{"order":2,"id":2,"name":"gps_signal_low","prevSize":32,"code":59648,"codes":[59648,59649,59650,59651,59652,59653,59654,59655,59656,59657,59658,59659,59660,59661,59662,59663,59664,59665,59666]},"setIdx":0,"setId":0,"iconIdx":0},{"icon":{"paths":["M601.248 1028.697c-200.416 0-398.483 0-596.55 0-0-341.333-0-682.667-0-1024 356.991-0 713.982-0 1070.972-0 0 341.333 0 682.666 0 1024-157.358 0-314.716 0-474.422 0zM992.795 670.585c26.319-76.179 33.921-154.172 18.963-233.408-47.787-253.137-266.071-416.098-522.563-391.022-277.053 27.086-471.494 299.456-412.78 571.963 40.134 186.274 229.787 382.491 455.627 363.915 38.042-3.129 77.887 4.194 114.39-4.212 168.807-38.872 283.272-142.1 346.364-307.236z","M992.239 672.346c-62.537 163.374-177.001 266.603-345.809 305.475-36.502 8.406-76.348 1.083-114.39 4.212-225.84 18.576-415.492-177.641-455.627-363.915-58.714-272.507 135.727-544.877 412.78-571.963 256.493-25.076 474.776 137.885 522.563 391.022 14.958 79.236 7.356 157.229-19.519 235.17zM664.661 812.673c34.442-0.014 68.894 0.519 103.322-0.157 68.659-1.348 123.080-55.748 124.381-124.386 0.682-35.994 0.692-72.023-0.003-108.016-1.326-68.636-55.819-123.928-124.405-124.27-148.722-0.741-297.452-0.743-446.174 0.001-68.563 0.343-123.086 55.742-124.408 124.298-0.634 32.864-0.22 65.75-0.12 98.626 0.245 80.241 53.727 133.784 133.948 133.873 109.587 0.121 219.175 0.031 333.459 0.031zM682.018 249.234c-58.826-19.859-118.173-30.421-180.832-19.534-68.359 11.877-127.277 40.675-177.552 87.976-10.499 9.878-22.386 19.995-6.634 34.441 13.496 12.377 25.599 7.537 37.361-4.155 89.111-88.585 230.179-104.762 339.503-45.382 25.276 13.729 47.029 33.919 70.515 50.983 10.067 7.314 20.821 8.483 29.222-1.699 8.968-10.869 5.112-20.28-4.318-30.329-29.478-31.414-65.62-52.506-107.267-72.301zM643.5 331.137c-28.682-7.041-57.115-18.587-86.096-20.186-60.451-3.335-115.995 13.125-163.652 53.267-26.11 21.993-34.912 35.592-22.149 46.667 22.561 19.577 33.74-3.52 47.501-15.060 37.726-31.635 82.827-48.181 130.31-46.809 53.545 1.548 101.016 25.184 141.615 61.611 5.127 4.6 16.988 1.697 25.729 2.27 0.367-10.311 5.19-25.976 0.338-30.014-22.038-18.339-46.802-33.401-73.596-51.747z","M662.312 812.673c-111.936-0-221.523 0.091-331.111-0.031-80.221-0.089-133.702-53.632-133.948-133.873-0.1-32.876-0.514-65.762 0.12-98.626 1.322-68.556 55.846-123.955 124.408-124.298 148.722-0.745 297.452-0.743 446.174-0.001 68.586 0.342 123.079 55.634 124.405 124.27 0.695 35.994 0.685 72.022 0.003 108.016-1.301 68.638-55.722 123.038-124.381 124.386-34.428 0.676-68.881 0.143-105.671 0.157zM368.775 793.819c130.586-0.001 261.173 0.164 391.759-0.071 67.376-0.121 112.735-45.978 113.104-113.691 0.179-32.841 0.547-65.694-0.096-98.524-1.159-59.195-46.848-106.465-105.64-106.775-148.567-0.785-297.144-0.749-445.711-0.025-57.945 0.282-104.202 46.517-105.834 104.128-1.040 36.722-0.916 73.515-0.024 110.244 1.173 48.284 33.133 89.407 80.327 100.115 21.711 4.926 44.915 3.271 72.114 4.599z","M683.667 250.093c39.997 18.936 76.139 40.028 105.617 71.442 9.43 10.049 13.285 19.46 4.318 30.329-8.401 10.182-19.155 9.012-29.222 1.699-23.486-17.063-45.239-37.254-70.515-50.983-109.325-59.38-250.392-43.203-339.503 45.382-11.762 11.692-23.865 16.532-37.361 4.155-15.752-14.446-3.865-24.563 6.634-34.441 50.274-47.301 109.193-76.099 177.552-87.976 62.658-10.887 122.006-0.326 182.481 20.393z","M645.005 332.189c25.288 17.293 50.053 32.356 72.090 50.695 4.853 4.038 0.030 19.703-0.338 30.014-8.741-0.573-20.601 2.331-25.729-2.27-40.6-36.427-88.071-60.063-141.615-61.611-47.484-1.373-92.584 15.174-130.31 46.809-13.761 11.54-24.94 34.636-47.501 15.060-12.763-11.075-3.961-24.674 22.149-46.667 47.658-40.143 103.201-56.602 163.652-53.267 28.982 1.599 57.415 13.145 87.602 21.238z","M366.439 793.819c-24.863-1.329-48.067 0.327-69.778-4.599-47.194-10.708-79.155-51.831-80.327-100.115-0.892-36.73-1.016-73.522 0.024-110.244 1.632-57.611 47.889-103.846 105.834-104.128 148.568-0.724 297.144-0.76 445.711 0.025 58.792 0.31 104.481 47.581 105.64 106.775 0.642 32.83 0.275 65.683 0.096 98.524-0.369 67.713-45.728 113.57-113.104 113.691-130.586 0.235-261.173 0.070-394.095 0.071zM410.844 657.647c6.827 0.309 13.654 0.619 22.056 1 0 14.41 0.614 27.571-0.338 40.618-0.327 4.485-3.472 10.549-7.185 12.728-40.471 23.743-102.254 0.921-113.308-44.194-5.174-21.115-4.564-45.116-0.526-66.677 6.082-32.478 33.028-52.991 66.314-50.82 26.181 1.708 51.959 9.592 78.275 14.804 5.308-16.108-3.216-24.293-20.19-29.897-58.040-19.164-113.328-6.091-138.535 34.017-26.323 41.883-21.286 111.849 11.512 144.489 36.008 35.836 89.26 35.018 139.289 12.143 3.876-1.772 8.115-7.772 8.234-11.92 0.748-26.179 0.379-52.389 0.379-78.263-27.606 0-52.162 0-77.875 0 0.878 8.232 1.576 14.768 2.345 21.973 9.523 0 17.283 0 29.554 0zM657.369 573.207c-1.997 41.811 9.834 57.724 50.995 67.96 17.323 4.308 35.697 5.977 51.802 12.971 8.886 3.859 18.649 15.769 19.638 24.952 1.019 9.461-5.876 22.55-13.498 29.368-7.288 6.519-20.319 10.495-30.202 9.485-26.452-2.705-52.568-8.684-81.127-13.773 1.527 5.9 0.893 18.391 6.62 23.030 27.401 22.194 96.867 19.471 124.106-3.414 16.705-14.035 24.667-31.973 20.79-54.121-3.782-21.604-17.421-34.603-37.705-40.392-17.18-4.903-34.768-8.413-52.239-12.252-20.136-4.424-36.857-13.551-34.112-36.974 2.673-22.812 21.442-31.392 41.201-29.732 24.914 2.094 49.415 9.105 74.859 14.146 5.42-15.393 0.070-27.113-21.212-30.435-11.497-1.795-22.617-6.101-34.125-7.704-38.083-5.303-68.182 10.241-85.791 46.885zM621.648 561.187c-33.585-44.783-81.323-26.053-124.295-30.444 0 70.19 0 137.675 0 205.577 8.016 0 14.624 0 23.448 0 0-24.832 0-48.61 0-72.033 97.387-10.277 124.705-37.339 100.847-103.1z","M408.589 657.647c-10.016 0-17.776 0-27.299 0-0.769-7.204-1.466-13.74-2.345-21.973 25.713 0 50.269 0 77.875 0 0 25.874 0.369 52.085-0.379 78.263-0.118 4.148-4.358 10.148-8.234 11.92-50.029 22.875-103.28 23.693-139.289-12.143-32.798-32.641-37.836-102.606-11.512-144.489 25.207-40.108 80.495-53.181 138.535-34.017 16.974 5.605 25.498 13.789 20.19 29.897-26.315-5.212-52.093-13.096-78.275-14.804-33.286-2.171-60.232 18.342-66.314 50.82-4.038 21.56-4.648 45.561 0.526 66.677 11.054 45.115 72.837 67.938 113.308 44.194 3.714-2.179 6.858-8.243 7.185-12.728 0.952-13.047 0.338-26.208 0.338-40.618-8.402-0.381-15.229-0.69-24.311-1z","M657.702 571.307c17.275-34.745 47.374-50.289 85.457-44.986 11.509 1.603 22.628 5.909 34.125 7.704 21.282 3.322 26.632 15.042 21.212 30.435-25.444-5.041-49.944-12.052-74.859-14.146-19.759-1.66-38.528 6.92-41.201 29.732-2.745 23.423 13.976 32.549 34.112 36.974 17.471 3.839 35.059 7.348 52.239 12.252 20.284 5.789 33.923 18.788 37.705 40.392 3.877 22.148-4.085 40.086-20.79 54.121-27.239 22.885-96.705 25.608-124.106 3.414-5.727-4.639-5.093-17.13-6.62-23.030 28.558 5.089 54.675 11.068 81.127 13.773 9.883 1.011 22.914-2.966 30.202-9.485 7.622-6.818 14.516-19.906 13.498-29.368-0.989-9.183-10.752-21.093-19.638-24.952-16.105-6.994-34.478-8.663-51.802-12.971-41.16-10.236-52.992-26.149-50.661-69.859z","M622.778 562.655c22.728 64.292-4.59 91.354-101.976 101.631 0 23.423 0 47.201 0 72.033-8.824 0-15.432 0-23.448 0 0-67.902 0-135.386 0-205.577 42.971 4.391 90.71-14.339 125.424 31.912zM604.49 587.095c-12.204-31.237-39.004-40.681-82.929-29.447 0 26.401 0 52.698 0 78.832 58.152 8.154 79.432-3.716 82.929-49.386z","M604.637 588.908c-3.644 43.857-24.924 55.727-83.076 47.573 0-26.134 0-52.432 0-78.832 43.925-11.234 70.725-1.79 83.076 31.26z"],"attrs":[{"fill":"rgb(255, 255, 254)"},{"fill":"rgb(255, 255, 1)"},{"fill":"rgb(6, 6, 0)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(251, 251, 0)"},{"fill":"rgb(11, 11, 4)"},{"fill":"rgb(13, 13, 4)"},{"fill":"rgb(11, 11, 4)"},{"fill":"rgb(251, 251, 0)"}],"width":1071,"isMulticolor":true,"isMulticolor2":false,"grid":0,"tags":["gps_signal_middle"]},"attrs":[{"fill":"rgb(255, 255, 254)"},{"fill":"rgb(255, 255, 1)"},{"fill":"rgb(6, 6, 0)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(251, 251, 0)"},{"fill":"rgb(11, 11, 4)"},{"fill":"rgb(13, 13, 4)"},{"fill":"rgb(11, 11, 4)"},{"fill":"rgb(251, 251, 0)"}],"properties":{"order":3,"id":1,"name":"gps_signal_middle","prevSize":32,"code":59667,"codes":[59667,59668,59669,59670,59671,59672,59673,59674,59675,59676]},"setIdx":0,"setId":0,"iconIdx":1},{"icon":{"paths":["M634.128 1028.697c-211.376 0-420.404 0-629.431 0-0-341.333-0-682.667-0-1024 356.991-0 713.982-0 1070.972-0 0 341.333 0 682.666 0 1024-146.398 0-292.795 0-441.541 0zM1018.959 501.469c-2.949-21.188-6.17-42.343-8.797-63.572-7.842-63.369-29.651-122.3-64.221-175.103-52.965-80.9-122.179-144.268-212.401-183.329-93.652-40.546-190.908-50.996-289.73-30.777-107.563 22.007-197.921 76.162-268.048 162.18-91.023 111.65-127.203 239.557-104.177 380.846 18.831 115.543 74.43 213.335 166.396 288.16 104.879 85.331 223.578 123.823 359.421 107.232 127.561-15.579 230.911-75.57 312.898-171.565 75.519-88.421 105.49-194.946 108.657-314.073z","M1019.106 503.618c-3.315 116.979-33.286 223.503-108.805 311.924-81.987 95.995-185.338 155.986-312.898 171.565-135.842 16.591-254.542-21.901-359.421-107.232-91.966-74.825-147.566-172.617-166.396-288.16-23.027-141.289 13.153-269.197 104.177-380.846 70.127-86.018 160.485-140.173 268.048-162.18 98.822-20.219 196.077-9.769 289.73 30.777 90.221 39.061 159.436 102.428 212.401 183.329 34.569 52.803 56.378 111.734 64.221 175.103 2.627 21.228 5.847 42.383 8.944 65.72zM799.743 807.979c62.703-22.243 92.613-64.375 92.766-130.838 0.074-32.063 0.444-64.135-0.102-96.189-1.189-69.756-55.905-124.893-125.817-125.173-147.803-0.59-295.611-0.59-443.413-0-69.904 0.279-124.673 55.467-125.831 125.172-0.533 32.055-0.164 64.126-0.096 96.189 0.174 82.015 53.086 135.436 134.513 135.5 141.549 0.11 283.097 0.132 424.645-0.139 13.163-0.025 26.322-2.415 43.334-4.522zM845.943 290.783c19.664-10.476 16.044-24.67 4.691-38.181-6.827-8.124-16.424-13.898-24.667-20.858-102.658-86.687-220.311-118.998-352.495-95.284-83.844 15.042-156.91 52.061-217.902 112.387-11.64 11.513-17.353 25.465-7.901 36.833 10.802 12.99 25.375 4.53 36.919-4.494 1.834-1.434 3.342-3.283 5.018-4.923 60.514-59.235 134.53-92.844 217.163-101.47 109.754-11.457 209.174 18.078 293.803 91.941 11.899 10.386 27.653 16.356 45.371 24.049zM775.039 307.636c-78.335-64.668-166.752-92.678-268.706-78.131-71.31 10.174-131.594 40.482-183.638 89.055-10.127 9.451-20.415 19.409-6.217 33.024 13.592 13.034 25.731 7.147 37.973-3.565 13.894-12.158 27.903-24.545 43.376-34.475 61.476-39.454 128.107-52.37 200.921-41.953 56.244 8.047 104.338 30.4 146.366 67.287 13.962 12.254 29.686 30.189 46.735 14.515 19.369-17.807-2.919-30.777-16.81-45.756zM399.999 413.2c4.109-3.525 8.218-7.050 12.327-10.576 55.468-47.602 118.517-67.82 188.788-42.195 32.254 11.762 60.401 34.606 90.859 51.624 5.929 3.313 16.294 6.3 20.4 3.423 5.732-4.015 9.759-13.546 10.223-20.983 0.345-5.545-4.97-12.755-9.708-17.108-71.283-65.488-154.002-84.582-245.127-52.948-31.757 11.024-60.431 32.244-88.491 51.721-7.438 5.163-7.376 21.133-10.757 32.141 9.337 1.91 18.674 3.821 31.487 4.902z","M797.817 808.386c-15.087 1.699-28.245 4.090-41.408 4.115-141.548 0.271-283.097 0.249-424.645 0.139-81.427-0.063-134.339-53.485-134.513-135.5-0.068-32.063-0.437-64.134 0.096-96.189 1.159-69.705 55.927-124.893 125.831-125.172 147.803-0.59 295.611-0.59 443.413 0 69.912 0.279 124.629 55.417 125.817 125.173 0.546 32.054 0.176 64.126 0.102 96.189-0.153 66.463-30.063 108.596-94.692 131.246zM462.679 793.82c100.986-0.005 201.975 0.427 302.958-0.18 60.49-0.363 106.842-46.898 107.908-107.238 0.608-34.434 0.52-68.894 0.025-103.332-0.887-61.658-47.25-108.284-109.092-108.475-146.39-0.453-292.783-0.452-439.173-0.001-61.838 0.191-108.207 46.807-109.117 108.458-0.496 33.655-0.422 67.327-0.021 100.984 0.751 63.060 47.040 109.14 110.297 109.701 43.836 0.389 87.678 0.079 136.214 0.082z","M844.068 290.944c-15.844-7.853-31.597-13.823-43.497-24.209-84.628-73.863-184.049-103.398-293.803-91.941-82.633 8.626-156.65 42.235-217.163 101.47-1.675 1.64-3.184 3.489-5.018 4.923-11.544 9.024-26.117 17.485-36.919 4.494-9.453-11.368-3.739-25.32 7.901-36.833 60.992-60.327 134.058-97.345 217.902-112.387 132.184-23.715 249.838 8.597 352.495 95.284 8.242 6.96 17.84 12.734 24.667 20.858 11.353 13.51 14.973 27.705-6.565 38.341z","M776.248 308.845c12.682 13.769 34.97 26.74 15.6 44.547-17.049 15.674-32.774-2.26-46.735-14.515-42.028-36.887-90.122-59.24-146.366-67.287-72.815-10.418-139.446 2.499-200.921 41.953-15.472 9.93-29.482 22.317-43.376 34.475-12.242 10.712-24.38 16.599-37.973 3.565-14.197-13.615-3.909-23.572 6.217-33.024 52.044-48.573 112.328-78.88 183.638-89.055 101.954-14.546 190.371 13.463 269.916 79.34z","M398.261 413.615c-11.075-1.496-20.412-3.406-29.749-5.317 3.381-11.008 3.319-26.978 10.757-32.141 28.060-19.476 56.734-40.696 88.491-51.721 91.125-31.634 173.844-12.54 245.127 52.948 4.738 4.353 10.054 11.563 9.708 17.108-0.463 7.438-4.491 16.968-10.223 20.983-4.106 2.876-14.471-0.111-20.4-3.423-30.457-17.017-58.605-39.862-90.859-51.624-70.271-25.626-133.319-5.407-188.788 42.195-4.109 3.526-8.218 7.051-14.065 10.991z","M460.33 793.82c-46.188-0.003-90.030 0.307-133.865-0.082-63.258-0.561-109.546-46.641-110.297-109.701-0.401-33.657-0.475-67.329 0.021-100.984 0.909-61.651 47.278-108.267 109.117-108.458 146.39-0.451 292.783-0.452 439.173 0.001 61.841 0.191 108.205 46.817 109.092 108.475 0.495 34.438 0.584 68.898-0.025 103.332-1.066 60.34-47.418 106.875-107.908 107.238-100.983 0.607-201.972 0.174-305.307 0.18zM340.633 734.174c35.799 14.349 70.394 7.23 104.144-6.899 5.113-2.141 11.149-9.453 11.386-14.62 1.171-25.462 0.532-51.008 0.532-76.842-27.198 0-51.657 0-75.875 0 0 7.697 0 13.674 0 22.485 17.769 0 34.459 0 52.007 0 0 14.544 1.262 26.438-0.504 37.863-0.961 6.217-5.759 14.609-11.061 16.943-38.518 16.957-84.093 3.123-102.989-30.044-21.968-38.559-13.994-97.141 18.296-119.752 10.837-7.589 26.101-13.677 38.802-12.783 26.576 1.871 52.794 8.84 80.39 13.943 4.744-12.517 0.763-23.549-16.791-27.935-10.55-2.636-21.136-5.176-31.792-7.328-55.339-11.175-102.859 12.441-119.261 60.624-15.491 45.51-9.434 119.357 52.716 144.345zM661.318 562.453c-12.57 40.998 1.507 66.808 42.796 77.559 18.777 4.889 38.636 6.857 56.313 14.213 8.794 3.66 18.328 16.050 19.211 25.287 0.905 9.46-6.123 22.435-13.802 29.166-7.362 6.452-20.389 10.329-30.272 9.265-26.437-2.848-52.522-8.954-81.688-14.305 2.368 7.142 2.569 19.328 8.532 23.757 27.572 20.48 90.308 19.552 118.496-0.039 18.721-13.012 28.751-30.903 26.118-54.048-2.676-23.529-16.679-38.243-39.23-44.283-19.539-5.233-39.435-9.122-59.019-14.2-15.932-4.132-26.398-14.267-26.752-31.326-0.367-17.68 11.667-26.503 26.824-31.211 6.559-2.037 14.138-3.582 20.689-2.35 22.963 4.317 45.681 9.94 71.626 15.771-2.892-8.827-3.314-22.748-8.993-25.396-36.951-17.233-99.421-26.774-130.849 22.141zM623.879 624.249c14.226-41.402 3.962-76.291-30.808-85.39-30.58-8.002-63.426-7.345-95.358-10.52 0 73.371 0 140.308 0 207.64 8.016 0 14.555 0 23.827 0 0-25.128 0-48.859 0-72.994 39.077-1.297 77.422 2.354 102.339-38.736z","M338.867 733.605c-60.385-24.419-66.442-98.266-50.95-143.776 16.401-48.183 63.922-71.799 119.261-60.624 10.657 2.152 21.243 4.692 31.792 7.328 17.554 4.386 21.535 15.418 16.791 27.935-27.596-5.103-53.814-12.071-80.39-13.943-12.701-0.894-27.964 5.194-38.802 12.783-32.29 22.611-40.264 81.194-18.296 119.752 18.897 33.168 64.471 47.001 102.989 30.044 5.302-2.334 10.1-10.726 11.061-16.943 1.766-11.425 0.504-23.319 0.504-37.863-17.548 0-34.239 0-52.007 0 0-8.811 0-14.788 0-22.485 24.218 0 48.676 0 75.875 0 0 25.834 0.639 51.38-0.532 76.842-0.238 5.167-6.273 12.479-11.386 14.62-33.75 14.129-68.344 21.248-105.909 6.329z","M662.091 560.759c30.656-47.221 93.125-37.68 130.076-20.448 5.679 2.648 6.101 16.569 8.993 25.396-25.945-5.831-48.663-11.454-71.626-15.771-6.551-1.232-14.13 0.312-20.689 2.35-15.157 4.708-27.191 13.531-26.824 31.211 0.354 17.060 10.82 27.195 26.752 31.326 19.584 5.079 39.48 8.967 59.019 14.2 22.551 6.040 36.554 20.754 39.23 44.283 2.633 23.145-7.397 41.036-26.118 54.048-28.188 19.592-90.924 20.519-118.496 0.039-5.963-4.429-6.164-16.614-8.532-23.757 29.166 5.351 55.252 11.457 81.688 14.305 9.883 1.065 22.911-2.812 30.272-9.265 7.68-6.731 14.707-19.706 13.802-29.166-0.883-9.236-10.418-21.627-19.211-25.287-17.677-7.356-37.536-9.324-56.313-14.213-41.289-10.751-55.366-36.561-42.024-79.253z","M622.875 625.792c-23.913 39.547-62.258 35.895-101.335 37.193 0 24.135 0 47.866 0 72.994-9.272 0-15.811 0-23.827 0 0-67.332 0-134.269 0-207.64 31.933 3.176 64.779 2.518 95.358 10.52 34.77 9.099 45.034 43.987 29.804 86.933zM571.737 555.427c-16.744 0-33.488 0-49.651 0 0 28.983 0 55.24 0 81.277 50.406 6.214 72.301-1.84 80.559-28.949 7.077-23.233-1.362-39.302-30.908-52.328z","M573.663 555.832c27.62 12.621 36.059 28.689 28.982 51.922-8.258 27.108-30.152 35.162-80.559 28.949 0-26.037 0-52.294 0-81.277 16.163 0 32.907 0 51.576 0.406z"],"attrs":[{"fill":"rgb(254, 255, 255)"},{"fill":"rgb(70, 248, 5)"},{"fill":"rgb(2, 5, 0)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(69, 244, 5)"},{"fill":"rgb(6, 11, 4)"},{"fill":"rgb(6, 12, 4)"},{"fill":"rgb(5, 11, 4)"},{"fill":"rgb(69, 244, 5)"}],"width":1071,"isMulticolor":true,"isMulticolor2":false,"grid":0,"tags":["gps_signal_high"]},"attrs":[{"fill":"rgb(254, 255, 255)"},{"fill":"rgb(70, 248, 5)"},{"fill":"rgb(2, 5, 0)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(2, 2, 2)"},{"fill":"rgb(1, 1, 1)"},{"fill":"rgb(69, 244, 5)"},{"fill":"rgb(6, 11, 4)"},{"fill":"rgb(6, 12, 4)"},{"fill":"rgb(5, 11, 4)"},{"fill":"rgb(69, 244, 5)"}],"properties":{"order":4,"id":0,"name":"gps_signal_high","prevSize":32,"code":59677,"codes":[59677,59678,59679,59680,59681,59682,59683,59684,59685,59686,59687]},"setIdx":0,"setId":0,"iconIdx":2}],"height":1024,"metadata":{"name":"icomoon"},"preferences":{"showGlyphs":true,"showCodes":true,"showQuickUse":true,"showQuickUse2":true,"showSVGs":true,"fontPref":{"prefix":"icon-","metadata":{"fontFamily":"icomoon"},"metrics":{"emSize":1024,"baseline":6.25,"whitespace":50},"embed":false},"imagePref":{"prefix":"icon-","png":true,"useClassSelector":true,"color":0,"bgColor":16777215},"historySize":50}} \ No newline at end of file diff --git a/assets/customIcon/icomoon/style.css b/assets/customIcon/icomoon/style.css new file mode 100644 index 0000000..181312b --- /dev/null +++ b/assets/customIcon/icomoon/style.css @@ -0,0 +1,224 @@ +@font-face { + font-family: 'icomoon'; + src: url('fonts/icomoon.eot?eq8f84'); + src: url('fonts/icomoon.eot?eq8f84#iefix') format('embedded-opentype'), + url('fonts/icomoon.ttf?eq8f84') format('truetype'), + url('fonts/icomoon.woff?eq8f84') format('woff'), + url('fonts/icomoon.svg?eq8f84#icomoon') format('svg'); + font-weight: normal; + font-style: normal; + font-display: block; +} + +[class^="icon-"], [class*=" icon-"] { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: 'icomoon' !important; + speak: never; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-gps_signal_low .path1:before { + content: "\e900"; + color: rgb(255, 253, 253); +} +.icon-gps_signal_low .path2:before { + content: "\e901"; + margin-left: -1.0458984375em; + color: rgb(255, 37, 4); +} +.icon-gps_signal_low .path3:before { + content: "\e902"; + margin-left: -1.0458984375em; + color: rgb(255, 253, 253); +} +.icon-gps_signal_low .path4:before { + content: "\e903"; + margin-left: -1.0458984375em; + color: rgb(255, 252, 252); +} +.icon-gps_signal_low .path5:before { + content: "\e904"; + margin-left: -1.0458984375em; + color: rgb(251, 251, 251); +} +.icon-gps_signal_low .path6:before { + content: "\e905"; + margin-left: -1.0458984375em; + color: rgb(252, 250, 250); +} +.icon-gps_signal_low .path7:before { + content: "\e906"; + margin-left: -1.0458984375em; + color: rgb(5, 5, 5); +} +.icon-gps_signal_low .path8:before { + content: "\e907"; + margin-left: -1.0458984375em; + color: rgb(1, 1, 1); +} +.icon-gps_signal_low .path9:before { + content: "\e908"; + margin-left: -1.0458984375em; + color: rgb(250, 248, 248); +} +.icon-gps_signal_low .path10:before { + content: "\e909"; + margin-left: -1.0458984375em; + color: rgb(6, 6, 6); +} +.icon-gps_signal_low .path11:before { + content: "\e90a"; + margin-left: -1.0458984375em; + color: rgb(10, 10, 10); +} +.icon-gps_signal_low .path12:before { + content: "\e90b"; + margin-left: -1.0458984375em; + color: rgb(12, 12, 12); +} +.icon-gps_signal_low .path13:before { + content: "\e90c"; + margin-left: -1.0458984375em; + color: rgb(251, 251, 251); +} +.icon-gps_signal_low .path14:before { + content: "\e90d"; + margin-left: -1.0458984375em; + color: rgb(10, 10, 10); +} +.icon-gps_signal_low .path15:before { + content: "\e90e"; + margin-left: -1.0458984375em; + color: rgb(250, 238, 238); +} +.icon-gps_signal_low .path16:before { + content: "\e90f"; + margin-left: -1.0458984375em; + color: rgb(1, 1, 1); +} +.icon-gps_signal_low .path17:before { + content: "\e910"; + margin-left: -1.0458984375em; + color: rgb(113, 106, 106); +} +.icon-gps_signal_low .path18:before { + content: "\e911"; + margin-left: -1.0458984375em; + color: rgb(113, 106, 106); +} +.icon-gps_signal_low .path19:before { + content: "\e912"; + margin-left: -1.0458984375em; + color: rgb(12, 12, 12); +} +.icon-gps_signal_middle .path1:before { + content: "\e913"; + color: rgb(255, 255, 254); +} +.icon-gps_signal_middle .path2:before { + content: "\e914"; + margin-left: -1.0458984375em; + color: rgb(255, 255, 1); +} +.icon-gps_signal_middle .path3:before { + content: "\e915"; + margin-left: -1.0458984375em; + color: rgb(6, 6, 0); +} +.icon-gps_signal_middle .path4:before { + content: "\e916"; + margin-left: -1.0458984375em; + color: rgb(2, 2, 2); +} +.icon-gps_signal_middle .path5:before { + content: "\e917"; + margin-left: -1.0458984375em; + color: rgb(1, 1, 1); +} +.icon-gps_signal_middle .path6:before { + content: "\e918"; + margin-left: -1.0458984375em; + color: rgb(251, 251, 0); +} +.icon-gps_signal_middle .path7:before { + content: "\e919"; + margin-left: -1.0458984375em; + color: rgb(11, 11, 4); +} +.icon-gps_signal_middle .path8:before { + content: "\e91a"; + margin-left: -1.0458984375em; + color: rgb(13, 13, 4); +} +.icon-gps_signal_middle .path9:before { + content: "\e91b"; + margin-left: -1.0458984375em; + color: rgb(11, 11, 4); +} +.icon-gps_signal_middle .path10:before { + content: "\e91c"; + margin-left: -1.0458984375em; + color: rgb(251, 251, 0); +} +.icon-gps_signal_high .path1:before { + content: "\e91d"; + color: rgb(254, 255, 255); +} +.icon-gps_signal_high .path2:before { + content: "\e91e"; + margin-left: -1.0458984375em; + color: rgb(70, 248, 5); +} +.icon-gps_signal_high .path3:before { + content: "\e91f"; + margin-left: -1.0458984375em; + color: rgb(2, 5, 0); +} +.icon-gps_signal_high .path4:before { + content: "\e920"; + margin-left: -1.0458984375em; + color: rgb(1, 1, 1); +} +.icon-gps_signal_high .path5:before { + content: "\e921"; + margin-left: -1.0458984375em; + color: rgb(2, 2, 2); +} +.icon-gps_signal_high .path6:before { + content: "\e922"; + margin-left: -1.0458984375em; + color: rgb(1, 1, 1); +} +.icon-gps_signal_high .path7:before { + content: "\e923"; + margin-left: -1.0458984375em; + color: rgb(69, 244, 5); +} +.icon-gps_signal_high .path8:before { + content: "\e924"; + margin-left: -1.0458984375em; + color: rgb(6, 11, 4); +} +.icon-gps_signal_high .path9:before { + content: "\e925"; + margin-left: -1.0458984375em; + color: rgb(6, 12, 4); +} +.icon-gps_signal_high .path10:before { + content: "\e926"; + margin-left: -1.0458984375em; + color: rgb(5, 11, 4); +} +.icon-gps_signal_high .path11:before { + content: "\e927"; + margin-left: -1.0458984375em; + color: rgb(69, 244, 5); +} diff --git a/assets/fonts/icomoon.ttf b/assets/fonts/icomoon.ttf new file mode 100644 index 0000000..f2e8bb4 Binary files /dev/null and b/assets/fonts/icomoon.ttf differ diff --git a/assets/images/QR_certificate.png b/assets/images/QR_certificate.png new file mode 100644 index 0000000..2097f17 Binary files /dev/null and b/assets/images/QR_certificate.png differ diff --git a/assets/images/QR_gifuroge_stage1.png b/assets/images/QR_gifuroge_stage1.png new file mode 100644 index 0000000..91dcdcc Binary files /dev/null and b/assets/images/QR_gifuroge_stage1.png differ diff --git a/assets/images/money.png b/assets/images/money.png new file mode 100644 index 0000000..9f2ff56 Binary files /dev/null and b/assets/images/money.png differ diff --git a/dependencies.txt b/dependencies.txt new file mode 100644 index 0000000..94e2545 --- /dev/null +++ b/dependencies.txt @@ -0,0 +1,535 @@ +Dart SDK 3.5.0 +Flutter SDK 3.24.0 +gifunavi 4.8.19+499 +├── async 2.11.0 +│ ├── collection... +│ └── meta... +├── camera 0.10.6 +│ ├── camera_android 0.10.9+11 +│ │ ├── camera_platform_interface... +│ │ ├── flutter... +│ │ ├── flutter_plugin_android_lifecycle... +│ │ └── stream_transform 2.1.0 +│ ├── camera_avfoundation 0.9.17+3 +│ │ ├── camera_platform_interface... +│ │ ├── flutter... +│ │ └── stream_transform... +│ ├── camera_platform_interface 2.8.0 +│ │ ├── cross_file... +│ │ ├── flutter... +│ │ ├── plugin_platform_interface... +│ │ └── stream_transform... +│ ├── camera_web 0.3.5 +│ │ ├── camera_platform_interface... +│ │ ├── flutter... +│ │ ├── flutter_web_plugins... +│ │ ├── stream_transform... +│ │ └── web... +│ ├── flutter... +│ └── flutter_plugin_android_lifecycle 2.0.21 +│ └── flutter... +├── camera_camera 3.0.0 +│ ├── camera... +│ ├── flutter... +│ └── font_awesome_flutter 10.7.0 +│ └── flutter... +├── circular_menu 4.0.0 +│ └── flutter... +├── collection 1.18.0 +├── connectivity_plus 6.0.5 +│ ├── collection... +│ ├── connectivity_plus_platform_interface 2.0.1 +│ │ ├── flutter... +│ │ ├── meta... +│ │ └── plugin_platform_interface... +│ ├── flutter... +│ ├── flutter_web_plugins... +│ ├── meta... +│ ├── nm 0.5.0 +│ │ └── dbus 0.7.10 +│ │ ├── args... +│ │ ├── ffi... +│ │ ├── meta... +│ │ └── xml... +│ └── web... +├── cupertino_icons 1.0.8 +├── flutter 0.0.0 +│ ├── characters 1.3.0 +│ ├── collection... +│ ├── material_color_utilities 0.11.1 +│ │ └── collection... +│ ├── meta... +│ ├── sky_engine 0.0.99 +│ └── vector_math... +├── flutter_breadcrumb 1.0.1 +│ ├── flutter... +│ └── pedantic 1.11.1 +├── flutter_compass 0.8.0 +│ └── flutter... +├── flutter_image 4.1.11 +│ └── flutter... +├── flutter_keyboard_visibility 6.0.0 +│ ├── flutter... +│ ├── flutter_keyboard_visibility_linux 1.0.0 +│ │ ├── flutter... +│ │ └── flutter_keyboard_visibility_platform_interface... +│ ├── flutter_keyboard_visibility_macos 1.0.0 +│ │ ├── flutter... +│ │ └── flutter_keyboard_visibility_platform_interface... +│ ├── flutter_keyboard_visibility_platform_interface 2.0.0 +│ │ ├── flutter... +│ │ ├── meta... +│ │ └── plugin_platform_interface... +│ ├── flutter_keyboard_visibility_web 2.0.0 +│ │ ├── flutter... +│ │ ├── flutter_keyboard_visibility_platform_interface... +│ │ └── flutter_web_plugins... +│ ├── flutter_keyboard_visibility_windows 1.0.0 +│ │ ├── flutter... +│ │ └── flutter_keyboard_visibility_platform_interface... +│ └── meta... +├── flutter_launcher_icons 0.13.1 +│ ├── args 2.5.0 +│ ├── checked_yaml 2.0.3 +│ │ ├── json_annotation... +│ │ ├── source_span... +│ │ └── yaml... +│ ├── cli_util 0.4.1 +│ │ ├── meta... +│ │ └── path... +│ ├── image 4.2.0 +│ │ ├── archive 3.6.1 +│ │ │ ├── crypto... +│ │ │ └── path... +│ │ ├── meta... +│ │ └── xml 6.5.0 +│ │ ├── collection... +│ │ ├── meta... +│ │ └── petitparser 6.0.2 +│ │ └── meta... +│ ├── json_annotation 4.9.0 +│ │ └── meta... +│ ├── path... +│ └── yaml 3.1.2 +│ ├── collection... +│ ├── source_span... +│ └── string_scanner... +├── flutter_lints 4.0.0 +│ └── lints 4.0.0 +├── flutter_map 6.2.1 +│ ├── async... +│ ├── collection... +│ ├── flutter... +│ ├── http... +│ ├── latlong2... +│ ├── logger 2.4.0 +│ ├── meta... +│ ├── polylabel 1.0.1 +│ │ └── collection... +│ ├── proj4dart... +│ └── vector_math... +├── flutter_map_location_marker 8.0.5 +│ ├── flutter... +│ ├── flutter_compass... +│ ├── flutter_map... +│ ├── geolocator... +│ └── latlong2... +├── flutter_map_marker_cluster 1.3.6 +│ ├── flutter... +│ ├── flutter_map... +│ ├── flutter_map_marker_popup 6.1.2 +│ │ ├── animated_stack_widget 0.0.4 +│ │ │ └── flutter... +│ │ ├── flutter... +│ │ ├── flutter_map... +│ │ ├── latlong2... +│ │ └── provider 6.1.2 +│ │ ├── collection... +│ │ ├── flutter... +│ │ └── nested 1.0.0 +│ │ └── flutter... +│ └── latlong2... +├── flutter_polyline_points 2.1.0 +│ ├── flutter... +│ └── http... +├── flutter_riverpod 2.5.1 +│ ├── collection... +│ ├── flutter... +│ ├── meta... +│ ├── riverpod 2.5.1 +│ │ ├── collection... +│ │ ├── meta... +│ │ ├── stack_trace... +│ │ └── state_notifier... +│ └── state_notifier 1.0.0 +│ └── meta... +├── flutter_test 0.0.0 +│ ├── async... +│ ├── boolean_selector 2.1.1 +│ │ ├── source_span... +│ │ └── string_scanner... +│ ├── characters... +│ ├── clock... +│ ├── collection... +│ ├── fake_async 1.3.1 +│ │ ├── clock... +│ │ └── collection... +│ ├── flutter... +│ ├── leak_tracker 10.0.5 +│ │ ├── clock... +│ │ ├── collection... +│ │ ├── meta... +│ │ ├── path... +│ │ └── vm_service... +│ ├── leak_tracker_flutter_testing 3.0.5 +│ │ ├── flutter... +│ │ ├── leak_tracker... +│ │ ├── leak_tracker_testing... +│ │ ├── matcher... +│ │ └── meta... +│ ├── leak_tracker_testing 3.0.1 +│ │ ├── leak_tracker... +│ │ ├── matcher... +│ │ └── meta... +│ ├── matcher 0.12.16+1 +│ │ ├── async... +│ │ ├── meta... +│ │ ├── stack_trace... +│ │ ├── term_glyph... +│ │ └── test_api... +│ ├── material_color_utilities... +│ ├── meta... +│ ├── path... +│ ├── source_span 1.10.0 +│ │ ├── collection... +│ │ ├── path... +│ │ └── term_glyph... +│ ├── stack_trace 1.11.1 +│ │ └── path... +│ ├── stream_channel 2.1.2 +│ │ └── async... +│ ├── string_scanner 1.2.0 +│ │ └── source_span... +│ ├── term_glyph 1.2.1 +│ ├── test_api 0.7.2 +│ │ ├── async... +│ │ ├── boolean_selector... +│ │ ├── collection... +│ │ ├── meta... +│ │ ├── source_span... +│ │ ├── stack_trace... +│ │ ├── stream_channel... +│ │ ├── string_scanner... +│ │ └── term_glyph... +│ ├── vector_math... +│ └── vm_service 14.2.4 +├── flutter_typeahead 5.2.0 +│ ├── flutter... +│ ├── flutter_keyboard_visibility... +│ └── pointer_interceptor 0.10.1+2 +│ ├── flutter... +│ ├── flutter_web_plugins... +│ ├── pointer_interceptor_ios 0.10.1 +│ │ ├── flutter... +│ │ ├── plugin_platform_interface... +│ │ └── pointer_interceptor_platform_interface... +│ ├── pointer_interceptor_platform_interface 0.10.0+1 +│ │ ├── flutter... +│ │ └── plugin_platform_interface... +│ └── pointer_interceptor_web 0.10.2+1 +│ ├── flutter... +│ ├── flutter_web_plugins... +│ ├── plugin_platform_interface... +│ ├── pointer_interceptor_platform_interface... +│ └── web... +├── geojson_vi 2.2.5 +├── geolocator 10.1.1 +│ ├── flutter... +│ ├── geolocator_android 4.6.1 +│ │ ├── flutter... +│ │ ├── geolocator_platform_interface... +│ │ ├── meta... +│ │ └── uuid 4.4.2 +│ │ ├── crypto... +│ │ ├── fixnum 1.1.0 +│ │ ├── meta... +│ │ └── sprintf 7.0.0 +│ ├── geolocator_apple 2.3.7 +│ │ ├── flutter... +│ │ └── geolocator_platform_interface... +│ ├── geolocator_platform_interface 4.2.4 +│ │ ├── flutter... +│ │ ├── meta... +│ │ ├── plugin_platform_interface 2.1.8 +│ │ │ └── meta... +│ │ └── vector_math... +│ ├── geolocator_web 2.2.1 +│ │ ├── flutter... +│ │ ├── flutter_web_plugins... +│ │ └── geolocator_platform_interface... +│ └── geolocator_windows 0.2.3 +│ ├── flutter... +│ └── geolocator_platform_interface... +├── get 4.6.6 +│ └── flutter... +├── google_api_availability 5.0.0 +│ ├── flutter... +│ ├── google_api_availability_android 1.0.1 +│ │ ├── flutter... +│ │ └── google_api_availability_platform_interface... +│ └── google_api_availability_platform_interface 1.0.1 +│ ├── flutter... +│ ├── meta... +│ └── plugin_platform_interface... +├── google_fonts 6.2.1 +│ ├── crypto 3.0.5 +│ │ └── typed_data 1.3.2 +│ │ └── collection... +│ ├── flutter... +│ ├── http... +│ └── path_provider... +├── http 1.2.2 +│ ├── async... +│ ├── http_parser 4.0.2 +│ │ ├── collection... +│ │ ├── source_span... +│ │ ├── string_scanner... +│ │ └── typed_data... +│ ├── meta... +│ └── web... +├── image_gallery_saver 2.0.3 +│ └── flutter... +├── image_picker 1.1.2 +│ ├── flutter... +│ ├── image_picker_android 0.8.12+12 +│ │ ├── flutter... +│ │ ├── flutter_plugin_android_lifecycle... +│ │ └── image_picker_platform_interface... +│ ├── image_picker_for_web 3.0.5 +│ │ ├── flutter... +│ │ ├── flutter_web_plugins... +│ │ ├── image_picker_platform_interface... +│ │ ├── mime 1.0.5 +│ │ └── web... +│ ├── image_picker_ios 0.8.12 +│ │ ├── flutter... +│ │ └── image_picker_platform_interface... +│ ├── image_picker_linux 0.2.1+1 +│ │ ├── file_selector_linux 0.9.2+1 +│ │ │ ├── cross_file... +│ │ │ ├── file_selector_platform_interface... +│ │ │ └── flutter... +│ │ ├── file_selector_platform_interface 2.6.2 +│ │ │ ├── cross_file... +│ │ │ ├── flutter... +│ │ │ ├── http... +│ │ │ └── plugin_platform_interface... +│ │ ├── flutter... +│ │ └── image_picker_platform_interface... +│ ├── image_picker_macos 0.2.1+1 +│ │ ├── file_selector_macos 0.9.4 +│ │ │ ├── cross_file... +│ │ │ ├── file_selector_platform_interface... +│ │ │ └── flutter... +│ │ ├── file_selector_platform_interface... +│ │ ├── flutter... +│ │ └── image_picker_platform_interface... +│ ├── image_picker_platform_interface 2.10.0 +│ │ ├── cross_file 0.3.4+2 +│ │ │ ├── meta... +│ │ │ └── web... +│ │ ├── flutter... +│ │ ├── http... +│ │ └── plugin_platform_interface... +│ └── image_picker_windows 0.2.1+1 +│ ├── file_selector_platform_interface... +│ ├── file_selector_windows 0.9.3+2 +│ │ ├── cross_file... +│ │ ├── file_selector_platform_interface... +│ │ └── flutter... +│ ├── flutter... +│ └── image_picker_platform_interface... +├── intl 0.19.0 +│ ├── clock... +│ ├── meta... +│ └── path... +├── keyboard_dismisser 3.0.0 +│ └── flutter... +├── latlong2 0.9.1 +│ └── intl... +├── logging 1.2.0 +├── material_design_icons_flutter 7.0.7296 +│ └── flutter... +├── meta 1.15.0 +├── modal_bottom_sheet 3.0.0 +│ └── flutter... +├── package_info_plus 8.0.2 +│ ├── clock 1.1.1 +│ ├── ffi... +│ ├── flutter... +│ ├── flutter_web_plugins 0.0.0 +│ │ ├── characters... +│ │ ├── collection... +│ │ ├── flutter... +│ │ ├── material_color_utilities... +│ │ ├── meta... +│ │ └── vector_math... +│ ├── http... +│ ├── meta... +│ ├── package_info_plus_platform_interface 3.0.1 +│ │ ├── flutter... +│ │ ├── meta... +│ │ └── plugin_platform_interface... +│ ├── path... +│ ├── web 1.0.0 +│ └── win32... +├── path_provider 2.1.4 +│ ├── flutter... +│ ├── path_provider_android 2.2.10 +│ │ ├── flutter... +│ │ └── path_provider_platform_interface... +│ ├── path_provider_foundation 2.4.0 +│ │ ├── flutter... +│ │ └── path_provider_platform_interface... +│ ├── path_provider_linux 2.2.1 +│ │ ├── ffi... +│ │ ├── flutter... +│ │ ├── path... +│ │ ├── path_provider_platform_interface... +│ │ └── xdg_directories 1.0.4 +│ │ ├── meta... +│ │ └── path... +│ ├── path_provider_platform_interface 2.1.2 +│ │ ├── flutter... +│ │ ├── platform 3.1.5 +│ │ └── plugin_platform_interface... +│ └── path_provider_windows 2.3.0 +│ ├── ffi... +│ ├── flutter... +│ ├── path... +│ └── path_provider_platform_interface... +├── permission_handler 11.3.1 +│ ├── flutter... +│ ├── meta... +│ ├── permission_handler_android 12.0.12 +│ │ ├── flutter... +│ │ └── permission_handler_platform_interface... +│ ├── permission_handler_apple 9.4.5 +│ │ ├── flutter... +│ │ └── permission_handler_platform_interface... +│ ├── permission_handler_html 0.1.3+1 +│ │ ├── flutter... +│ │ ├── flutter_web_plugins... +│ │ ├── permission_handler_platform_interface... +│ │ └── web... +│ ├── permission_handler_platform_interface 4.2.2 +│ │ ├── flutter... +│ │ ├── meta... +│ │ └── plugin_platform_interface... +│ └── permission_handler_windows 0.2.1 +│ ├── flutter... +│ └── permission_handler_platform_interface... +├── positioned_tap_detector_2 1.0.4 +│ └── flutter... +├── proj4dart 2.1.0 +│ ├── meta... +│ ├── mgrs_dart 2.0.0 +│ │ └── unicode 0.3.1 +│ │ └── lists 1.0.1 +│ │ └── meta... +│ └── wkt_parser 2.0.0 +├── qr_code_scanner 1.0.1 +│ ├── flutter... +│ ├── flutter_web_plugins... +│ └── js 0.6.7 +│ └── meta... +├── rename 3.0.2 +│ ├── args... +│ ├── logger... +│ └── path... +├── shared_preferences 2.3.2 +│ ├── flutter... +│ ├── shared_preferences_android 2.3.1 +│ │ ├── flutter... +│ │ └── shared_preferences_platform_interface... +│ ├── shared_preferences_foundation 2.5.2 +│ │ ├── flutter... +│ │ └── shared_preferences_platform_interface... +│ ├── shared_preferences_linux 2.4.1 +│ │ ├── file 7.0.0 +│ │ │ ├── meta... +│ │ │ └── path... +│ │ ├── flutter... +│ │ ├── path... +│ │ ├── path_provider_linux... +│ │ ├── path_provider_platform_interface... +│ │ └── shared_preferences_platform_interface... +│ ├── shared_preferences_platform_interface 2.4.1 +│ │ ├── flutter... +│ │ └── plugin_platform_interface... +│ ├── shared_preferences_web 2.4.2 +│ │ ├── flutter... +│ │ ├── flutter_web_plugins... +│ │ ├── shared_preferences_platform_interface... +│ │ └── web... +│ └── shared_preferences_windows 2.4.1 +│ ├── file... +│ ├── flutter... +│ ├── path... +│ ├── path_provider_platform_interface... +│ ├── path_provider_windows... +│ └── shared_preferences_platform_interface... +├── sqflite 2.3.3+1 +│ ├── flutter... +│ ├── path 1.9.0 +│ └── sqflite_common 2.5.4+2 +│ ├── meta... +│ ├── path... +│ └── synchronized 3.2.0 +├── timeline_tile 2.0.0 +│ └── flutter... +├── timezone 0.9.4 +│ └── path... +├── transparent_image 2.0.1 +├── tuple 2.0.2 +├── url_launcher 6.3.0 +│ ├── flutter... +│ ├── url_launcher_android 6.3.9 +│ │ ├── flutter... +│ │ └── url_launcher_platform_interface... +│ ├── url_launcher_ios 6.3.1 +│ │ ├── flutter... +│ │ └── url_launcher_platform_interface... +│ ├── url_launcher_linux 3.2.0 +│ │ ├── flutter... +│ │ └── url_launcher_platform_interface... +│ ├── url_launcher_macos 3.2.0 +│ │ ├── flutter... +│ │ └── url_launcher_platform_interface... +│ ├── url_launcher_platform_interface 2.3.2 +│ │ ├── flutter... +│ │ └── plugin_platform_interface... +│ ├── url_launcher_web 2.3.3 +│ │ ├── flutter... +│ │ ├── flutter_web_plugins... +│ │ ├── url_launcher_platform_interface... +│ │ └── web... +│ └── url_launcher_windows 3.1.2 +│ ├── flutter... +│ └── url_launcher_platform_interface... +├── vector_math 2.1.4 +├── webview_flutter 4.8.0 +│ ├── flutter... +│ ├── webview_flutter_android 3.16.6 +│ │ ├── flutter... +│ │ └── webview_flutter_platform_interface... +│ ├── webview_flutter_platform_interface 2.10.0 +│ │ ├── flutter... +│ │ ├── meta... +│ │ └── plugin_platform_interface... +│ └── webview_flutter_wkwebview 3.15.0 +│ ├── flutter... +│ ├── path... +│ └── webview_flutter_platform_interface... +└── win32 5.5.4 + └── ffi 2.1.3 diff --git a/flutter_launcher_icons.yaml b/flutter_launcher_icons.yaml new file mode 100644 index 0000000..fd81304 --- /dev/null +++ b/flutter_launcher_icons.yaml @@ -0,0 +1,5 @@ +flutter_launcher_icons: + android: "launcher_icon" + ios: true + image_path: "assets/images/appicon.png" + min_sdk_android: 21 # android min sdk min:16, default 21 diff --git a/gifunavi/.gitignore b/gifunavi/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/gifunavi/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/gifunavi/build.gradle.kts b/gifunavi/build.gradle.kts new file mode 100644 index 0000000..c692702 --- /dev/null +++ b/gifunavi/build.gradle.kts @@ -0,0 +1,40 @@ +plugins { + id("com.android.application") +} + +android { + namespace = "com.dvox.gifunavi" + compileSdk = 34 + + defaultConfig { + applicationId = "com.dvox.gifunavi" + minSdk = 21 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } +} + +dependencies { + + implementation("com.android.support:appcompat-v7:28.0.0") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("com.android.support.test:runner:1.0.2") + androidTestImplementation("com.android.support.test.espresso:espresso-core:3.0.2") +} \ No newline at end of file diff --git a/gifunavi/proguard-rules.pro b/gifunavi/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/gifunavi/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/gifunavi/src/androidTest/java/com/dvox/gifunavi/ExampleInstrumentedTest.java b/gifunavi/src/androidTest/java/com/dvox/gifunavi/ExampleInstrumentedTest.java new file mode 100644 index 0000000..559af56 --- /dev/null +++ b/gifunavi/src/androidTest/java/com/dvox/gifunavi/ExampleInstrumentedTest.java @@ -0,0 +1,25 @@ +package com.dvox.gifunavi; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.dvox.gifunavi", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/gifunavi/src/main/AndroidManifest.xml b/gifunavi/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3189f62 --- /dev/null +++ b/gifunavi/src/main/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/gifunavi/src/main/res/drawable-v24/ic_launcher_foreground.xml b/gifunavi/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/gifunavi/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/gifunavi/src/main/res/drawable/ic_launcher_background.xml b/gifunavi/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/gifunavi/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gifunavi/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/gifunavi/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/gifunavi/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/gifunavi/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/gifunavi/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/gifunavi/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/gifunavi/src/main/res/mipmap-hdpi/ic_launcher.webp b/gifunavi/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/gifunavi/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/gifunavi/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/gifunavi/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/gifunavi/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/gifunavi/src/main/res/mipmap-mdpi/ic_launcher.webp b/gifunavi/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/gifunavi/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/gifunavi/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/gifunavi/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/gifunavi/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/gifunavi/src/main/res/mipmap-xhdpi/ic_launcher.webp b/gifunavi/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/gifunavi/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/gifunavi/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/gifunavi/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/gifunavi/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/gifunavi/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/gifunavi/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/gifunavi/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/gifunavi/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/gifunavi/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/gifunavi/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/gifunavi/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/gifunavi/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/gifunavi/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/gifunavi/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/gifunavi/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/gifunavi/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/gifunavi/src/main/res/values-night/themes.xml b/gifunavi/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..e4b5f5c --- /dev/null +++ b/gifunavi/src/main/res/values-night/themes.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/gifunavi/src/main/res/values/colors.xml b/gifunavi/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/gifunavi/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/gifunavi/src/main/res/values/strings.xml b/gifunavi/src/main/res/values/strings.xml new file mode 100644 index 0000000..e03dff2 --- /dev/null +++ b/gifunavi/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + gifunavi + \ No newline at end of file diff --git a/gifunavi/src/main/res/values/themes.xml b/gifunavi/src/main/res/values/themes.xml new file mode 100644 index 0000000..8cd4042 --- /dev/null +++ b/gifunavi/src/main/res/values/themes.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/gifunavi/src/test/java/com/dvox/gifunavi/ExampleUnitTest.java b/gifunavi/src/test/java/com/dvox/gifunavi/ExampleUnitTest.java new file mode 100644 index 0000000..208582b --- /dev/null +++ b/gifunavi/src/test/java/com/dvox/gifunavi/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.dvox.gifunavi; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..1becba2 --- /dev/null +++ b/gradlew @@ -0,0 +1 @@ +404: Not Found \ No newline at end of file diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..1becba2 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1 @@ +404: Not Found \ No newline at end of file diff --git a/ios/.gitignore b/ios/.gitignore index ad322bc..7a7f987 100644 --- a/ios/.gitignore +++ b/ios/.gitignore @@ -1,34 +1,34 @@ -**/dgph -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/ephemeral/ -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 9625e10..7c56964 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index dfd2626..ec97fc6 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1,2 +1,2 @@ #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index a97381a..c4855bf 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1,2 +1,2 @@ #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/GifuNavigation.mobileprovision b/ios/GifuNavigation.mobileprovision new file mode 100644 index 0000000..c523c3f Binary files /dev/null and b/ios/GifuNavigation.mobileprovision differ diff --git a/ios/Podfile b/ios/Podfile index 6f71b24..d97f17e 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '12.0' +# platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -32,75 +32,13 @@ target 'Runner' do use_modular_headers! flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end end -# post_install do |installer| -# installer.pods_project.targets.each do |target| -# flutter_additional_ios_build_settings(target) -# end -# end - post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) - - # Start of the permission_handler configuration - target.build_configurations.each do |config| - - # You can enable the permissions needed here. For example to enable camera - # permission, just remove the `#` character in front so it looks like this: - # - # ## dart: PermissionGroup.camera - # 'PERMISSION_CAMERA=1' - # - # Preprocessor definitions can be found in: https://github.com/Baseflow/flutter-permission-handler/blob/master/permission_handler_apple/ios/Classes/PermissionHandlerEnums.h - config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ - '$(inherited)', - - ## dart: PermissionGroup.calendar - # 'PERMISSION_EVENTS=1', - - ## dart: PermissionGroup.reminders - # 'PERMISSION_REMINDERS=1', - - ## dart: PermissionGroup.contacts - # 'PERMISSION_CONTACTS=1', - - ## dart: PermissionGroup.camera - # 'PERMISSION_CAMERA=1', - - ## dart: PermissionGroup.microphone - # 'PERMISSION_MICROPHONE=1', - - ## dart: PermissionGroup.speech - # 'PERMISSION_SPEECH_RECOGNIZER=1', - - ## dart: PermissionGroup.photos - # 'PERMISSION_PHOTOS=1', - - ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse] - 'PERMISSION_LOCATION=1' - - ## dart: PermissionGroup.notification - # 'PERMISSION_NOTIFICATIONS=1', - - ## dart: PermissionGroup.mediaLibrary - # 'PERMISSION_MEDIA_LIBRARY=1', - - ## dart: PermissionGroup.sensors - # 'PERMISSION_SENSORS=1', - - ## dart: PermissionGroup.bluetooth - # 'PERMISSION_BLUETOOTH=1', - - ## dart: PermissionGroup.appTrackingTransparency - # 'PERMISSION_APP_TRACKING_TRANSPARENCY=1', - - ## dart: PermissionGroup.criticalAlerts - # 'PERMISSION_CRITICAL_ALERTS=1' - ] - - end - # End of the permission_handler configuration end end diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 90b2601..2531a8c 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -3,71 +3,71 @@ PODS: - Flutter - connectivity_plus (0.0.1): - Flutter - - ReachabilitySwift + - FlutterMacOS - Flutter (1.0.0) - flutter_compass (0.0.1): - Flutter - flutter_keyboard_visibility (0.0.1): - Flutter - - FMDB (2.7.5): - - FMDB/standard (= 2.7.5) - - FMDB/standard (2.7.5) - geolocator_apple (1.2.0): - Flutter - - google_maps_flutter_ios (0.0.1): + - image_gallery_saver (2.0.2): - Flutter - - GoogleMaps (< 8.0) - - GoogleMaps (6.2.1): - - GoogleMaps/Maps (= 6.2.1) - - GoogleMaps/Base (6.2.1) - - GoogleMaps/Maps (6.2.1): - - GoogleMaps/Base - image_picker_ios (0.0.1): - Flutter - - isar_flutter_libs (1.0.0): + - MTBBarcodeScanner (5.0.11) + - package_info_plus (0.4.5): - Flutter - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - - permission_handler_apple (9.1.1): + - permission_handler_apple (9.3.0): - Flutter - - ReachabilitySwift (5.0.0) + - pointer_interceptor_ios (0.0.1): + - Flutter + - qr_code_scanner (0.2.0): + - Flutter + - MTBBarcodeScanner - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS - sqflite (0.0.3): - Flutter - - FMDB (>= 2.7.5) + - FlutterMacOS - url_launcher_ios (0.0.1): - Flutter + - webview_flutter_wkwebview (0.0.1): + - Flutter + - FlutterMacOS DEPENDENCIES: - camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`) - - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) + - connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`) - Flutter (from `Flutter`) - flutter_compass (from `.symlinks/plugins/flutter_compass/ios`) - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`) - geolocator_apple (from `.symlinks/plugins/geolocator_apple/ios`) - - google_maps_flutter_ios (from `.symlinks/plugins/google_maps_flutter_ios/ios`) + - image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - - isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) + - pointer_interceptor_ios (from `.symlinks/plugins/pointer_interceptor_ios/ios`) + - qr_code_scanner (from `.symlinks/plugins/qr_code_scanner/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - - sqflite (from `.symlinks/plugins/sqflite/ios`) + - sqflite (from `.symlinks/plugins/sqflite/darwin`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`) SPEC REPOS: trunk: - - FMDB - - GoogleMaps - - ReachabilitySwift + - MTBBarcodeScanner EXTERNAL SOURCES: camera_avfoundation: :path: ".symlinks/plugins/camera_avfoundation/ios" connectivity_plus: - :path: ".symlinks/plugins/connectivity_plus/ios" + :path: ".symlinks/plugins/connectivity_plus/darwin" Flutter: :path: Flutter flutter_compass: @@ -76,42 +76,49 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_keyboard_visibility/ios" geolocator_apple: :path: ".symlinks/plugins/geolocator_apple/ios" - google_maps_flutter_ios: - :path: ".symlinks/plugins/google_maps_flutter_ios/ios" + image_gallery_saver: + :path: ".symlinks/plugins/image_gallery_saver/ios" image_picker_ios: :path: ".symlinks/plugins/image_picker_ios/ios" - isar_flutter_libs: - :path: ".symlinks/plugins/isar_flutter_libs/ios" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" permission_handler_apple: :path: ".symlinks/plugins/permission_handler_apple/ios" + pointer_interceptor_ios: + :path: ".symlinks/plugins/pointer_interceptor_ios/ios" + qr_code_scanner: + :path: ".symlinks/plugins/qr_code_scanner/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" sqflite: - :path: ".symlinks/plugins/sqflite/ios" + :path: ".symlinks/plugins/sqflite/darwin" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" + webview_flutter_wkwebview: + :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin" SPEC CHECKSUMS: - camera_avfoundation: 3125e8cd1a4387f6f31c6c63abb8a55892a9eeeb - connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a - Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + camera_avfoundation: dd002b0330f4981e1bbcb46ae9b62829237459a4 + connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_compass: cbbd285cea1584c7ac9c4e0c3e1f17cbea55e855 flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069 - FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - geolocator_apple: cc556e6844d508c95df1e87e3ea6fa4e58c50401 - google_maps_flutter_ios: abdac20d6ce8931f6ebc5f46616df241bfaa2cfd - GoogleMaps: 20d7b12be49a14287f797e88e0e31bc4156aaeb4 - image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5 - isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073 - path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 - permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 - ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 - shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 - sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a - url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4 + geolocator_apple: 6cbaf322953988e009e5ecb481f07efece75c450 + image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb + image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 + MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb + package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 + pointer_interceptor_ios: 508241697ff0947f853c061945a8b822463947c1 + qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe + webview_flutter_wkwebview: 0982481e3d9c78fd5c6f62a002fcd24fc791f1e4 -PODFILE CHECKSUM: 7a34d5e980f9e05ecf4e24c79da64ca020615638 +PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 -COCOAPODS: 1.12.1 +COCOAPODS: 1.15.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index f3465f5..986ec01 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -8,14 +8,26 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 2EAA11F0595B6B1A6B6ADED8 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53C8E795FF5969F8C7B6A237 /* Pods_RunnerTests.framework */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - EAFC0FFB27C8DC9071E67D5D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 853FC5087A11FEE54BE443C8 /* Pods_Runner.framework */; }; + B6A4962F862B554C98AD7E0A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F31FDD2E828C96A459E4AA85 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -30,14 +42,18 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 13E5CD9612AF4CC8B90A74C3 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 53C8E795FF5969F8C7B6A237 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 853FC5087A11FEE54BE443C8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 84738EC4DA98A3780DDAB81F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 89205C63F97F88BDB459168E /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 89EEC485D24E5B1BC0DD7B8E /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -45,8 +61,10 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - BEC4F75C6B655CA90D5B5E18 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - F1D3FD8F51C2EAF6C3A7EE25 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + AC42218FD0A0DB137D157E93 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + D773E9161AA2211A3E2B94DD /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + DA00EBAA91366A0B5BFDAE96 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + F31FDD2E828C96A459E4AA85 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,21 +72,36 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - EAFC0FFB27C8DC9071E67D5D /* Pods_Runner.framework in Frameworks */, + B6A4962F862B554C98AD7E0A /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CEDA9491D04A70CE2A58DF32 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2EAA11F0595B6B1A6B6ADED8 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 6C3C11D7BE49E5BED7989712 /* Pods */ = { + 331C8082294A63A400263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( - BEC4F75C6B655CA90D5B5E18 /* Pods-Runner.debug.xcconfig */, - 13E5CD9612AF4CC8B90A74C3 /* Pods-Runner.release.xcconfig */, - F1D3FD8F51C2EAF6C3A7EE25 /* Pods-Runner.profile.xcconfig */, + 331C807B294A618700263BE5 /* RunnerTests.swift */, ); - path = Pods; + path = RunnerTests; + sourceTree = ""; + }; + 3806CD679A8B2BB869428B93 /* Frameworks */ = { + isa = PBXGroup; + children = ( + F31FDD2E828C96A459E4AA85 /* Pods_Runner.framework */, + 53C8E795FF5969F8C7B6A237 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; sourceTree = ""; }; 9740EEB11CF90186004384FC /* Flutter */ = { @@ -88,8 +121,9 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - 6C3C11D7BE49E5BED7989712 /* Pods */, - E6F2437A5B5AAF08D2998197 /* Frameworks */, + 331C8082294A63A400263BE5 /* RunnerTests */, + B9F40AE3939A8E59418B2C25 /* Pods */, + 3806CD679A8B2BB869428B93 /* Frameworks */, ); sourceTree = ""; }; @@ -97,6 +131,7 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -116,30 +151,54 @@ path = Runner; sourceTree = ""; }; - E6F2437A5B5AAF08D2998197 /* Frameworks */ = { + B9F40AE3939A8E59418B2C25 /* Pods */ = { isa = PBXGroup; children = ( - 853FC5087A11FEE54BE443C8 /* Pods_Runner.framework */, + DA00EBAA91366A0B5BFDAE96 /* Pods-Runner.debug.xcconfig */, + D773E9161AA2211A3E2B94DD /* Pods-Runner.release.xcconfig */, + 84738EC4DA98A3780DDAB81F /* Pods-Runner.profile.xcconfig */, + AC42218FD0A0DB137D157E93 /* Pods-RunnerTests.debug.xcconfig */, + 89205C63F97F88BDB459168E /* Pods-RunnerTests.release.xcconfig */, + 89EEC485D24E5B1BC0DD7B8E /* Pods-RunnerTests.profile.xcconfig */, ); - name = Frameworks; + path = Pods; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + CD76B1A18202CBE77CBE3DCF /* [CP] Check Pods Manifest.lock */, + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + CEDA9491D04A70CE2A58DF32 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 6B25D2836FC6A82461E36025 /* [CP] Check Pods Manifest.lock */, + 7EC19810B524B833F58A16B8 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ED4E40E6FE1C0A73CAC736F8 /* [CP] Embed Pods Frameworks */, - BC610379D726847A0183FBD1 /* [CP] Copy Pods Resources */, + 3CF3DF1DB3C3E48099A705D1 /* [CP] Embed Pods Frameworks */, + 9D3C0722FFFB9D74548C2A72 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -156,9 +215,14 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; LastSwiftMigration = 1100; @@ -179,11 +243,19 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -214,7 +286,24 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 6B25D2836FC6A82461E36025 /* [CP] Check Pods Manifest.lock */ = { + 3CF3DF1DB3C3E48099A705D1 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 7EC19810B524B833F58A16B8 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -251,7 +340,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - BC610379D726847A0183FBD1 /* [CP] Copy Pods Resources */ = { + 9D3C0722FFFB9D74548C2A72 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -268,26 +357,39 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; showEnvVarsInLog = 0; }; - ED4E40E6FE1C0A73CAC736F8 /* [CP] Embed Pods Frameworks */ = { + CD76B1A18202CBE77CBE3DCF /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -299,6 +401,14 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -323,6 +433,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -352,6 +463,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -360,7 +472,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -376,13 +488,16 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = D5SL68ATB9; + DEVELOPMENT_TEAM = UMNEWT25JR; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "岐阜ナビ"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 4.8.19; PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunavi; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -391,10 +506,61 @@ }; name = Profile; }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AC42218FD0A0DB137D157E93 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.gifunavi.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 89205C63F97F88BDB459168E /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.gifunavi.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 89EEC485D24E5B1BC0DD7B8E /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.gifunavi.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -424,6 +590,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -438,7 +605,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -450,6 +617,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -479,6 +647,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -487,7 +656,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -505,13 +674,16 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = D5SL68ATB9; + DEVELOPMENT_TEAM = UMNEWT25JR; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "岐阜ナビ"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 4.8.19; PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunavi; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -528,13 +700,16 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = D5SL68ATB9; + DEVELOPMENT_TEAM = UMNEWT25JR; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "岐阜ナビ"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 4.8.19; PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunavi; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -546,6 +721,16 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index c4b79bd..919434a 100644 --- a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -1,7 +1,7 @@ - - - - - + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist index fc6bf80..18d9810 100644 --- a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -1,8 +1,8 @@ - - - - - IDEDidComputeMac32BitWarning - - - + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings index af0309c..f9b0d7c 100644 --- a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -1,8 +1,8 @@ - - - - - PreviewsEnabled - - - + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a6b826d..8e3ca5d 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ + + + + - - - - IDEDidComputeMac32BitWarning - - - + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings index af0309c..f9b0d7c 100644 --- a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -1,8 +1,8 @@ - - - - - PreviewsEnabled - - - + + + + + PreviewsEnabled + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 143ef75..6266644 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,15 +1,13 @@ -import UIKit -import Flutter -import GoogleMaps - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GMSServices.provideAPIKey("AIzaSyAUBI1ablMKuJwGj2-kSuEhvYxvB1A-mOE") - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} +import Flutter +import UIKit + +@main +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json index 1950fd8..d36b1fa 100644 --- a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,122 +1,122 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json index d08a4de..0bedcf2 100644 --- a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -1,23 +1,23 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md index 65a94b5..89c2725 100644 --- a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -1,5 +1,5 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard index 497371e..f2e259c 100644 --- a/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -1,37 +1,37 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard index bbb83ca..f3c2851 100644 --- a/ios/Runner/Base.lproj/Main.storyboard +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -1,26 +1,26 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index de6d341..5bf9375 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -24,28 +24,32 @@ ???? CFBundleVersion $(FLUTTER_BUILD_NUMBER) + LSApplicationCategoryType + LSRequiresIPhoneOS NSCameraUsageDescription - 写真撮影のためカメラにアクセスします + 岐阜ナビはチェックポイントで撮影した写真を写真ライブラリに保存し、通過記録を保持し、競技結果として提出することができます。 NSLocationAlwaysAndWhenInUseUsageDescription - このアプリでは、位置情報の収集を行います。 + 岐阜ナビはアプリが閉じられているときでも位置情報へのアクセスが必要です。これにより、走行履歴の記録ができ、レビュー時の参考にすることができます。 NSLocationAlwaysUsageDescription - このアプリでは、バックグラウンドで位置情報を収集します。 + このアプリではバックグラウンドで位置情報にアクセスします。 NSLocationWhenInUseUsageDescription - このアプリでは、開始時点で位置情報を収集します。 + このアプリはチェックポイントへのチェックインや走行履歴を記録するために、位置情報にアクセスします。 NSMicrophoneUsageDescription - プロフィールに動画を投稿してください。 + このアプリではカメラは使用しますが、マイクの使用は当面行いません。 NSPhotoLibraryUsageDescription - 写真ライブラリへのアクセス警告 + 撮影した写真はデバイスのアルバムに保存されます。これにより、不具合時の通過記録を安全に担保することができます。 + UIApplicationSupportsIndirectInputEvents + + UIBackgroundModes + + location + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main - NSCameraUsageDescription - ロゲイニングのゴールでは写真撮影が必要です。 - NSMicrophoneUsageDescription - 収録音声の保存が必要です。 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait @@ -63,7 +67,5 @@ io.flutter.embedded_views_preview - UIApplicationSupportsIndirectInputEvents - diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h index fae207f..308a2a5 100644 --- a/ios/Runner/Runner-Bridging-Header.h +++ b/ios/Runner/Runner-Bridging-Header.h @@ -1 +1 @@ -#import "GeneratedPluginRegistrant.h" +#import "GeneratedPluginRegistrant.h" diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/lib/data/location_data.dart b/lib/data/location_data.dart index 608819d..a7e8d6f 100644 --- a/lib/data/location_data.dart +++ b/lib/data/location_data.dart @@ -1,4 +1,5 @@ // ignore: non_constant_identifier_names +// 不要 String location_line_date = """ { "type": "FeatureCollection", diff --git a/lib/main.dart b/lib/main.dart index 3b090fc..f1a4da6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,44 +1,600 @@ - +import 'dart:async'; +import 'dart:io'; +//import 'dart:convert'; +//import 'dart:developer'; +import 'package:gifunavi/model/gps_data.dart'; +//import 'package:gifunavi/pages/home/home_page.dart'; +import 'package:gifunavi/utils/database_gps.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart'; +//import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart'; +import 'package:geolocator/geolocator.dart'; import 'package:get/get.dart'; -import 'package:rogapp/pages/index/index_binding.dart'; -import 'package:rogapp/routes/app_pages.dart'; -import 'package:rogapp/utils/string_values.dart'; +//import 'package:vm_service/vm_service.dart'; +//import 'package:dart_vm_info/dart_vm_info.dart'; +import 'package:timezone/data/latest.dart' as tz; + +import 'package:gifunavi/pages/settings/settings_controller.dart'; + +import 'package:gifunavi/pages/destination/destination_controller.dart'; +import 'package:gifunavi/pages/index/index_binding.dart'; +import 'package:gifunavi/pages/index/index_controller.dart'; +import 'package:gifunavi/routes/app_pages.dart'; +import 'package:gifunavi/utils/location_controller.dart'; +import 'package:gifunavi/utils/string_values.dart'; +import 'package:gifunavi/widgets/debug_widget.dart'; import 'package:shared_preferences/shared_preferences.dart'; +// import 'package:is_lock_screen/is_lock_screen.dart'; +//import 'package:gifunavi/services/device_info_service.dart'; +import 'package:gifunavi/services/error_service.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +//import 'dart:async'; +//import 'package:get/get.dart'; +import 'package:flutter/services.dart'; -String? userToken; +import 'package:permission_handler/permission_handler.dart'; + +import 'pages/permission/permission.dart'; +import 'package:gifunavi/services/api_service.dart'; + +import 'package:gifunavi/provider/cached_tile_provider.dart'; +import 'package:gifunavi/pages/entry/entry_controller.dart'; +import 'package:gifunavi/pages/team/team_controller.dart'; + +Map deviceInfo = {}; + +/* +void saveGameState() async { + DestinationController destinationController = + Get.find(); + SharedPreferences pref = await SharedPreferences.getInstance(); + pref.setBool("is_in_rog", destinationController.isInRog.value); + pref.setBool( + "rogaining_counted", destinationController.rogainingCounted.value); + pref.setBool("ready_for_goal", DestinationController.ready_for_goal); +} + */ + +// 現在のユーザーのIDも一緒に保存するようにします。 +void saveGameState() async { + DestinationController destinationController = + Get.find(); + IndexController indexController = Get.find(); + SharedPreferences pref = await SharedPreferences.getInstance(); + debugPrint("indexController.currentUser = ${indexController.currentUser}"); + if(indexController.currentUser.isNotEmpty) { + pref.setInt("user_id", indexController.currentUser[0]["user"]["id"]); + }else{ + debugPrint("User is empty...."); + } + pref.setBool("is_in_rog", destinationController.isInRog.value); + pref.setBool( + "rogaining_counted", destinationController.rogainingCounted.value); + pref.setBool("ready_for_goal", DestinationController.ready_for_goal); +} + +/* +void restoreGame() async { + SharedPreferences pref = await SharedPreferences.getInstance(); + DestinationController destinationController = + Get.find(); + destinationController.skipGps = false; + destinationController.isInRog.value = pref.getBool("is_in_rog") ?? false; + destinationController.rogainingCounted.value = + pref.getBool("rogaining_counted") ?? false; + DestinationController.ready_for_goal = + pref.getBool("ready_for_goal") ?? false; + //print( + // "--restored -- destinationController.isInRog.value ${pref.getBool("is_in_rog")} -- ${pref.getBool("rogaining_counted")}"); +} + */ + +void restoreGame() async { + SharedPreferences pref = await SharedPreferences.getInstance(); + IndexController indexController = Get.find(); + int? savedUserId = pref.getInt("user_id"); + //int? currUserId = indexController.currentUser[0]['user']['id']; + //debugPrint("savedUserId=${savedUserId}, currentUser=${currUserId}"); + if (indexController.currentUser.isNotEmpty && + indexController.currentUser[0]["user"]["id"] == savedUserId) { + DestinationController destinationController = + Get.find(); + destinationController.skipGps = false; + destinationController.isInRog.value = pref.getBool("is_in_rog") ?? false; + destinationController.rogainingCounted.value = + pref.getBool("rogaining_counted") ?? false; + DestinationController.ready_for_goal = + pref.getBool("ready_for_goal") ?? false; + await Get.putAsync(() => ApiService().init()); + } +} void main() async { - WidgetsFlutterBinding.ensureInitialized(); - - await FlutterMapTileCaching.initialise(); - final StoreDirectory instanceA = FMTC.instance('OpenStreetMap (A)'); - await instanceA.manage.createAsync(); - await instanceA.metadata.addAsync( - key: 'sourceURL', - value: 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', - ); - await instanceA.metadata.addAsync( - key: 'validDuration', - value: '14', - ); - await instanceA.metadata.addAsync( - key: 'behaviour', - value: 'cacheFirst', - ); - runApp(const MyApp()); + Get.put(LocationController()); + /* + Get.put(ApiService()); + Get.put(TeamController()); + Get.put(EntryController()); + Get.put(IndexController()); + */ + /* + await FlutterMapTileCaching.initialise(); + + final StoreDirectory instanceA = FMTC.instance('OpenStreetMap (A)'); + await instanceA.manage.createAsync(); + await instanceA.metadata.addAsync( + key: 'sourceURL', + value: 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', + ); + await instanceA.metadata.addAsync( + key: 'validDuration', + value: '14', + ); + await instanceA.metadata.addAsync( + key: 'behaviour', + value: 'cacheFirst', + ); +*/ +// 新しいキャッシュプロバイダーの初期化 + await CacheProvider.initialize(); + + // 使用不可 + //deviceInfo = await DeviceInfoService.getDeviceInfo(); + + FlutterError.onError = (FlutterErrorDetails details) { + FlutterError.presentError(details); + Get.log('Flutter error: ${details.exception}'); + Get.log('Stack trace: ${details.stack}'); + ErrorService.reportError(details.exception, details.stack ?? StackTrace.current, deviceInfo, LogManager().operationLogs); + }; + + //Get.put(LocationController()); + + //await PermissionController.checkAndRequestPermissions(); + //requestLocationPermission(); + + + + // startMemoryMonitoring(); // 2024-4-8 Akira: メモリ使用量のチェックを開始 See #2810 + Get.put(SettingsController()); // これを追加 + + + /* + runZonedGuarded(() { + runApp(const ProviderScope(child: MyApp())); + }, (error, stackTrace) { + ErrorService.reportError(error, stackTrace, deviceInfo); + }); + */ + + FlutterError.onError = (FlutterErrorDetails details) { + FlutterError.presentError(details); + debugPrint('Flutter error: ${details.exception}'); + debugPrint('Stack trace: ${details.stack}'); + }; + + try { + tz.initializeTimeZones(); + + // ApiServiceを初期化 + //await Get.putAsync(() => ApiService().init()); + await initServices(); + + runApp(const ProviderScope(child: MyApp())); + //runApp(HomePage()); // MyApp()からHomePage()に変更 + //runApp(const MyApp()); + }catch(e, stackTrace){ + print('Error during initialization: $e'); + print('Stack trace: $stackTrace'); + } +} + +Future initServices() async { + print('Starting services ...'); + try { + await Get.putAsync(() => ApiService().init()); + print('All services started...'); + }catch(e){ + print('Error initializing ApiService: $e'); + } + + try { + Get.put(SettingsController()); + print('SettingsController initialized successfully'); + } catch (e) { + print('Error initializing SettingsController: $e'); + } + + print('All services started...'); + +} + +Future requestLocationPermission() async { + try { + final status = await Permission.locationAlways.request(); + if (status == PermissionStatus.granted) { + print('Location permission granted'); + } else { + print('Location permission denied'); + //await showLocationPermissionDeniedDialog(); // 追加 + } + } catch (e) { + print('Error requesting location permission: $e'); + } } -class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); + +// メモリ使用量の解説:https://qiita.com/hukusuke1007/items/e4e987836412e9bc73b9 + +/* +// 2024-4-8 Akira: メモリ使用量のチェックのため追加 See #2810 +// startMemoryMonitoring関数が5分ごとに呼び出され、メモリ使用量をチェックします。 +// メモリ使用量が閾値(ここでは500MB)を超えた場合、エラーメッセージとスタックトレースをレポートします。 +// +void startMemoryMonitoring() { + const threshold = 500 * 1024 * 1024; // 500MB + + // メモリ使用量情報を取得 + final memoryUsage = MemoryUsage.fromJson(DartVMInfo.getAllocationProfile()); + + if (memoryUsage.heapUsage > threshold) { + final now = DateTime.now().toIso8601String(); + final message = 'High memory usage detected at $now: ${memoryUsage.heapUsage} bytes'; + print(message); + reportError(message, StackTrace.current); + showMemoryWarningDialog(); + } + + Timer(const Duration(minutes: 5), startMemoryMonitoring); +} + +class MemoryUsage { + final int heapUsage; + + MemoryUsage({required this.heapUsage}); + + factory MemoryUsage.fromJson(Map json) { + return MemoryUsage( + heapUsage: json['heapUsage'] as int, + ); + } +} +*/ + +// 2024-4-8 Akira: メモリ使用量のチェックのため追加 See #2810 +// reportError関数でエラーレポートを送信します。具体的な実装は、利用するエラー報告サービスによって異なります。 +void reportError(String message, StackTrace stackTrace) async { + // エラーレポートの送信処理を実装 + // 例: SentryやFirebase Crashlyticsなどのエラー報告サービスを利用 + print("ReportError : $message . stacktrace : $stackTrace"); +} + +// 2024-4-8 Akira: メモリ使用量のチェックのため追加 See #2810 +// showMemoryWarningDialog関数で、メモリ使用量が高い場合にユーザーに警告ダイアログを表示します。 +// +void showMemoryWarningDialog() { + if (Get.context != null) { + showDialog( + context: Get.context!, + builder: (context) => AlertDialog( + title: const Text('メモリ使用量の警告'), + content: const Text('アプリのメモリ使用量が高くなっています。アプリを再起動することをお勧めします。'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('OK'), + ), + ], + ), + ); + } +} + +StreamSubscription? positionStream; +bool background=false; +DateTime lastGPSCollectedTime=DateTime.now(); +String team_name=""; +String event_code=""; + +Future startBackgroundTracking() async { + if (Platform.isIOS && background==false) { + + final IndexController indexController = Get.find(); + if(indexController.currentUser.isNotEmpty) { + team_name = indexController.currentUser[0]["user"]['team_name']; + event_code = indexController.currentUser[0]["user"]["event_code"]; + } + background = true; + debugPrint("バックグラウンド処理を開始しました。"); + const LocationSettings locationSettings = LocationSettings( + accuracy: LocationAccuracy.high, + distanceFilter: 100, + ); + + try { + positionStream = Geolocator.getPositionStream(locationSettings: locationSettings) + .listen((Position? position) async { + if (position != null) { + final lat = position.latitude; + final lng = position.longitude; + //final timestamp = DateTime.now(); + final accuracy = position.accuracy; + + // GPS信号強度がlowの場合はスキップ + if (accuracy > 100) { + debugPrint("GPS signal strength is low. Skipping data saving."); + return; + } + + Duration difference = lastGPSCollectedTime.difference(DateTime.now()) + .abs(); + // 最後にGPS信号を取得した時刻から10秒以上経過、かつ10m以上経過(普通に歩くスピード) + //debugPrint("時間差:${difference}"); + if (difference.inSeconds >= 10 ) { + debugPrint("バックグラウンドでのGPS取得時の処理(10secおき) count=${difference.inSeconds}, time=${DateTime.now()}"); + + // DBにGPSデータを保存 pages/destination/destination_controller.dart + await addGPStoDB(lat, lng); + + lastGPSCollectedTime = DateTime.now(); + } + } + }, onError: (error) { + if (error is LocationServiceDisabledException) { + print('Location services are disabled'); + } else if (error is PermissionDeniedException) { + print('Location permissions are denied'); + } else { + print('Location Error: $error'); + } + }); + } catch (e) { + print('Error starting background tracking: $e'); + } + }else if (Platform.isAndroid && background == false) { + background = true; + debugPrint("バックグラウンド処理を開始しました。"); + + try { + // 位置情報の権限が許可されているかを確認 + await PermissionController.checkAndRequestPermissions(); + }catch(e){ + print('Error starting background tracking: $e'); + } + } +} + +Future addGPStoDB(double la, double ln) async { + //debugPrint("in addGPStoDB ${indexController.currentUser}"); + GpsDatabaseHelper db = GpsDatabaseHelper.instance; + try { + GpsData gpsData = GpsData( + id: 0, + team_name: team_name, + event_code: event_code, + lat: la, + lon: ln, + is_checkin: 0, + created_at: DateTime.now().millisecondsSinceEpoch); + var res = await db.insertGps(gpsData); + debugPrint("バックグラウンドでのGPS保存:"); + } catch (err) { + print("errr ready gps $err"); + return; + } +} + +Future stopBackgroundTracking() async { + if (Platform.isIOS && background==true) { + background=false; + debugPrint("バックグラウンド処理:停止しました。"); + await positionStream?.cancel(); + positionStream = null; + }else if(Platform.isAndroid && background==true){ + background=false; + debugPrint("バックグラウンド処理:停止しました。"); + const platform = MethodChannel('location'); + try { + await platform.invokeMethod('stopLocationService'); + } on PlatformException catch (e) { + print("Failed to stop location service: '${e.message}'."); + } + } +} + +class MyApp extends StatefulWidget { + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State with WidgetsBindingObserver { // This widget is the root of your application. + + @override + void initState() { + super.initState(); + if (!Get.isRegistered()) { + Get.put(LocationController()); + } + + if (context.mounted) { + restoreGame(); + } + WidgetsBinding.instance.addObserver(this); + + // ウィジェットが構築された後に権限をチェック + WidgetsBinding.instance.addPostFrameCallback((_) { + PermissionController.checkAndRequestPermissions(); + }); + + debugPrint("Start MyAppState..."); + } + +/* + void showPermissionRequiredDialog() { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return AlertDialog( + title: Text('権限が必要です'), + content: Text('このアプリは機能するために位置情報の権限が必要です。設定で権限を許可してください。'), + actions: [ + TextButton( + child: Text('設定を開く'), + onPressed: () { + openAppSettings(); + Navigator.of(context).pop(); + }, + ), + TextButton( + child: Text('アプリを終了'), + onPressed: () { + // アプリを終了 + Navigator.of(context).pop(); + // よりクリーンな終了のために 'flutter_exit_app' のようなプラグインを使用することをお勧めします + // 今回は単にすべてのルートをポップします + Navigator.of(context).popUntil((route) => false); + }, + ), + ], + ); + }, + ); + } + + */ + + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + // void saveGameState() async { + // DestinationController destinationController = Get.find(); + // SharedPreferences pref = await SharedPreferences.getInstance(); + // pref.setBool("is_in_rog", destinationController.is_in_rog.value); + // pref.setBool("rogaining_counted", destinationController.rogaining_counted.value); + // } + + @override + Future didChangeAppLifecycleState(AppLifecycleState state) async { + try { + LocationController locationController = Get.find(); + + DestinationController destinationController = Get.find< + DestinationController>(); + + //DestinationController destinationController = + // Get.find(); + switch (state) { + case AppLifecycleState.resumed: + // 追加 2024.8.13. + await stopBackgroundTracking(); + destinationController.restartGPS(); + // 追加 2024.8.13. + + // バックグラウンド処理を停止 + if (Platform.isIOS && destinationController.isRunningBackgroundGPS) { + // Foreground に戻った時の処理 + debugPrint( + " ==(Status Changed)==> RESUMED. フォアグラウンドに戻りました"); + locationController.resumePositionStream(); + //print("RESUMED"); + restoreGame(); + + stopBackgroundTracking(); + destinationController.isRunningBackgroundGPS = false; + destinationController.restartGPS(); + } else if (Platform.isAndroid) { + if (destinationController.isRunningBackgroundGPS) { + const platform = MethodChannel('location'); + platform.invokeMethod('stopLocationService'); + destinationController.isRunningBackgroundGPS = false; + destinationController.restartGPS(); + debugPrint("stopped android location service.."); + } + + debugPrint( + "==(Status Changed)==> RESUMED. android フォアグラウンドに戻りました"); + locationController.resumePositionStream(); + //print("RESUMED"); + restoreGame(); + } else { + debugPrint("==(Status Changed)==> RESUMED 不明状態"); + } + break; + case AppLifecycleState.inactive: + // アプリが非アクティブになったときに発生します。 + + if (Platform.isIOS && !destinationController + .isRunningBackgroundGPS) { // iOSはバックグラウンドでもフロントの処理が生きている。 + // これは、別のアプリやシステムのオーバーレイ(着信通話やアラームなど)によって一時的に中断された状態です。 + debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。"); + //locationController.resumePositionStream(); + + // 追加: フロントエンドのGPS信号のlistenを停止 + locationController.stopPositionStream(); + + destinationController.isRunningBackgroundGPS = true; + startBackgroundTracking(); + } else if (Platform.isAndroid && + !destinationController.isRunningBackgroundGPS) { + debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。"); + } else { + debugPrint("==(Status Changed)==> INACTIVE 不明状態"); + } + saveGameState(); + break; + case AppLifecycleState.paused: + // バックグラウンドに移行したときの処理 + //locationController.resumePositionStream(); + debugPrint(" ==(Status Changed)==> PAUSED. バックグラウンド処理。"); + if (Platform.isIOS && !destinationController.isRunningBackgroundGPS) { + debugPrint( + "iOS already running background GPS processing when it's inactive"); + } else if (Platform.isAndroid && + !destinationController.isRunningBackgroundGPS) { + debugPrint( + " ==(Status Changed)==> PAUSED. Android バックグラウンド処理。"); + locationController.stopPositionStream(); + const platform = MethodChannel('location'); + platform.invokeMethod('startLocationService'); + //platform.invokeMethod('stopLocationService'); + destinationController.isRunningBackgroundGPS = true; + //startBackgroundTracking(); + } + saveGameState(); + break; + case AppLifecycleState.detached: + // アプリが終了する直前に発生します。この状態では、アプリはメモリから解放される予定です。 + //locationController.resumePositionStream(); + debugPrint(" ==(Status Changed)==> DETACHED アプリは終了します。"); + saveGameState(); + break; + case AppLifecycleState.hidden: + // Web用の特殊な状態で、モバイルアプリでは発生しません。 + //locationController.resumePositionStream(); + debugPrint(" ==(Status Changed)==> Hidden アプリが隠れた"); + saveGameState(); + break; + } + }catch(e){ + print('Error finding LocationController: $e'); + } + } + + @override Widget build(BuildContext context) { + return GetMaterialApp( translations: StringValues(), locale: const Locale('ja', 'JP'), @@ -47,7 +603,7 @@ class MyApp extends StatelessWidget { title: 'ROGAINING', theme: ThemeData( colorScheme: ColorScheme.fromSeed( - seedColor: const Color.fromARGB(255, 4, 88, 161)), + seedColor: const Color.fromARGB(255, 36, 135, 221)), useMaterial3: true, ), debugShowCheckedModeBanner: false, @@ -55,58 +611,13 @@ class MyApp extends StatelessWidget { opaqueRoute: Get.isOpaqueRouteDefault, popGesture: Get.isPopGestureEnable, transitionDuration: const Duration(milliseconds: 230), - initialBinding: IndexBinding(userToken), //HomeBinding(), + initialBinding: IndexBinding(), //HomeBinding(), initialRoute: AppPages.PERMISSION, getPages: AppPages.routes, enableLog: true, ); } + + + } - - - -// class MyApp extends StatelessWidget { -// MyApp({Key? key}) : super(key: key); - -// // This widget is the root of your application. -// @override -// Widget build(BuildContext context) { -// return MaterialApp( -// title: 'Flutter Demo', -// theme: ThemeData( -// primaryColor: Color(0xfff00B074), -// textTheme: const TextTheme( -// bodyText1: TextStyle( -// fontSize: 18.0, -// fontFamily: 'Barlow-Medium', -// color: Color(0xff464255)), -// ), -// ), -// home: PermissionHandlerScreen(), -// ); -// } -// } - - - - -// class SplashScreen extends StatelessWidget { -// const SplashScreen({Key? key}) : super(key: key); - -// @override -// Widget build(BuildContext context) { -// return WillPopScope( -// onWillPop: () async { -// SystemNavigator.pop(); -// return true; -// }, -// child: Scaffold( -// body: Center( -// child: Text( -// "Splash Screen", -// ), -// ), -// ), -// ); -// } -// } diff --git a/lib/model/auth_user.dart b/lib/model/auth_user.dart new file mode 100644 index 0000000..c77922e --- /dev/null +++ b/lib/model/auth_user.dart @@ -0,0 +1,27 @@ +// プロパティの型がString?やint?などのオプショナル型になっています。 +// これらのプロパティが常に値を持つことが保証されている場合は、非オプショナル型を使用することで、不要なnullチェックを回避できます。 +// +class AuthUser { + AuthUser(); + + //AuthUser.from({required this.id, required this.email, required this.is_rogaining, required this.group, required this.zekken_number, required this.event_code, required this.team_name}); + + AuthUser.fromMap(Map map) + : id = int.parse(map["id"].toString()), + email = map["email"].toString(), + is_rogaining = bool.parse(map["is_rogaining"].toString()), + group = map["group"].toString(), + zekken_number = map["zekken_number"].toString(), + event_code = map["event_code"].toString(), + team_name = map["team_name"].toString(), + auth_token = map["token"]; + + int? id; + String? email; + bool? is_rogaining; + String? group; + String? zekken_number; + String? event_code; + String? team_name; + String? auth_token; +} diff --git a/lib/model/category.dart b/lib/model/category.dart new file mode 100644 index 0000000..07e4bd3 --- /dev/null +++ b/lib/model/category.dart @@ -0,0 +1,80 @@ +// lib/models/category.dart + +class NewCategory { + final int id; + final String categoryName; + final int categoryNumber; + final Duration duration; + final int numOfMember; + final bool family; + final bool female; + final String? time; + + NewCategory({ + required this.id, + required this.categoryName, + this.time, + required this.categoryNumber, + required this.duration, + required this.numOfMember, + required this.family, + required this.female, + }); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is NewCategory && + runtimeType == other.runtimeType && + id == other.id; + + @override + int get hashCode => id.hashCode; + + factory NewCategory.fromJson(Map json) { + final fullCategoryName = json['category_name'] as String; + final parts = fullCategoryName.split('-'); + final baseName = parts[0].trim(); + final time = parts.length > 1 ? parts[1].trim() : null; + + return NewCategory( + id: json['id'] ?? 0, + categoryName: json['category_name'] ?? 'Unknown Category', + time: time, + categoryNumber: json['category_number'] ?? 0, + duration: parseDuration(json['duration']), + numOfMember: json['num_of_member'] ?? 1, + family: json['family'] ?? false, + female: json['female'] ?? false, + ); + } + + String get baseCategory => categoryName.split('-')[0].trim(); + + static Duration parseDuration(String s) { + int hours = 0; + int minutes = 0; + int micros; + List parts = s.split(':'); + if (parts.length > 2) { + hours = int.parse(parts[parts.length - 3]); + } + if (parts.length > 1) { + minutes = int.parse(parts[parts.length - 2]); + } + micros = (double.parse(parts[parts.length - 1]) * 1000000).round(); + return Duration(hours: hours, minutes: minutes, microseconds: micros); + } + + Map toJson() { + return { + 'id': id, + 'category_name': categoryName, + 'category_number': categoryNumber, + 'duration': duration.inSeconds, + 'num_of_member': numOfMember, + 'family': family, + 'female': female, + }; + } +} \ No newline at end of file diff --git a/lib/model/destination.dart b/lib/model/destination.dart index 7730066..39b7eeb 100644 --- a/lib/model/destination.dart +++ b/lib/model/destination.dart @@ -1,113 +1,138 @@ - - +// プロパティの型がString?やint?などのオプショナル型になっています。 +// これらのプロパティが常に値を持つことが保証されている場合は、非オプショナル型を使用することで、不要なnullチェックを回避できます。 +// class Destination { - String? name; - String? address; - String? phone; - String? email; - String? webcontents; - String? videos; - String? category; - int? series; - double? lat; - double? lon; - String? sub_loc_id; - int? location_id; - int? list_order; - String? photos; - double? checkin_radious; - int? auto_checkin; - bool? selected = false; - bool? checkedin = false; - double? cp; - double? checkin_point; - double? buy_point; - int? hidden_location; + String? name; + String? address; + String? phone; + String? email; + String? webcontents; + String? videos; + String? category; + int? series; + double? lat; + double? lon; + String? sub_loc_id; + int? location_id; + int? list_order; + String? photos; + double? checkin_radious; + int? auto_checkin; + bool? selected = false; + bool? checkedin = false; + double? cp; + double? checkin_point; + double? buy_point; + int? hidden_location; + String? checkin_image; + String? buypoint_image; + bool forced_checkin = false; + int recipt_times = 0; + String? tags; - Destination({ - this.name, - this.address, - this.phone, - this.email, - this.webcontents, - this.videos, - this.category, - this.series, - this.lat, - this.lon, - this.sub_loc_id, - this.location_id, - this.list_order, - this.photos, - this.checkin_radious, - this.auto_checkin, - this.selected, - this.checkedin, - this.cp, - this.checkin_point, - this.buy_point, - this.hidden_location - }); + bool use_qr_code = false; // QR code を使用するかどうか。default=false - factory Destination.fromMap(Map json) { + Destination( + {this.name, + this.address, + this.phone, + this.email, + this.webcontents, + this.videos, + this.category, + this.series, + this.lat, + this.lon, + this.sub_loc_id, + this.location_id, + this.list_order, + this.photos, + this.checkin_radious, + this.auto_checkin, + this.selected, + this.checkedin, + this.cp, + this.checkin_point, + this.buy_point, + this.hidden_location, + this.checkin_image, + this.buypoint_image, + this.forced_checkin = false, + this.recipt_times = 0, + this.tags}); //, ... use_qr_code をDBに追加したらオープン +// this.use_qr_code = false}); - bool selec = json['selected'] == 0 ? false : true; - bool checkin = json['checkedin'] == 0 ? false : true; + factory Destination.fromMap(Map json) { + bool selec = json['selected'] == 0 ? false : true; + bool checkin = json['checkedin'] == 0 ? false : true; + bool forcedCheckin = json['forced_checkin'] == 0 ? false : true; + bool useQrCode = json['use_qr_code'] == 1 ? true : false; + //print("-----tags model----- ${json}"); - return Destination( - name: json['name'], - address: json['address'], - phone: json['phone'], - email: json['email'], - webcontents: json['webcontents'], - videos: json['videos'], - category: json['category'], - series: json['series'], - lat: json['lat'], - lon: json['lon'], - sub_loc_id : json['sub_loc_id'], - location_id: json['location_id'], - list_order: json['list_order'], - photos: json['photos'], - checkin_radious: json['checkin_radious'], - auto_checkin:json['auto_checkin'], - selected: selec, - checkedin: checkin, - cp: json['cp'], - checkin_point: json['checkin_point'], - buy_point: json['buy_point'], - hidden_location: json['hidden_location'] - ); - } + return Destination( + name: json['name'], + address: json['address'], + phone: json['phone'], + email: json['email'], + webcontents: json['webcontents'], + videos: json['videos'], + category: json['category'], + series: json['series'], + lat: json['lat'], + lon: json['lon'], + sub_loc_id: json['sub_loc_id'], + location_id: json['location_id'], + list_order: json['list_order'], + photos: json['photos'], + checkin_radious: json['checkin_radious'], + auto_checkin: json['auto_checkin'], + selected: selec, + checkedin: checkin, + cp: json['cp'], + checkin_point: json['checkin_point'], + buy_point: json['buy_point'], + hidden_location: json['hidden_location'], + checkin_image: json['checkin_image'], + buypoint_image: json["buypoint_image"], + forced_checkin: forcedCheckin, + recipt_times: json["recipt_times"], + tags: json["tags"] ); //, +// use_qr_code: useQrCode); + } - Map toMap(){ - int sel = selected == false ? 0 : 1; - int check = checkedin == false ? 0 : 1; - return { - 'name':name, - 'address': address, - 'phone': phone, - 'email': email, - 'webcontents': webcontents, - 'videos': videos, - 'category':category, - 'series':series, - 'lat':lat, - 'lon':lon, - 'sub_loc_id': sub_loc_id, - 'location_id':location_id, - 'list_order':list_order, - 'photos':photos, - 'checkin_radious': checkin_radious, - 'auto_checkin': auto_checkin, - 'selected': sel, - 'checkedin': check, - 'cp' : cp, - 'checkin_point' : checkin_point, - 'buy_point' : buy_point, - 'hidden_location' : hidden_location - }; - } - - -} \ No newline at end of file + Map toMap() { + int sel = selected == false ? 0 : 1; + int check = checkedin == false ? 0 : 1; + int forcedCheckin = forced_checkin == false ? 0 : 1; + return { + 'name': name, + 'address': address, + 'phone': phone, + 'email': email, + 'webcontents': webcontents, + 'videos': videos, + 'category': category, + 'series': series, + 'lat': lat, + 'lon': lon, + 'sub_loc_id': sub_loc_id, + 'location_id': location_id, + 'list_order': list_order, + 'photos': photos, + 'checkin_radious': checkin_radious, + 'auto_checkin': auto_checkin, + 'selected': sel, + 'checkedin': check, + 'cp': cp, + 'checkin_point': checkin_point, + 'buy_point': buy_point, + 'hidden_location': hidden_location, + 'checkin_image': checkin_image, + 'buypoint_image': buypoint_image, + 'forced_checkin': forcedCheckin, + 'recipt_times': recipt_times, + 'tags': tags //, + //'use_qr_code': use_qr_code + }; + } +} diff --git a/lib/model/entry.dart b/lib/model/entry.dart new file mode 100644 index 0000000..6e84524 --- /dev/null +++ b/lib/model/entry.dart @@ -0,0 +1,50 @@ +// lib/models/entry.dart +import 'event.dart'; +import 'team.dart'; +import 'category.dart'; + +class Entry { + final int id; + final Team team; + final Event event; + final NewCategory category; + final DateTime? date; + final int zekkenNumber; // 新しく追加 + final String owner; + + Entry({ + required this.id, + required this.team, + required this.event, + required this.category, + required this.date, + required this.zekkenNumber, + required this.owner, + }); + + factory Entry.fromJson(Map json) { + return Entry( + id: json['id'], + team: Team.fromJson(json['team']), + event: Event.fromJson(json['event']), + category: NewCategory.fromJson(json['category']), + date: json['date'] != null + ? DateTime.tryParse(json['date']) + : null, + zekkenNumber: json['zekken_number'], // 新しく追加 + owner: json['owner'] is Map ? json['owner']['name'] ?? '' : json['owner'] ?? '', + ); + } + + Map toJson() { + return { + 'id': id, + 'team': team.toJson(), + 'event': event.toJson(), + 'category': category.toJson(), + 'date': date?.toIso8601String(), + 'zekken_number': zekkenNumber, // 新しく追加 + 'owner': owner, + }; + } +} \ No newline at end of file diff --git a/lib/model/event.dart b/lib/model/event.dart new file mode 100644 index 0000000..2b4746c --- /dev/null +++ b/lib/model/event.dart @@ -0,0 +1,40 @@ +// lib/models/event.dart + +class Event { + final int id; + final String eventName; + final DateTime startDatetime; + final DateTime endDatetime; + final DateTime deadlineDateTime; // 新しく追加 + + Event({ + required this.id, + required this.eventName, + required this.startDatetime, + required this.endDatetime, + required this.deadlineDateTime, + }); + + factory Event.fromJson(Map json) { + final endDatetime = DateTime.parse(json['end_datetime']); + return Event( + id: json['id'], + eventName: json['event_name'], + startDatetime: DateTime.parse(json['start_datetime']), + endDatetime: DateTime.parse(json['end_datetime']), + deadlineDateTime: json['deadline_datetime'] != null + ? DateTime.parse(json['deadline_datetime']) + : endDatetime.subtract(const Duration(days: 7)), // 仮の実装 + // deadlineDateTime: DateTime.parse(json['deadline_datetime']), + ); + } + + Map toJson() { + return { + 'id': id, + 'event_name': eventName, + 'start_datetime': startDatetime.toIso8601String(), + 'end_datetime': endDatetime.toIso8601String(), + }; + } +} \ No newline at end of file diff --git a/lib/model/game_state_instance.dart b/lib/model/game_state_instance.dart new file mode 100644 index 0000000..12f88c9 --- /dev/null +++ b/lib/model/game_state_instance.dart @@ -0,0 +1,8 @@ +enum LocationState { noGps, notInCheckin, withinCheckin } + +enum GameState { notStarted, startedNotCounted, startedCounted, nodeGoal } + +class GameInsStatetance { + LocationState locationState = LocationState.noGps; + GameState gameState = GameState.notStarted; +} diff --git a/lib/model/gps_data.dart b/lib/model/gps_data.dart new file mode 100644 index 0000000..404a900 --- /dev/null +++ b/lib/model/gps_data.dart @@ -0,0 +1,47 @@ +class GpsData { + int id; + String team_name; + String event_code; + double lat; + double lon; + int is_checkin; + int created_at; + int is_synced; + + GpsData({ + required this.id, + required this.team_name, + required this.event_code, + required this.lat, + required this.lon, + required this.created_at, + this.is_checkin = 0, + this.is_synced = 0, + }); + + factory GpsData.fromMap(Map json) { + return GpsData( + id: json["id"], + team_name: json["team_name"], + event_code: json["event_code"], + lat: json["lat"], + lon: json["lon"], + is_checkin: json["is_checkin"], + created_at: json["created_at"], + is_synced: json["is_synced"] ?? 0, + ); + } + + Map toMap() { + return { + 'id': id, + 'team_name': team_name, + 'event_code': event_code, + 'lat': lat, + 'lon': lon, + 'is_checkin': is_checkin, + 'created_at': created_at, + 'is_synced': is_synced, + }; + } +} diff --git a/lib/model/map_state_instance.dart b/lib/model/map_state_instance.dart new file mode 100644 index 0000000..933e2b3 --- /dev/null +++ b/lib/model/map_state_instance.dart @@ -0,0 +1,6 @@ +import 'package:flutter_map/flutter_map.dart'; + +class MapStateInstance { + MapController? mapController; + LatLngBounds? currentBounds; +} diff --git a/lib/model/team.dart b/lib/model/team.dart new file mode 100644 index 0000000..c906a87 --- /dev/null +++ b/lib/model/team.dart @@ -0,0 +1,51 @@ +// lib/models/team.dart + +import 'category.dart'; +import 'user.dart'; + +class Team { + final int id; +// final String zekkenNumber; + final String teamName; + final NewCategory category; + final User owner; + List members; // membersフィールドを追加 + + Team({ + required this.id, +// required this.zekkenNumber, + required this.teamName, + required this.category, + required this.owner, + this.members = const [], // デフォルト値を空のリストに設定 + }); + + factory Team.fromJson(Map json) { + return Team( + id: json['id'] ?? 0, + //zekkenNumber: json['zekken_number'] ?? 'Unknown', + teamName: json['team_name'] ?? 'Unknown Team', + category: json['category'] != null + ? NewCategory.fromJson(json['category']) + : NewCategory(id: 0, categoryName: 'Unknown', categoryNumber: 0, duration: Duration.zero, numOfMember: 1, family: false, female: false), + owner: json['owner'] != null + ? User.fromJson(json['owner']) + : User(id: 0, email: 'unknown@example.com', firstname: 'Unknown', lastname: 'User', dateOfBirth: null, female: false, isActive: false), + members: json['members'] != null // membersフィールドを解析 + ? List.from(json['members'].map((x) => User.fromJson(x))) + : [], + ); + } + + + Map toJson() { + return { + 'id': id, + //'zekken_number': zekkenNumber, + 'team_name': teamName, + 'category': category.toJson(), + 'owner': owner.toJson(), + 'members': members.map((member) => member.toJson()).toList(), // membersフィールドをJSONに変換 + }; + } +} \ No newline at end of file diff --git a/lib/model/user.dart b/lib/model/user.dart new file mode 100644 index 0000000..9ccf674 --- /dev/null +++ b/lib/model/user.dart @@ -0,0 +1,47 @@ +// lib/models/user.dart + +class User { + final int? id; + final String? email; + final String firstname; + final String lastname; + final DateTime? dateOfBirth; + late final bool female; + final bool isActive; + + User({ + this.id, + this.email, + required this.firstname, + required this.lastname, + this.dateOfBirth, + required this.female, + required this.isActive, + }); + + factory User.fromJson(Map json) { + return User( + id: json['id'], + email: json['email'], + firstname: json['firstname'] ?? 'Unknown', + lastname: json['lastname'] ?? 'Unknown', + dateOfBirth: json['date_of_birth'] != null + ? DateTime.tryParse(json['date_of_birth']) + : null, + female: json['female'] ?? false, + isActive: json['is_active'] ?? false, + ); + } + + Map toJson() { + return { + 'id': id, + 'email': email, + 'firstname': firstname, + 'lastname': lastname, + 'date_of_birth': dateOfBirth?.toIso8601String(), + 'female': female, + 'is_active': isActive, + }; + } +} \ No newline at end of file diff --git a/lib/nrog/pages/auth_page.dart b/lib/nrog/pages/auth_page.dart new file mode 100644 index 0000000..978cc60 --- /dev/null +++ b/lib/nrog/pages/auth_page.dart @@ -0,0 +1,227 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import 'package:keyboard_dismisser/keyboard_dismisser.dart'; +import 'package:gifunavi/model/auth_user.dart'; +import 'package:gifunavi/nrog/pages/home_page.dart'; +import 'package:gifunavi/provider/auth_provider.dart'; +import 'package:gifunavi/services/auth_service.dart'; +import 'package:gifunavi/widgets/c_form_text_field.dart'; +import 'package:gifunavi/widgets/c_password_text_filed.dart'; + +class AuthPage extends ConsumerStatefulWidget { + const AuthPage({super.key}); + + @override + ConsumerState createState() => _AuthPageState(); +} + +class _AuthPageState extends ConsumerState { + final _formkey = GlobalKey(); + final FocusNode focusEmail = FocusNode(); + final FocusNode focusPwd = FocusNode(); + var _authMode = 'login'; + bool _isLoginProgress = false; + + final TextEditingController _emailTextEditingController = + TextEditingController(); + final TextEditingController _passwordTextEditingController = + TextEditingController(); + + @override + void initState() { + super.initState(); + _emailTextEditingController.addListener(() => setState(() {})); + WidgetsBinding.instance.addPostFrameCallback((_) { + checkUser(); + }); + } + + void _submit() async { + setState(() { + _isLoginProgress = true; + }); + if (_formkey.currentState!.validate()) { + AuthService authService = AuthService(); + AuthUser? user = await authService.userLogin( + _emailTextEditingController.text, + _passwordTextEditingController.text); + if (user != null) { + setState(() { + ref.read(authUserStateProvider.notifier).addLogin(user); + }); + } + } + setState(() { + _isLoginProgress = false; + }); + } + + Future _submitToken(String token) async { + setState(() { + _isLoginProgress = true; + }); + AuthService authService = AuthService(); + AuthUser? user = await authService.userFromToken(token); + //////////////print("---user is ${user} ---"); + if (user != null) { + setState(() { + _isLoginProgress = false; + ref.read(authUserStateProvider.notifier).addLogin(user); + }); + } else {} + } + + void checkUser() async { + String? token = + await ref.read(authUserStateProvider.notifier).tokenFromDevice(); + //////////////print("--- red token is ${token} ---"); + await _submitToken(token!); + final id = ref.read(authUserStateProvider).id; + if (id != null) { + if (context.mounted) { + Navigator.of(context) + .push(MaterialPageRoute(builder: (ctx) => const HomePage())); + } + } + return; + } + + @override + Widget build(BuildContext context) { + if (ref.read(authUserStateProvider).id != null) { + Navigator.of(context) + .push(MaterialPageRoute(builder: (ctx) => const HomePage())); + } + + return Scaffold( + resizeToAvoidBottomInset: true, + body: KeyboardDismisser( + gestures: const [ + GestureType.onTap, + //GestureType.onVerticalDragDown + ], + child: Center( + child: SizedBox( + child: Stack( + clipBehavior: Clip.none, + children: [ + buildAuthCard(), + buildLogo(), + ], + ), + )), + ), + ); + } + + Positioned buildLogo() { + return Positioned( + top: -170, + left: MediaQuery.of(context).size.width / 2 - 100, + child: Center( + child: Container( + alignment: Alignment.center, + width: 200, + height: 200, + decoration: const BoxDecoration( + shape: BoxShape.circle, + image: DecorationImage( + image: AssetImage('assets/images/appicon.png'), + fit: BoxFit.fill), + ), + ), + ), + ); + } + + Widget buildAuthCard() { + return Card( + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Form( + key: _formkey, + child: Padding( + padding: const EdgeInsets.only( + top: 40, bottom: 10, left: 12, right: 12), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(10.0), + child: CFormTextField( + cFocus: focusEmail, + cController: _emailTextEditingController), + ), + Padding( + padding: const EdgeInsets.all(10), + child: CPasswordTextField( + cController: _passwordTextEditingController, + cFocusNode: focusPwd, + ), + ), + const SizedBox( + height: 12, + ), + buildControlls(), + // SizedBox(height: MediaQuery.of(context).viewInsets.bottom,) + ], + ), + ), + ), + ], + ), + ); + } + + Widget buildControlls() { + if (_isLoginProgress) { + return const Center( + child: CircularProgressIndicator(), + ); + } + + final usr = ref.read(authUserStateProvider); + return Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.only(right: 12.0), + child: ElevatedButton( + onPressed: _submit, + child: Text(_authMode == "login" ? "Submit" : "Register", + style: GoogleFonts.lato( + color: Theme.of(context).colorScheme.secondary))), + ) + ], + ), + TextButton( + onPressed: () { + setState(() { + if (_authMode == 'login') { + _authMode = 'register'; + } else { + _authMode = 'login'; + } + }); + }, + child: Text( + _authMode == "login" + ? "${usr.id} Dont have account, please Register" + : "Already Registered, Login", + style: GoogleFonts.lato( + color: Theme.of(context).colorScheme.primary, + decoration: TextDecoration.underline, + fontSize: 16, + fontWeight: FontWeight.bold), + ), + ), + ], + ); + } +} diff --git a/lib/nrog/pages/home_page.dart b/lib/nrog/pages/home_page.dart new file mode 100644 index 0000000..d96e570 --- /dev/null +++ b/lib/nrog/pages/home_page.dart @@ -0,0 +1,119 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map_location_marker/flutter_map_location_marker.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:latlong2/latlong.dart'; +import 'package:gifunavi/provider/map_state_provider.dart'; +import 'package:gifunavi/widgets/base_layer_widget.dart'; + +class HomePage extends ConsumerStatefulWidget { + const HomePage({super.key}); + + @override + ConsumerState createState() => _HomePageState(); +} + +class _HomePageState extends ConsumerState { + StreamSubscription? subscription; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + final mapStateInstance = ref.watch(mapStateNotifierProvider); + return Scaffold( + //drawer: DrawerPage(), + appBar: AppBar( + title: const Text("Rogaining"), + actions: [ + IconButton( + onPressed: () { + //Get.toNamed(AppPages.HISTORY); + }, + icon: const Icon(Icons.history)), + IconButton( + onPressed: () { + // final tk = indexController.currentUser[0]["token"]; + // if (tk != null) { + // destinationController.fixMapBound(tk); + // } + }, + icon: const Icon(Icons.refresh)), + InkWell( + onTap: () { + //Get.toNamed(AppPages.SEARCH); + }, + child: Container( + height: 32, + width: 75, + decoration: BoxDecoration( + color: Colors.blue, + borderRadius: BorderRadius.circular(25), + ), + child: const Center( + child: Icon(Icons.search), + ), + ), + ), + //CatWidget(indexController: indexController,), + ], + ), + body: Center( + child: FlutterMap( + mapController: mapStateInstance.mapController, + options: MapOptions( + maxZoom: 18.4, + onMapReady: () { + // indexController.is_mapController_loaded.value = true; + subscription = mapStateInstance.mapController?.mapEventStream + .listen((MapEvent mapEvent) { + if (mapEvent is MapEventMoveStart) { + //print(DateTime.now().toString() + ' [MapEventMoveStart] START'); + // do something + } + // if (mapEvent is MapEventMoveEnd && + // indexController.currentUser.isEmpty) { + //print(DateTime.now().toString() + ' [MapEventMoveStart] END'); + // indexController.loadLocationsBound(); + //indexController.rogMapController!.move(c.center, c.zoom); + // } + }); + }, + center: const LatLng(37.15319600454702, 139.58765950528198), + //bounds: + zoom: 18, + interactiveFlags: InteractiveFlag.pinchZoom | InteractiveFlag.drag, + + onPositionChanged: (MapPosition pos, isvalue) { + //indexController.currentBound = [pos.bounds!]; + }, + // onTap: (_, __) => popupController + // .hideAllPopups(), // Hide popup when the map is tapped. + ), + children: [ + const BaseLayer(), + CurrentLocationLayer( + followOnLocationUpdate: FollowOnLocationUpdate.once, + turnOnHeadingUpdate: TurnOnHeadingUpdate.never, + style: const LocationMarkerStyle( + marker: DefaultLocationMarker( + child: Icon( + Icons.navigation, + color: Colors.yellowAccent, + ), + ), + markerSize: const Size(27, 27), + markerDirection: MarkerDirection.heading, + ), + ), + const MarkerLayer(markers: []) + ], + ), + ), + ); + } +} diff --git a/lib/nrog/pages/permission_page.dart b/lib/nrog/pages/permission_page.dart new file mode 100644 index 0000000..5403060 --- /dev/null +++ b/lib/nrog/pages/permission_page.dart @@ -0,0 +1,126 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:gifunavi/nrog/pages/auth_page.dart'; + +class PermissionPage extends StatefulWidget { + const PermissionPage({super.key}); + + @override + State createState() => _PermissionPageState(); +} + +class _PermissionPageState extends State { + bool hasNavigated = false; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + _checkPermissionStatus(); + }); + } + + _checkPermissionStatus() async { + PermissionStatus status = await Permission.location.status; + + if (status.isGranted == false) { + if (context.mounted) { + showAlert(context); + } + } else if (status.isPermanentlyDenied) { + await requestPermission(); + } else { + if (mounted) { + Navigator.of(context) + .push(MaterialPageRoute(builder: (_) => const AuthPage())); + } + } + } + + @override + Widget build(BuildContext context) { + return const Scaffold( + body: Text(""), + ); + } + + void showAlert(BuildContext context) { + showDialog( + context: context, + builder: (_) => AlertDialog( + title: const Text('ロケーション許可'), + content: const SingleChildScrollView( + child: ListBody( + children: [ + Text('このアプリでは、位置情報の収集を行います。'), + Text( + '岐阜ナビアプリではチェックポイントの自動チェックインの機能を可能にするために、現在地のデータが収集されます。アプリを閉じている時や、使用していないときにも収集されます。位置情報は、個人を特定できない統計的な情報として、ユーザーの個人情報とは一切結びつかない形で送信されます。お知らせの配信、位置情報の利用を許可しない場合は、この後表示されるダイアログで「許可しない」を選択してください。'), + ], + ), + ), + actions: [ + ElevatedButton( + child: const Text('OK'), + onPressed: () { + requestPermission(); + }, + ), + ], + )); + } + + Future requestPermission() async { + PermissionStatus permission = await Permission.location.status; + if (permission == PermissionStatus.permanentlyDenied) { + showPermanentAlert(); + } else { + PermissionStatus newPermission = await Permission.location.request(); + if (newPermission != PermissionStatus.granted) { + exit(0); + } else { + if (context.mounted) { + Navigator.of(context) + .push(MaterialPageRoute(builder: (_) => const AuthPage())); + } + } + } + } + + void showPermanentAlert() { + showDialog( + context: context, + builder: (_) => AlertDialog( + title: const Text('無効'), + content: const SingleChildScrollView( + child: ListBody( + children: [ + Text('位置情報が無効になっています'), + Text( + 'このアプリケーションへの位置情報アクセスが無効になっています。続行するには設定>プライバシーとセキュリティ>位置情報サービス>岐阜ナビ で有効にしてください。'), + ], + ), + ), + actions: [ + ElevatedButton( + child: const Text('OK'), + onPressed: () async { + await openAppSettings().then( + (value) async { + if (value) { + if (await Permission + .location.status.isPermanentlyDenied == + true && + await Permission.location.status.isGranted == + false) { + requestPermission(); /* opens app settings until permission is granted */ + } + } + }, + ); + }, + ), + ], + )); + } +} diff --git a/lib/pages/WebView/WebView_page.dart b/lib/pages/WebView/WebView_page.dart new file mode 100644 index 0000000..3186594 --- /dev/null +++ b/lib/pages/WebView/WebView_page.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +class WebViewPage extends StatelessWidget { + final String url; + + const WebViewPage({super.key, required this.url}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('WebView'), + ), + body: WebViewWidget( + controller: WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..loadRequest(Uri.parse(url)), + ), + ); + } +} \ No newline at end of file diff --git a/lib/pages/camera/camera_page.dart b/lib/pages/camera/camera_page.dart index f48fd30..0046d21 100644 --- a/lib/pages/camera/camera_page.dart +++ b/lib/pages/camera/camera_page.dart @@ -1,126 +1,492 @@ import 'dart:async'; +import 'dart:convert'; // この行を追加または確認 +import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; -import 'package:rogapp/model/destination.dart'; -import 'package:rogapp/pages/destination/destination_controller.dart'; -import 'package:rogapp/pages/index/index_controller.dart'; -import 'package:rogapp/services/external_service.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:gifunavi/model/destination.dart'; +import 'package:gifunavi/pages/destination/destination_controller.dart'; +import 'package:gifunavi/pages/index/index_controller.dart'; +import 'package:gifunavi/services/external_service.dart'; +import 'package:gifunavi/utils/const.dart'; +import 'package:qr_code_scanner/qr_code_scanner.dart'; +import 'package:http/http.dart' as http; // この行を追加 + + +// 関数 getTagText は、特定の条件に基づいて文字列から特定の部分を抽出し、返却するためのものです。 +// 関数は2つのパラメータを受け取り、条件分岐を通じて結果を返します。 +// +// この関数は、タグのリスト(空白を含む文字列)を適切に解析し、条件に応じて特定のタグを抽出するために設計されています。 +// 異なる種類の空白文字(半角、全角)で異なる分割を行い、与えられた条件(isRecept)に応じて適切なタグを選択して返却します。 +// +String getTagText(bool isRecept, String? tags) { + // bool isRecept: 真偽値を受け取り、この値によって処理の分岐が行われます。 + // String? tags: オプショナルな文字列(null が許容される)。空白文字を含む可能性のあるタグのリストを表します。 + + // 空のチェック: + // tags が null または空文字列 ("") の場合、何も含まれていないことを意味し、関数はただちに空文字列を返します。 + // + if (tags == null || tags.isEmpty) { + return ""; + } + + // タグの分割: + // tags が空ではない場合、文字列を空白文字で分割します。 + // ここで2種類の空白文字(半角 " " と全角 " ")に対応するため、2回分割を行っています。 + // tts: 半角スペース " " で分割した結果のリスト。 + // ttt: 全角スペース " " で分割した結果のリスト。 + // + List tts = tags.split(" "); + List ttt = tags.split(" "); + + // 条件分岐: + // isRecept の値によって、処理が分岐します。 + // + if (isRecept) { + // isRecept が true の場合: + // 全角スペースで分割した結果 (ttt) の長さが半角スペースで分割した結果 (tts) の長さより大きく、 + // かつ ttt が1つ以上の要素を持つ場合、ttt[1] (全角スペースで分割後の2番目の要素)を返します。 + if (ttt.length > tts.length && ttt.length > 1) { + return ttt[1]; + } + } + if (!isRecept) { + // isRecept が false の場合: + // 全角スペースで分割したリストが半角スペースで分割したリストよりも長い場合、ttt[0] (全角スペースで分割後の最初の要素)を返します。 + // 上記の条件に当てはまらない場合、半角スペースで分割したリストの最初の要素 tts[0] を返します。 + // + if (ttt.length > tts.length && ttt.length > 1) { + return ttt[0]; + } + } + if (!isRecept) { + // 最終的な返却: + // 上記の条件に何も該当しない場合(主に isRecept が true であり、全角スペースの条件に該当しない場合)、空文字列 "" を返します。 + return tts[0]; + } + return ""; +} + +// 要修正:画像の読み込みエラーが発生した場合のエラーハンドリングが不十分です。エラーメッセージを表示するなどの処理を追加してください。 +// getDisplayImage は、Destination オブジェクトを受け取り、特定の条件に基づいて表示する画像を返す機能を持っています。 +// Flutterの Image ウィジェットを使用して、適切な画像を表示します。 +// +// この関数は、提供された Destination オブジェクトに基づいて適切な画像を動的に選択し、 +// その画像を表示するための Image ウィジェットを生成します。 +// デフォルトの画像、完全なURL、またはサーバーURLと組み合わされた画像パスを使用して、条件に応じた画像の取得を試みます。 +// また、エラー発生時にはデフォルト画像にフォールバックすることでユーザー体験を向上させます。 +// +Image getDisplayImage(Destination destination) { + // Destination destination: これは Destination クラスのインスタンスで、 + // CheckPointのデータを持っているオブジェクトです。 + // このクラスには少なくとも phone と photos というプロパティが含まれている + // + + // サーバーURLの取得: + // serverUrl 変数には ConstValues.currentServer() メソッドから現在のサーバーのURLが取得されます。 + // これは画像を取得する際の基本URLとして使用される可能性があります。 + // + String serverUrl = ConstValues.currentServer(); + + // デフォルト画像の設定: + // img 変数にはデフォルトの画像が設定されます。 + // これは、アセットから "assets/images/empty_image.png" をロードするための Image.asset コンストラクタを使用しています。 + // + Image img = Image.asset("assets/images/empty_image.png"); + + // 電話番号のチェック: + // destination.phone が null の場合、関数は img(デフォルト画像)を返します。 + // これは、phone プロパティが画像URLの代用として何らかの形で使用されることを示唆していますが、 + // それが null であればデフォルト画像を使用するという意味です。 + // + if (destination.phone == null) { + return img; + } + + // 画像URLの構築と画像の返却: + // destination.photos が http を含む場合、これはすでに完全なURLとして提供されていることを意味します。 + // このURLを NetworkImage コンストラクタに渡し、Image ウィジェットを生成して返します。 + // そうでない場合は、serverUrl と destination.photos を組み合わせたURLを生成して NetworkImage に渡し、画像を取得します。 + // + if (destination.photos!.contains('http')) { + return Image( + image: NetworkImage( + destination.phone!, + ), + errorBuilder: + (BuildContext context, Object exception, StackTrace? stackTrace) { + return Image.asset("assets/images/empty_image.png"); + }, + ); + } else { + return Image( + image: NetworkImage( + '$serverUrl/media/compressed/${destination.photos}', + ), + errorBuilder: + (BuildContext context, Object exception, StackTrace? stackTrace) { + return Image.asset("assets/images/empty_image.png"); + }, + ); + } +} + +// getFinishImage は、ImageProvider 型のオブジェクトを返す関数で、Flutterアプリケーションで使用される画像を提供します。 +// この関数は、DestinationController というクラスのインスタンスに依存しており、特定の状態に基づいて適切な画像を返します。 +// +// この関数は、アプリケーションの現在の状態に依存して動的に画像を提供します。 +// DestinationController の photos リストに基づいて画像を選択し、リストが空の場合はデフォルトの画像を提供します。 +// これにより、画像の動的な管理が可能になり、ユーザーインターフェースの柔軟性が向上します。 +// また、ImageProvider クラスを使用することで、 +// 画像の具体的な取得方法(ファイルからの読み込みやアセットからのロードなど)を抽象化し、 +// Flutterの Image ウィジェットで直接使用できる形式で画像を返します。 +// +ImageProvider getFinishImage() { + + // DestinationControllerの取得: + // destinationController は Get.find() を使用して取得されます。 + // これは、GetXというFlutterの状態管理ライブラリの機能を使用して、 + // 現在のアプリケーションコンテキストから DestinationController タイプのインスタンスを取得するものです。 + // これにより、アプリケーションの他の部分で共有されている DestinationController の現在のインスタンスにアクセスします。 + // + DestinationController destinationController = + Get.find(); + + // 画像の決定: + // destinationController.photos リストが空でないかどうかをチェックします。 + // このリストは、ファイルパスまたは画像リソースへの参照を含む可能性があります。 + // + if (destinationController.photos.isNotEmpty) { + // リストが空でない場合、最初の要素 (destinationController.photos[0]) が使用されます。 + // FileImage コンストラクタを使用して、このパスから ImageProvider を生成します。 + // これは、ローカルファイルシステム上の画像ファイルを参照するためのものです。 + // + return FileImage(destinationController.photos[0]); + + } else { + // destinationController.photos が空の場合、 + // AssetImage を使用してアプリケーションのアセットからデフォルトの画像('assets/images/empty_image.png')を + // ロードします。これはビルド時にアプリケーションに組み込まれる静的なリソースです。 + // + return const AssetImage('assets/images/empty_image.png'); + } +} + +// getReceiptImage は、ImageProvider 型を返す関数です。 +// この関数は DestinationController オブジェクトに依存しており、条件に応じて特定の画像を返します。 +// この関数は getFinishImage 関数と非常に似ており、ほぼ同じロジックを使用していますが、返されるデフォルトの画像が異なります。 +// +ImageProvider getReceiptImage() { + DestinationController destinationController = + Get.find(); + if (destinationController.photos.isNotEmpty) { + return FileImage(destinationController.photos[0]); + } else { + return const AssetImage('assets/images/money.png'); + } +} + +// CameraPageクラスは、目的地に応じて適切なカメラ機能とアクションボタンを提供します。 +// 手動チェックイン、ゴール撮影、購入ポイント撮影など、様々なシナリオに対応しています。 +// また、ロゲイニングが開始されていない場合は、StartRogainingウィジェットを表示して、ユーザーにロゲイニングの開始を促します。 +// CameraPageクラスは、IndexControllerとDestinationControllerを使用して、 +// 現在の状態や目的地の情報を取得し、適切なUIを構築します。 +// また、写真の撮影や購入ポイントの処理など、様々な機能を提供しています。 +// class CameraPage extends StatelessWidget { - Destination? destination; - CameraPage({Key? key, this.destination}) : super(key: key); - DestinationController destinationController = Get.find(); + bool? manulaCheckin = false; // 手動チェックインを示すブール値(デフォルトはfalse) + bool? buyPointPhoto = false; // 購入ポイントの写真を示すブール値(デフォルトはfalse) + Destination destination; // 目的地オブジェクト + Destination? dbDest; // データベースから取得した目的地オブジェクト(オプショナル) + String? initImage; // 初期画像のパス(オプショナル) + bool? buyQrCode = false; + + CameraPage( + {super.key, + required this.destination, + this.dbDest, + this.manulaCheckin, + this.buyPointPhoto, + this.initImage}); + DestinationController destinationController = + Get.find(); IndexController indexController = Get.find(); + var settingGoal = false.obs; + Timer? timer; - ImageProvider getFinishImage(){ - if(destinationController.photos.isNotEmpty){ - return FileImage(destinationController.photos[0]); - } - else{ - return const AssetImage('assets/images/empty_image.png'); - } - } + // 現在の状態に基づいて、適切なアクションボタンを返します。 + // 要修正:エラーハンドリングが不十分です。例外が発生した場合の処理を追加することをお勧めします。 + // + Widget getAction(BuildContext context) { + if (manulaCheckin == true) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Wrap( + spacing: 16.0, + runSpacing: 8.0, + children: [ + Obx(() => ElevatedButton( + onPressed: () { + destinationController.openCamera(context, destination); + }, + style: ElevatedButton.styleFrom( + shape: const CircleBorder(), + padding: const EdgeInsets.all(20), + foregroundColor: Colors.white, + backgroundColor: destinationController.photos.isEmpty + ? Colors.red + : Colors.grey[300], + ), + child: destinationController.photos.isEmpty + ? const Text("撮影", style: TextStyle(color: Colors.white)) + : const Text("再撮影", style: TextStyle(color: Colors.black)), + )), + Obx(() => destinationController.photos.isNotEmpty + ? ElevatedButton( + style: ElevatedButton.styleFrom(backgroundColor: Colors.red), + onPressed: () async { + await destinationController.makeCheckin(destination, true, + destinationController.photos[0].path); + destinationController.rogainingCounted.value = true; + destinationController.skipGps = false; + destinationController.isPhotoShoot.value = false; - Widget getAction(BuildContext context){ - if(destinationController.is_at_goal.value && destinationController.is_in_rog.value){ - return Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - ElevatedButton( - onPressed: (){ - destinationController.openCamera(context); - }, - child: Text("take_photo of the clock".tr) - ), - Obx(() => - destinationController.photos.isNotEmpty ? - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red - ), - onPressed: (){ - int userId = indexController.currentUser[0]["user"]["id"]; - //print("--- Pressed -----"); - String team = indexController.currentUser[0]["user"]['team_name']; - //print("--- _team : ${_team}-----"); - String eventCode = indexController.currentUser[0]["user"]["event_code"]; - //print("--- _event_code : ${_event_code}-----"); - String token = indexController.currentUser[0]["token"]; - //print("--- _token : ${_token}-----"); - DateTime now = DateTime.now(); - String formattedDate = DateFormat('yyyy-MM-dd HH:mm:ss').format(now); - - ExternalService().makeGoal(userId, token, team, destinationController.photos[0].path, formattedDate, eventCode).then((value){ - print("---called ext api ${value['status']} ------"); - if(value['status'] == 'OK'){ - Get.back(); - destinationController.skip_gps = false; - Get.snackbar("目標が保存されました", "目標が正常に追加されました"); - destinationController.resetRogaining(); - } - else{ - print("---- status ${value['status']} ---- "); - Get.snackbar("目標が追加されていません", "please_try_again"); - } - }); - }, - child: Text("finish_goal".tr) - ): - Container() - ) - ], - ); + Get.snackbar("チェックインしました。", + "${destination.sub_loc_id} : ${destination.name}", + backgroundColor: Colors.green, + colorText: Colors.white, + duration: const Duration(seconds: 2), + ); + await Future.delayed(const Duration(seconds: 2)); + + Navigator.of(context).pop(true); + }, + child: const Text("チェックイン", style: TextStyle(color: Colors.white)), + ) + : Container()) + ], + ), + ); } - else{ + + if (destinationController.isAtGoal.value && + destinationController.isInRog.value && + destination.cp == -1) { + // isAtGoalがtrueで、isInRogがtrue、destination.cpが-1の場合は、ゴール用の撮影ボタンとゴール完了ボタンを表示します。 + //goal return Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Obx(() => - ElevatedButton( - onPressed: (){ - destinationController.openCamera(context); - }, - child: destinationController.photos.isNotEmpty ? const Text("再撮影") : const Text("撮影") - ) - ), - Obx(() => - destinationController.photos.isNotEmpty ? - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.red - ), - onPressed: (){ - print("##### current destination ${indexController.currentDestinationFeature[0].sub_loc_id} #######"); - destinationController.makeCheckin(indexController.currentDestinationFeature[0], true, destinationController.photos[0].path); - Get.back(); - destinationController.rogaining_counted.value = true; - destinationController.skip_gps = false; - destinationController.is_photo_shoot.value = false; - Get.snackbar("チェックインした", "正常にチェックインしました"); - - // ExternalService().makeGoal(user_id, _token, _team, destinationController.photos[0].path, formattedDate, _event_code).then((value){ - // print("---called ext api ${value['status']} ------"); - // if(value['status'] == 'OK'){ - // Get.back(); - // destinationController.skip_gps = false; - // Get.snackbar("Checked in", "Checked in successfuly"); - // } - // else{ - // print("---- status ${value['status']} ---- "); - // Get.snackbar("Checkin not added", "please_try_again"); - // } - // }); - }, - child: const Text("チェックイン") - ): - Container() - ) - ], - ); + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + ElevatedButton( + onPressed: () { + if (settingGoal.value == false) { + destinationController.openCamera(context, destination); + } + }, + child: Text("take_photo of the clock".tr)), + Obx(() => destinationController.photos.isNotEmpty + ? settingGoal.value == false + ? ElevatedButton( + style: + ElevatedButton.styleFrom( + foregroundColor: Colors.white, + backgroundColor: Colors.red + ), + onPressed: () async { + // print( + // "----- user isss ${indexController.currentUser[0]} -----"); + + settingGoal.value = true; + try { + int userId = + indexController.currentUser[0]["user"]["id"]; + //print("--- Pressed -----"); + String team = indexController.currentUser[0]["user"] + ['team_name']; + //print("--- _team : ${_team}-----"); + String eventCode = indexController.currentUser[0] + ["user"]["event_code"]; + //print("--- _event_code : ${_event_code}-----"); + String token = + indexController.currentUser[0]["token"]; + //print("--- _token : ${_token}-----"); + DateTime now = DateTime.now(); + String formattedDate = + DateFormat('yyyy-MM-dd HH:mm:ss').format(now); + + await ExternalService() + .makeGoal( + userId, + token, + team, + destinationController.photos[0].path, + formattedDate, + eventCode) + .then((value) { + // print( + // "---called ext api ${value['status']} ------"); + if (value['status'] == 'OK') { + Get.back(); + destinationController.skipGps = false; + Get.snackbar("目標が保存されました", "目標が正常に追加されました", + backgroundColor: Colors.green, + colorText: Colors.white + ); + destinationController.resetRogaining( + isgoal: true); + } else { + //print("---- status ${value['status']} ---- "); + Get.snackbar(value["detail"], "ERROR", + backgroundColor: Colors.green, + colorText: Colors.white + ); + } + }); + } on Exception catch (_) { + settingGoal.value = false; + } finally { + settingGoal.value = false; + } + }, + child: Text("finish_goal".tr)) + : const Center( + child: CircularProgressIndicator(), + ) + : Container()) + ], + ); + + } else if ((destinationController.isInRog.value || (destination.buy_point != null && destination.buy_point! > 0)) && + dbDest?.checkedin != null && + destination.cp != -1 && + dbDest?.checkedin == true) { + // isInRogがtrueで、dbDest?.checkedinがtrue、destination.cpが-1以外の場合は、購入ポイントの撮影ボタンと完了ボタンを表示します。 + //make buypoint image + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Obx(() => ElevatedButton( + onPressed: () { + destinationController.openCamera(context, destination); + }, + child: destinationController.photos.isNotEmpty + ? const Text("再撮影") + : const Text("撮影"))), + Obx(() => destinationController.photos.isNotEmpty + ? ElevatedButton( + style: ElevatedButton.styleFrom(backgroundColor: Colors.red), + onPressed: () async { + // print( + // "##### current destination ${indexController.currentDestinationFeature[0].sub_loc_id} #######"); + await destinationController.makeBuyPoint( + destination, destinationController.photos[0].path); + Get.back(); + destinationController.rogainingCounted.value = true; + destinationController.skipGps = false; + destinationController.isPhotoShoot.value = false; + Get.snackbar("お買い物加点を行いました。", + "${destination.sub_loc_id} : ${destination.name}", + backgroundColor: Colors.green, + colorText: Colors.white + ); + Navigator.of(context).pop(true); // ここを修正 + }, + child: const Text("レシートの写真を撮ってください")) + : Container()) + ], + ); + + } else if ((destinationController.isInRog.value || (destination.buy_point != null && destination.buy_point! > 0)) && + dbDest?.checkedin != null && + destination.cp != -1 && + destination.use_qr_code == true && + dbDest?.checkedin == true) { + // isInRogがtrueで、dbDest?.checkedinがtrue、destination.cpが-1以外、qrCode == true の場合は、 + // QRCode 撮影ボタンを表示 + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Obx(() => ElevatedButton( + onPressed: () { + destinationController.openCamera(context, destination); + }, + child: destinationController.photos.isNotEmpty + ? const Text("再QR読込") + : const Text("QR読込"))), + Obx(() => destinationController.photos.isNotEmpty + ? ElevatedButton( + style: ElevatedButton.styleFrom(backgroundColor: Colors.red), + onPressed: () async { + // print( + // "##### current destination ${indexController.currentDestinationFeature[0].sub_loc_id} #######"); + await destinationController.makeBuyPoint( + destination, destinationController.photos[0].path); + Get.back(); + destinationController.rogainingCounted.value = true; + destinationController.skipGps = false; + destinationController.isPhotoShoot.value = false; + Get.snackbar("お買い物加点を行いました。", + "${destination.sub_loc_id} : ${destination.name}", + backgroundColor: Colors.green, + colorText: Colors.white + ); + }, + child: const Text("QRコードを読み取ってください")) + : Container()) + ], + ); + } else { + // それ以外の場合は、撮影ボタンとチェックインボタンを表示します。 + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Obx(() => ElevatedButton( + onPressed: () { + destinationController.openCamera(context, destination); + }, + child: destinationController.photos.isNotEmpty + ? const Text("再撮影") + : const Text("撮影"))), + Obx(() => destinationController.photos.isNotEmpty + ? ElevatedButton( + style: ElevatedButton.styleFrom(backgroundColor: Colors.red), + onPressed: () async { + // print( + + // "##### current destination ${indexController.currentDestinationFeature[0].sub_loc_id} #######"); + await destinationController.makeCheckin( + indexController.currentDestinationFeature[0], + true, + destinationController.photos[0].path); + //Get.back(); + destinationController.rogainingCounted.value = true; + destinationController.skipGps = false; + destinationController.isPhotoShoot.value = false; + + + Get.snackbar( + "チェックインしました", + indexController.currentDestinationFeature[0].name ?? + "", + backgroundColor: Colors.green, + colorText: Colors.white, + duration: const Duration(seconds: 2), // 表示時間を1秒に設定 + ); + // SnackBarの表示が終了するのを待ってからCameraPageを閉じる + await Future.delayed(const Duration(seconds: 2)); + + Navigator.of(context).pop(true); // ここを修正 + }, + child: const Text("チェックイン")) + : Container()) + ], + ); } } @@ -130,127 +496,477 @@ class CameraPage extends StatelessWidget { @override Widget build(BuildContext context) { - if(destinationController.is_in_rog.value){ + //print("---- photos ${destination.photos} ----"); + if (buyPointPhoto == true) { + // buyPointPhotoがtrueの場合は、BuyPointCameraウィジェットを返します。 + //print("--- buy point camera ${destination.toString()}"); + //return BuyPointCamera(destination: destination); + + return SwitchableBuyPointCamera(destination: destination); + + //}else if(destination.use_qr_code){ + // return QRCodeScannerPage(destination: destination); + } else if (destinationController.isInRog.value || (destination.buy_point != null && destination.buy_point! > 0)) { + // isInRogがtrueの場合は、カメラページのUIを構築します。 + // AppBarには、目的地の情報を表示します。 + // ボディには、目的地の画像、タグ、アクションボタンを表示します。 + //print("-----tags camera page----- ${destination.tags}"); + //print("--- in normal camera ${destination.toString()}"); return Scaffold( - appBar: - destinationController.is_in_rog.value && destinationController.rogaining_counted.value == true ? - AppBar( - title: destination!.cp == -1 ? - Text("finishing_rogaining".tr) - : - Text("cp_pls_take_photo".tr) - , - leading: IconButton( - icon: Text("cancel".tr), - onPressed: (){ - Navigator.of(context).pop(); - destinationController.skip_10s = true; - timer = Timer.periodic(const Duration(seconds: 10), (Timer t){ - destinationController.skip_10s = false; - }); - }, - ), - centerTitle: true, - ) - : - AppBar( - title: const Text("チェックポイント"), + appBar: destinationController.isInRog.value && + destinationController.rogainingCounted.value == true + ? AppBar( + automaticallyImplyLeading: false, + title: destination.cp == -1 + ? Text("finishing_rogaining".tr) + : Text("${destination.sub_loc_id} : ${destination.name}"), + leading: IconButton( + icon: Text("cancel".tr), + onPressed: () { + Navigator.of(context).pop(); + destinationController.skip_10s = true; + timer = + Timer.periodic(const Duration(seconds: 10), (Timer t) { + destinationController.skip_10s = false; + }); + }, + ), + centerTitle: true, + ) + : AppBar( + automaticallyImplyLeading: false, + title: Text("${destination.sub_loc_id} : ${destination.name}"), + ), + body: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Center( + child: Obx( + () => Container( + width: MediaQuery.of(context).size.width, + height: 370, + decoration: BoxDecoration( + image: DecorationImage( + image: destinationController.photos.isEmpty + ? getDisplayImage(destination).image + : getFinishImage(), + fit: BoxFit.cover)), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Text(getTagText( + false, + destination.tags, + )) + // child: Obx(() => destinationController.photos.isEmpty == true + // ? const Text("撮影してチェックインしてください。") + // : const Text("チェックインをタップしてください。")), + ), + getAction(context), + ], + ), + ), + ); + } else { + // isInRogがfalseの場合は、StartRogainingウィジェットを返します。 + return StartRogaining(); + } + } +} + +// ロゲイニングが開始されていない場合に表示されるウィジェットです。 +// "You have not started rogaining yet."というメッセージと、戻るボタンを表示します。 +// +class StartRogaining extends StatelessWidget { + StartRogaining({super.key}); + + DestinationController destinationController = + Get.find(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + title: Text( + "Not started yet".tr, + ), ), - body: Column( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("You have not started rogaining yet.".tr, + style: const TextStyle(fontSize: 24)), + const SizedBox( + height: 40.0, + ), + ElevatedButton( + onPressed: () { + Get.back(); + destinationController.skipGps = false; + }, + child: const Text("Back"), + ), + ], + ), + ), + ); + } +} + +// 購入ポイントの写真撮影用のウィジェットです。 +// 目的地の画像、タグ、撮影ボタン、完了ボタン、購入なしボタンを表示します。 +// 撮影ボタンをタップすると、カメラが起動します。 +// 完了ボタンをタップすると、購入ポイントの処理が行われます。 +// 購入なしボタンをタップすると、購入ポイントがキャンセルされます。 +// +class SwitchableBuyPointCamera extends StatefulWidget { + final Destination destination; + + const SwitchableBuyPointCamera({super.key, required this.destination}); + + @override + _SwitchableBuyPointCameraState createState() => _SwitchableBuyPointCameraState(); +} + +class _SwitchableBuyPointCameraState extends State { + bool isQRMode = true; + + @override + Widget build(BuildContext context) { + final screenWidth = MediaQuery.of(context).size.width; + final screenHeight = MediaQuery.of(context).size.height; + final qrViewWidth = screenWidth * 2 / 3; + + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + title: Text("${widget.destination.sub_loc_id} : ${widget.destination.name}"), + ), + body: SafeArea( + child: Stack( + children: [ + if (isQRMode) + Column( + children: [ + SizedBox(height: screenHeight * 0.1), + Center( + child: SizedBox( + width: qrViewWidth, + height: qrViewWidth, + child: BuyPointCamera_QR(destination: widget.destination), + ), + ), + const Expanded( + child: Align( + alignment: Alignment.topCenter, + child: Padding( + padding: EdgeInsets.only(top: 16.0), + child: Text( + "岐阜ロゲQRコードにかざしてください。", + style: TextStyle(fontSize: 16), + ), + ), + ), + ), + ], + ) + else + Positioned.fill( + child: BuyPointCamera(destination: widget.destination), + ), + Positioned( + right: 16, + bottom: 16, + child: Container( + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.7), + borderRadius: BorderRadius.circular(20), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(isQRMode ? "カメラへ" : "QRへ"), + Switch( + value: isQRMode, + onChanged: (value) { + setState(() { + isQRMode = value; + }); + }, + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} + + +class BuyPointCamera extends StatelessWidget { + BuyPointCamera({super.key, required this.destination}); + + Destination destination; + DestinationController destinationController = + Get.find(); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Padding( padding: const EdgeInsets.all(8.0), child: Center( - child: Obx(() => - Container( - width: MediaQuery.of(context).size.width, - height: 370, - decoration: BoxDecoration( - image: - DecorationImage( - image: getFinishImage(), - fit: BoxFit.cover - ) - ), - ), + child: Obx( + () => + Container( + width: MediaQuery + .of(context) + .size + .width, + height: 370, + decoration: BoxDecoration( + image: DecorationImage( + // 要修正:getReceiptImage関数の戻り値がnullの場合のエラーハンドリングが不十分です。適切なデフォルト画像を表示するなどの処理を追加してください。 + // + image: getReceiptImage(), fit: BoxFit.cover)), + ), ), ), ), - getAction(context), - ], - ), - ); - } - else { - return StartRogaining(); - } - } -} - -class StartRogaining extends StatelessWidget { - StartRogaining({Key? key}) : super(key: key); - - DestinationController destinationController = Get.find(); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text("Not started yet".tr,), - ), - body: Container( - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text("You have not started rogaining yet.".tr, style: const TextStyle(fontSize: 24)), - const SizedBox(height: 40.0,), - ElevatedButton( - onPressed: (){ - Get.back(); - destinationController.skip_gps = false; - }, - child: const Text("Back"), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text(getTagText(true, destination.tags)), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Wrap( + spacing: 16.0, + runSpacing: 8.0, + children: [ + Obx(() => + ElevatedButton( + onPressed: () { + destinationController.openCamera(context, destination); + }, + style: ElevatedButton.styleFrom( + shape: const CircleBorder(), + padding: const EdgeInsets.all(20), + backgroundColor: destinationController.photos.isEmpty + ? Colors.red + : Colors.grey[300], + ), + child: destinationController.photos.isEmpty + ? const Text("撮影", + style: TextStyle(color: Colors.white)) + : const Text("再撮影", + style: TextStyle(color: Colors.black)), + )), + ElevatedButton( + onPressed: () async { + await destinationController.cancelBuyPoint(destination); + Navigator.of(Get.context!).pop(); + destinationController.rogainingCounted.value = true; + destinationController.skipGps = false; + destinationController.isPhotoShoot.value = false; + }, + child: const Text("買い物なし")), + Obx(() => + destinationController.photos.isNotEmpty + ? ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red), + onPressed: () async { + await destinationController.makeBuyPoint( + destination, + destinationController.photos[0].path); + Get.back(); + destinationController.rogainingCounted.value = true; + destinationController.skipGps = false; + destinationController.isPhotoShoot.value = false; + Get.snackbar("お買い物加点を行いました", + "${destination.sub_loc_id} : ${destination.name}", + backgroundColor: Colors.green, + colorText: Colors.white); + }, + child: const Text("完了", + style: TextStyle(color: Colors.white))) + : Container()) + ], ), - ], - ), + ), + ], ), - ), ); } } -class NotAtGoal extends StatelessWidget { - NotAtGoal({Key? key}) : super(key: key); - DestinationController destinationController = Get.find(); + +class BuyPointCamera_QR extends StatefulWidget { + final Destination destination; + + const BuyPointCamera_QR({super.key, required this.destination}); + + @override + _BuyPointCamera_QRState createState() => _BuyPointCamera_QRState(); +} + + + +class _BuyPointCamera_QRState extends State { + final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); + QRViewController? controller; + bool isQRScanned = false; + + final DestinationController destinationController = Get.find(); + + @override + Widget build(BuildContext context) { + return QRView( + key: qrKey, + onQRViewCreated: _onQRViewCreated, + ); + } + + void _onQRViewCreated(QRViewController controller) { + this.controller = controller; + controller.scannedDataStream.listen((scanData) { + if (!isQRScanned && scanData.code != null && scanData.code!.startsWith('https://rogaining.sumasen.net/api/activate_buy_point/')) { + isQRScanned = true; + _processBuyPoint(); + //_activateBuyPoint(scanData.code!); + } + }); + } + + Future getImageFilePathFromAssets(String assetPath) async { + final byteData = await rootBundle.load(assetPath); + final buffer = byteData.buffer; + Directory tempDir = await getTemporaryDirectory(); + String tempPath = tempDir.path; + var filePath = '$tempPath/temp_qr_receipt.png'; + return (await File(filePath).writeAsBytes( + buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes) + )).path; + } + + + void _processBuyPoint() async { + // アセットの画像をテンポラリファイルにコピー + String predefinedImagePath = await getImageFilePathFromAssets('assets/images/QR_certificate.png'); + + try { + await destinationController.makeBuyPoint(widget.destination, predefinedImagePath); + Get.snackbar('成功', 'お買い物ポイントが有効化されました'); + Navigator.of(context).pop(); + } catch (e) { + Get.snackbar('エラー', 'お買い物ポイントの有効化に失敗しました'); + } finally { + isQRScanned = false; + } + } + + void _activateBuyPoint(String qrCode) async { + final IndexController indexController = Get.find(); + + final userId = indexController.currentUser[0]["user"]["id"]; + final token = indexController.currentUser[0]["token"]; + final teamName = indexController.currentUser[0]["user"]['team_name']; + final eventCode = indexController.currentUser[0]["user"]["event_code"]; + //final cpNumber = destinationController.currentDestinationFeature[0].cp; + final cpNumber = widget.destination.cp; + + try { + final response = await http.post( + Uri.parse('https://rogaining.sumasen.net/api/activate_buy_point/'), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Token $token', + }, + body: jsonEncode({ + 'user_id': userId, + 'team_name': teamName, + 'event_code': eventCode, + 'cp_number': cpNumber, + 'qr_code': qrCode, + }), + ); + + if (response.statusCode == 200) { + Get.snackbar('成功', 'お買い物ポイントが有効化されました'); + Navigator.of(context).pop(); + } else { + Get.snackbar('エラー', 'お買い物ポイントの有効化に失敗しました'); + } + } catch (e) { + Get.snackbar('エラー', 'ネットワークエラーが発生しました'); + } finally { + isQRScanned = false; + } + } + + @override + void dispose() { + controller?.dispose(); + super.dispose(); + } +} + + + +class QRCodeScannerPage extends StatefulWidget { + + QRCodeScannerPage({super.key, required this.destination}); + + Destination destination; + + @override + _QRCodeScannerPageState createState() => _QRCodeScannerPageState(); +} + +class _QRCodeScannerPageState extends State { + final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); + QRViewController? controller; + + @override + void dispose() { + controller?.dispose(); + super.dispose(); + } + + void _onQRViewCreated(QRViewController controller) { + this.controller = controller; + controller.scannedDataStream.listen((scanData) { + // QRコードのデータを処理する + debugPrint("scan data = $scanData"); + String? qrCodeData = scanData.code; + // qrCodeDataを使用してチェックポイントの処理を行う + // 例えば、qrCodeDataからCPのIDと店名を取得し、加点処理を行う + }); + } @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text("Not reached the goal yet".tr,), + body: QRView( + key: qrKey, + onQRViewCreated: _onQRViewCreated, ), - body: Container( - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text("You have not reached the goal yet.".tr, style: const TextStyle(fontSize: 24)), - const SizedBox(height: 40.0,), - ElevatedButton( - onPressed: (){ - Get.back(); - destinationController.skip_gps = false; - }, - child: const Text("Back"), - ), - ], - ), - ), - ), ); } } \ No newline at end of file diff --git a/lib/pages/camera/custom_camera_view.dart b/lib/pages/camera/custom_camera_view.dart new file mode 100644 index 0000000..7684b43 --- /dev/null +++ b/lib/pages/camera/custom_camera_view.dart @@ -0,0 +1,185 @@ +import 'dart:io'; +import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider/path_provider.dart'; +import 'package:gifunavi/model/destination.dart'; + +class CustomCameraView extends StatefulWidget { + final Function(String) onImageCaptured; + final Destination? destination; + + const CustomCameraView({super.key, required this.onImageCaptured, required this.destination}); + + @override + _CustomCameraViewState createState() => _CustomCameraViewState(); +} + +class _CustomCameraViewState extends State { + CameraController? _controller; + late List _cameras; + int _selectedCameraIndex = 0; + double _currentScale = 1.0; + FlashMode _currentFlashMode = FlashMode.off; + Destination? destination; + + @override + void initState() { + super.initState(); + _initializeCamera(); + destination = widget.destination; + } + + Future _initializeCamera() async { + _cameras = await availableCameras(); + _controller = CameraController(_cameras[_selectedCameraIndex], ResolutionPreset.medium); + await _controller!.initialize(); + setState(() {}); + } + + @override + void dispose() { + _controller?.dispose(); + super.dispose(); + } + + Future _toggleCameraLens() async { + final newIndex = (_selectedCameraIndex + 1) % _cameras.length; + await _controller!.dispose(); + + setState(() { + _controller = null; + _selectedCameraIndex = newIndex; + }); + + _controller = CameraController(_cameras[_selectedCameraIndex], ResolutionPreset.medium); + await _controller!.initialize(); + + setState(() {}); + } + + void _toggleFlashMode() { + setState(() { + _currentFlashMode = (_currentFlashMode == FlashMode.off) ? FlashMode.torch : FlashMode.off; + }); + _controller!.setFlashMode(_currentFlashMode); + } + + void _zoomIn() { + setState(() { + _currentScale += 0.1; + if (_currentScale > 5.0) _currentScale = 5.0; + }); + _controller!.setZoomLevel(_currentScale); + } + + void _zoomOut() { + setState(() { + _currentScale -= 0.1; + if (_currentScale < 1.0) _currentScale = 1.0; + }); + _controller!.setZoomLevel(_currentScale); + } + + void _captureImage() async { + if (_controller!.value.isInitialized) { + final Directory appDirectory = await getApplicationDocumentsDirectory(); + final String imagePath = path.join(appDirectory.path, '${DateTime.now()}.jpg'); + + final XFile imageFile = await _controller!.takePicture(); + await imageFile.saveTo(imagePath); + + widget.onImageCaptured(imagePath); + Navigator.pop(context); + } + } + + @override + Widget build(BuildContext context) { + if (_controller == null || !_controller!.value.isInitialized) { + return Container(); + } + + return Stack( + children: [ + Padding( + padding: const EdgeInsets.only(top: 60.0), // 上部に60ピクセルのパディングを追加 + child: CameraPreview(_controller!), + ), + Positioned( + bottom: 120.0, + left: 16.0, + right: 16.0, + child: Center( + child: Text( + destination?.tags ?? '', + style: const TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + Positioned( + bottom: 16.0, + left: 16.0, + right: 16.0, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + onPressed: _toggleFlashMode, + icon: Icon( + (_currentFlashMode == FlashMode.off) ? Icons.flash_off : Icons.flash_on, + color: Colors.white, + ), + iconSize: 32, + color: Colors.orange, + ), + GestureDetector( + onTap: _captureImage, + child: Container( + height: 80, + width: 80, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.white, + border: Border.all(color: Colors.red, width: 4), + ), + child: const Icon(Icons.camera_alt, color: Colors.red, size: 40), + ), + ), + IconButton( + onPressed: _toggleCameraLens, + icon: const Icon(Icons.flip_camera_ios, color: Colors.white), + iconSize: 32, + color: Colors.blue, + ), + ], + ), + ), + Positioned( + top: 16.0, + right: 16.0, + child: Column( + children: [ + IconButton( + onPressed: _zoomIn, + icon: const Icon(Icons.zoom_in, color: Colors.white), + iconSize: 32, + color: Colors.green, + ), + IconButton( + onPressed: _zoomOut, + icon: const Icon(Icons.zoom_out, color: Colors.white), + iconSize: 32, + color: Colors.green, + ), + ], + ), + ), + ], + ); + } +} \ No newline at end of file diff --git a/lib/pages/category/category_page.dart b/lib/pages/category/category_page.dart index 12d7cb9..fee3118 100644 --- a/lib/pages/category/category_page.dart +++ b/lib/pages/category/category_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; class CategoryPage extends StatelessWidget { - const CategoryPage({Key? key}) : super(key: key); + const CategoryPage({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/changepassword/change_password_page.dart b/lib/pages/changepassword/change_password_page.dart index 3c6798c..6de73ae 100644 --- a/lib/pages/changepassword/change_password_page.dart +++ b/lib/pages/changepassword/change_password_page.dart @@ -1,9 +1,12 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:rogapp/pages/index/index_controller.dart'; +import 'package:gifunavi/pages/index/index_controller.dart'; +import 'package:gifunavi/widgets/debug_widget.dart'; class ChangePasswordPage extends StatelessWidget { - ChangePasswordPage({Key? key}) : super(key: key); + ChangePasswordPage({super.key}); + + LogManager logManager = LogManager(); IndexController indexController = Get.find(); @@ -13,150 +16,174 @@ class ChangePasswordPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - resizeToAvoidBottomInset: false, + resizeToAvoidBottomInset: false, backgroundColor: Colors.white, appBar: AppBar( elevation: 0, - backgroundColor: Colors.white, - leading: - IconButton( onPressed: (){ - Navigator.pop(context); - },icon:const Icon(Icons.arrow_back_ios,size: 20,color: Colors.black,)), + leading: IconButton( + onPressed: () { + logManager.addOperationLog('User clicked cancel button on the drawer'); + Navigator.pop(context); + }, + icon: const Icon( + Icons.arrow_back_ios, + size: 20, + color: Colors.black, + )), ), - body: - SizedBox( - width: double.infinity, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Column( - children: [ - Column( - children: [ - Container( - child: Text("change_password".tr, style: const TextStyle(fontSize: 24.0),), - ), - const SizedBox(height: 30,), - ], - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 40 - ), - child: Column( + body: SizedBox( + width: double.infinity, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Column( + children: [ + Column( children: [ - makeInput(label: "old_password".tr, controller: oldPasswordController), - makeInput(label: "new_password".tr, controller: newPasswordController, obsureText: true), + Text( + "change_password".tr, + style: const TextStyle(fontSize: 24.0), + ), + const SizedBox( + height: 30, + ), ], ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 40), - child: Container( - padding: const EdgeInsets.only(top: 3,left: 3), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(40), - ), - child: Obx((() => - indexController.is_loading == true ? MaterialButton( - minWidth: double.infinity, - height:60, - onPressed: (){ - - }, - color: Colors.grey[400], - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(40) - ), - child: const CircularProgressIndicator(), - ) : - Column( - children: [ - MaterialButton( - minWidth: double.infinity, - height:60, - onPressed: (){ - if(oldPasswordController.text.isEmpty || newPasswordController.text.isEmpty){ - Get.snackbar( - "no_values".tr, - "values_required".tr, - icon: const Icon(Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue), - snackPosition: SnackPosition.TOP, - duration: const Duration(milliseconds: 800), - backgroundColor: Colors.yellow, - //icon:Image(image:AssetImage("assets/images/dora.png")) - ); - return; - } - indexController.is_loading.value = true; - indexController.changePassword(oldPasswordController.text, newPasswordController.text, context); - //indexController.login(oldPasswordController.text, newPasswordController.text, context); - }, - color: Colors.indigoAccent[400], - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(40) - ), - child: const Text("ログイン",style: TextStyle( - fontWeight: FontWeight.w600,fontSize: 16,color: Colors.white70 - ), - ), - ), - const SizedBox(height: 10.0,), - - ], - ) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: Column( + children: [ + makeInput( + label: "old_password".tr, + controller: oldPasswordController), + makeInput( + label: "new_password".tr, + controller: newPasswordController, + obsureText: true), + ], ), ), - ) - ), - const SizedBox(height: 20,), - const Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - - ], - ) - ], - - ), - ], - ), - ) - ); + Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: Container( + padding: const EdgeInsets.only(top: 3, left: 3), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(40), + ), + child: Obx( + (() => indexController.isLoading.value == true + ? MaterialButton( + minWidth: double.infinity, + height: 60, + onPressed: () {}, + color: Colors.grey[400], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(40)), + child: const CircularProgressIndicator(), + ) + : Column( + children: [ + MaterialButton( + minWidth: double.infinity, + height: 60, + onPressed: () { + if (oldPasswordController + .text.isEmpty || + newPasswordController + .text.isEmpty) { + logManager.addOperationLog('User tried to login with blank old password ${oldPasswordController.text} or new password ${newPasswordController.text}.'); + Get.snackbar( + "no_values".tr, + "values_required".tr, + backgroundColor: Colors.red, + colorText: Colors.white, + icon: const Icon( + Icons.assistant_photo_outlined, + size: 40.0, + color: Colors.blue), + snackPosition: SnackPosition.TOP, + duration: const Duration( + milliseconds: 800), + //backgroundColor: Colors.yellow, + //icon:Image(image:AssetImage("assets/images/dora.png")) + ); + return; + } + indexController.isLoading.value = true; + indexController.changePassword( + oldPasswordController.text, + newPasswordController.text, + context); + //indexController.login(oldPasswordController.text, newPasswordController.text, context); + }, + color: Colors.indigoAccent[400], + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(40)), + child: Text( + "login".tr, + style: const TextStyle( + fontWeight: FontWeight.w600, + fontSize: 16, + color: Colors.white70), + ), + ), + const SizedBox( + height: 10.0, + ), + ], + )), + ), + )), + const SizedBox( + height: 20, + ), + const Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [], + ) + ], + ), + ], + ), + )); } - Widget makeInput({label, required TextEditingController controller, obsureText = false}){ - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(label,style:const TextStyle( - fontSize: 15, - fontWeight: FontWeight.w400, - color: Colors.black87 - ),), - const SizedBox(height: 5,), - TextField( - controller: controller, - obscureText: obsureText, - decoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric(vertical: 0,horizontal: 10), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: (Colors.grey[400])!, + Widget makeInput( + {label, required TextEditingController controller, obsureText = false}) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: const TextStyle( + fontSize: 15, fontWeight: FontWeight.w400, color: Colors.black87), + ), + const SizedBox( + height: 5, + ), + TextField( + controller: controller, + obscureText: obsureText, + decoration: InputDecoration( + contentPadding: + const EdgeInsets.symmetric(vertical: 0, horizontal: 10), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: (Colors.grey[400])!, + ), + ), + border: OutlineInputBorder( + borderSide: BorderSide(color: (Colors.grey[400])!), ), ), - border: OutlineInputBorder( - borderSide: BorderSide(color: (Colors.grey[400])! - ), ), - ), - ), - const SizedBox(height: 30.0,) - ], - ); + const SizedBox( + height: 30.0, + ) + ], + ); + } } - - -} \ No newline at end of file diff --git a/lib/pages/city/city_page.dart b/lib/pages/city/city_page.dart index 1682025..67efe54 100644 --- a/lib/pages/city/city_page.dart +++ b/lib/pages/city/city_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; class CityPage extends StatelessWidget { - const CityPage({Key? key}) : super(key: key); + const CityPage({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/pages/debug/debug_binding.dart b/lib/pages/debug/debug_binding.dart new file mode 100644 index 0000000..7160fde --- /dev/null +++ b/lib/pages/debug/debug_binding.dart @@ -0,0 +1,9 @@ +import 'package:get/get.dart'; +import 'package:gifunavi/pages/debug/debug_controller.dart'; + +class DebugBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => DebugController()); + } +} \ No newline at end of file diff --git a/lib/pages/debug/debug_controller.dart b/lib/pages/debug/debug_controller.dart new file mode 100644 index 0000000..c85693e --- /dev/null +++ b/lib/pages/debug/debug_controller.dart @@ -0,0 +1,47 @@ +import 'package:geolocator/geolocator.dart'; +import 'package:get/get.dart'; +import 'package:gifunavi/utils/location_controller.dart'; + +class DebugController extends GetxController { + final LocationController locationController = Get.find(); + final gpsSignalStrength = 'high'.obs; + final currentPosition = Rx(null); + final isSimulationMode = false.obs; + + @override + void onInit() { + super.onInit(); + // 位置情報の更新を監視 + locationController.locationMarkerPositionStream.listen((position) { + if (position != null) { + currentPosition.value = Position( + latitude: position.latitude, + longitude: position.longitude, + accuracy: position.accuracy, + altitudeAccuracy: 30, + headingAccuracy: 30, + heading: 0, + altitude: 0, + speed: 0, + speedAccuracy: 0, + timestamp: DateTime.now(), + ); + } + }); + } + + void setGpsSignalStrength(String value) { + gpsSignalStrength.value = value; + locationController.setSimulatedSignalStrength(value); + } + + void toggleSimulationMode() { + isSimulationMode.value = !isSimulationMode.value; + locationController.setSimulationMode(isSimulationMode.value); + if (!isSimulationMode.value) { + // 標準モードに切り替えた場合は、シミュレートされた信号強度をリセット + locationController.setSimulatedSignalStrength('low'); + gpsSignalStrength.value = 'low'; + } + } +} \ No newline at end of file diff --git a/lib/pages/debug/debug_page.dart b/lib/pages/debug/debug_page.dart new file mode 100644 index 0000000..19fbc93 --- /dev/null +++ b/lib/pages/debug/debug_page.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:gifunavi/pages/debug/debug_controller.dart'; + +class DebugPage extends GetView { + const DebugPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('デバッグモード'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('GPS信号強度'), + const SizedBox(height: 20), + Obx( + () => DropdownButton( + value: controller.gpsSignalStrength.value, + onChanged: (value) { + controller.setGpsSignalStrength(value!); + }, + items: ['high', 'medium', 'low'] + .map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ), + ), + const SizedBox(height: 20), + const Text('現在のGPS座標'), + const SizedBox(height: 10), + Obx( + () => Text( + '緯度: ${controller.currentPosition.value?.latitude ?? '-'}, 経度: ${controller.currentPosition.value?.longitude ?? '-'}', + ), + ), + const SizedBox(height: 20), + const Text('現在のGPS精度'), + const SizedBox(height: 10), + Obx( + () => Text( + '精度: ${controller.currentPosition.value?.accuracy.toStringAsFixed(2) ?? '-'} m', + ), + ), + const SizedBox(height: 20), + Obx( + () => ElevatedButton( + onPressed: controller.toggleSimulationMode, + child: Text(controller.isSimulationMode.value + ? 'シミュレーションモード' + : '標準モード'), + ), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/pages/destination/destination_binding.dart b/lib/pages/destination/destination_binding.dart index 8fab747..ac36fa5 100644 --- a/lib/pages/destination/destination_binding.dart +++ b/lib/pages/destination/destination_binding.dart @@ -1,9 +1,11 @@ import 'package:get/get.dart'; -import 'package:rogapp/pages/destination/destination_controller.dart'; +import 'package:gifunavi/main.dart'; +import 'package:gifunavi/pages/destination/destination_controller.dart'; class DestinationBinding extends Bindings { @override void dependencies() { Get.put(DestinationController()); + restoreGame(); } } diff --git a/lib/pages/destination/destination_controller.dart b/lib/pages/destination/destination_controller.dart index 0416732..931657f 100644 --- a/lib/pages/destination/destination_controller.dart +++ b/lib/pages/destination/destination_controller.dart @@ -1,84 +1,250 @@ import 'dart:io'; - -import 'package:camera_camera/camera_camera.dart'; -import 'package:flutter/foundation.dart'; +import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; -import 'package:geojson/geojson.dart'; +import 'package:flutter_map_location_marker/flutter_map_location_marker.dart'; +import 'package:geojson_vi/geojson_vi.dart'; import 'package:geolocator/geolocator.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:latlong2/latlong.dart'; -import 'package:rogapp/model/destination.dart'; -import 'package:rogapp/pages/camera/camera_page.dart'; -import 'package:rogapp/pages/index/index_controller.dart'; -import 'package:rogapp/routes/app_pages.dart'; -import 'package:rogapp/services/destination_service.dart'; -import 'package:rogapp/services/external_service.dart'; -import 'package:rogapp/services/location_service.dart'; -import 'package:rogapp/services/maxtrix_service.dart'; -import 'package:rogapp/services/perfecture_service.dart'; -import 'package:rogapp/utils/database_helper.dart'; -import 'package:rogapp/widgets/bottom_sheet_new.dart'; +import 'package:gifunavi/main.dart'; +import 'package:gifunavi/model/destination.dart'; +import 'package:gifunavi/model/gps_data.dart'; +import 'package:gifunavi/pages/camera/camera_page.dart'; +import 'package:gifunavi/pages/camera/custom_camera_view.dart'; +import 'package:gifunavi/pages/index/index_controller.dart'; +import 'package:gifunavi/routes/app_pages.dart'; +import 'package:gifunavi/services/DatabaseService.dart'; +import 'package:gifunavi/services/destination_service.dart'; +import 'package:gifunavi/services/external_service.dart'; +import 'package:gifunavi/services/location_service.dart'; +import 'package:gifunavi/services/maxtrix_service.dart'; +import 'package:gifunavi/services/perfecture_service.dart'; +import 'package:gifunavi/utils/database_gps.dart'; +import 'package:gifunavi/utils/database_helper.dart'; +import 'package:gifunavi/utils/location_controller.dart'; +import 'package:gifunavi/widgets/bottom_sheet_new.dart'; import 'dart:async'; +import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; +import 'package:gifunavi/widgets/debug_widget.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:image_gallery_saver/image_gallery_saver.dart'; + +import 'package:gifunavi/pages/permission/permission.dart' ; + +// 目的地に関連する状態管理とロジックを担当するクラスです。 +// class DestinationController extends GetxController { - late LocationSettings locationSettings; + late LocationSettings locationSettings; // 位置情報の設定を保持する変数です。 - var destinationCount = 0.obs; - List destinations = [].obs; - double current_lat = 0.0; - double current_lon = 0.0; + //late TeamController teamController = TeamController(); + //Timer? _GPStimer; // GPSタイマーを保持する変数です。 - bool skip_10s = false; + var destinationCount = 0.obs; // 目的地の数を保持するReactive変数です。 + List destinations = [].obs; // 目的地のリストを保持するObservable変数です。 + double currentLat = 0.0; // 現在の緯度と経度を保持する変数です。 + double currentLon = 0.0; + double lastValidLat = 0.0; // 最後に中・強信号で拾ったGPS位置。 + // ロゲ開始を屋内でやったら 0 のままなので、屋外で行うこと。 + double lastValidLon = 0.0; + DateTime lastGPSCollectedTime = DateTime.now(); // 最後にGPSデータが収集された時刻を保持する変数です。 - List currentSelectedDestinations = [].obs; + bool shouldShowBottomSheet = true; // ボトムシートを表示すべきかどうかを示すフラグです。 - var is_in_checkin = false.obs; - var is_in_rog = false.obs; - var is_at_start = false.obs; - var is_at_goal = false.obs; - var is_photo_shoot = false.obs; - DateTime last_goal_at = DateTime.now().subtract(const Duration(days: 1)); + static bool gps_push_started = false; // ゲームの状態を示す静的変数です。 + static bool game_started = false; + static bool ready_for_goal = false; + + bool skip_10s = false; // 10秒間のスキップフラグを示す変数です。 + + List currentSelectedDestinations = [].obs; // 現在選択されている目的地のリストを保持するObservable変数です。 + + var isInCheckin = false.obs; // ゲームの状態を示すReactive変数です。 + var isInRog = false.obs; + var isAtStart = false.obs; + var isAtGoal = false.obs; + var isPhotoShoot = false.obs; + + DateTime lastGoalAt = DateTime.now().subtract(const Duration(days: 1)); // 最後にゴールした時刻を保持する変数です。 //List rogainings = [].obs; - bool checking_in = false; - var is_gps_selected = true.obs; - BuildContext? context; + bool checkingIn = false; // チェックイン中かどうかを示すフラグです。 + var isGpsSelected = true.obs; // GPSが選択されているかどうかを示すReactive変数です。 + BuildContext? context; // ビルドコンテキストを保持する変数です。 - List gps = ["-- stating --"].obs; + List gps = ["-- stating --"].obs; // GPSと位置情報の許可に関する情報を保持するObservable変数です。 List locationPermission = [" -- starting -- "].obs; - var travelMode = 0.obs; + var travelMode = 0.obs; // 移動モードを保持するReactive変数です。 - bool skip_gps = false; + bool skipGps = false; // GPSをスキップするかどうかを示すフラグです。 + bool okToUseGPS = false; // 最新のGPS情報を使用して良いかを示すフラグ。 - Map matrix = {}; + Map matrix = {}; // 行列データを保持する変数です。 - final photos = [].obs; + final photos = [].obs; // 写真のリストを保持するReactive変数です。 - final IndexController indexController = Get.find(); + final IndexController indexController = Get.find(); // IndexControllerのインスタンスを保持する変数です。 + final LocationController locationController = Get.put(LocationController()); // LocationControllerのインスタンスを保持する変数です。 + final DatabaseService dbService = DatabaseService(); // DatabaseServiceのインスタンスを保持する変数です。 - Timer? _timer; - int _start = 0; - int chekcs = 0; - var rogaining_counted = false.obs; + int _start = 0; // 開始時刻を保持する変数です。 + int chekcs = 0; // チェックポイントの数を保持する変数です。 + var rogainingCounted = false.obs; // ロゲイニングがカウントされたかどうかを示すReactive変数です。 + // destinationController.rogainingCountedは、現在のロゲイニングセッションでポイントがカウントされたかどうかを管理するフラグです。 + // + // このフラグは以下のような状況で使用されます: + // + // ロゲイニングを開始したとき、rogainingCountedはfalseに初期化されます。これは、まだポイントがカウントされていないことを示します。 + // チェックポイントに到着し、チェックインが成功したとき、rogainingCountedはtrueに設定されます。これは、そのセッションでポイントがカウントされたことを示します。 + // ロゲイニングを終了したとき、rogainingCountedは再びfalseに設定されます。これは、次のセッションに備えてフラグをリセットするためです。 + // このフラグは、主に以下の目的で使用されます: + // + // ゴール地点でのロジックの制御:rogainingCountedがtrueの場合、つまりポイントがカウントされている場合にのみ、ゴール処理を実行できます。 + // UI の更新:rogainingCountedの状態に基づいて、適切なメッセージやボタンを表示することができます。 + + bool isMapControllerReady = false; + + LatLng lastValidGPSLocation = const LatLng(0, 0); + DateTime lastGPSDataReceivedTime = DateTime.now(); + DateTime lastPopupShownTime = DateTime.now().subtract(const Duration(minutes: 10)); + bool isPopupShown = false; + bool hasReceivedGPSData = true; + + var isCheckingIn = false.obs; // チェックイン操作中はtrueになり、重複してポップアップが出ないようにするもの。 + + var isRouteShowing = false.obs; // ルートが表示されているかどうかを示すReactive変数 + /* + //==== Akira .. GPS信号シミュレーション用 ===== ここから、2024-4-5 + // + + bool kDebugMode = true; + + // シミュレーションモードのフラグ + RxBool isSimulationMode = RxBool(true); + + // シミュレーションモードを切り替えるための関数 + void toggleSimulationMode(bool value) { + isSimulationMode.value = value; + } + + // 現在位置の取得メソッドを追加 + LatLng getCurrentLocation() { + return LatLng(lastValidLat, lastValidLon); + } + + // + // GPS信号の強弱を判断するメソッドを追加します。 + // + String getGpsSignalStrength() { + // デバッグモードかつシミュレーションモードの場合は、シミュレートされた信号強度を返す + print("kDebugMode : ${kDebugMode}, isSimulationMode : ${isSimulationMode.value}"); + if (kDebugMode && isSimulationMode.value) { + return locationController.getSimulatedSignalStrength(); + } + + // 通常モードの場合は、実際の信号強度を返す + final accuracy = locationController.currentPosition.value?.accuracy ?? double.infinity; + if (accuracy <= 10) { + return 'high'; + } else if (accuracy <= 30) { + return 'medium'; + } else { + return 'low'; + } + } + + // + //==== Akira .. GPS信号シミュレーション用 ======= ここまで + */ + + + // ルートをクリアする関数です。 + void clearRoute() { + indexController.routePoints.clear(); + indexController.routePointLenght.value = 0; + isRouteShowing.value = false; + } + + void showGPSDataNotReceivedPopup() { + if (Get.context != null) { + Get.dialog( + AlertDialog( + title: const Text('GPS信号が受信できません'), + content: const Text('GPS信号が受信できる場所に移動してください。'), + actions: [ + TextButton( + onPressed: () => Get.back(), + child: const Text('OK'), + ), + ], + ), + ); + } else { + // Get.contextがnullの場合の処理を追加 + print('GPS signal not received, but context is null'); + } + } + + // 最後に有効なGPSデータを受け取ってから10分以上経過している場合にのみメッセージを表示するようにします。 + // + void checkGPSDataReceived() { + if (!hasReceivedGPSData) { + //debugPrint("GPS信号を全く受信していない。"); + if (!isPopupShown) { + // ポップアップしていない。 + showGPSDataNotReceivedPopup(); + lastPopupShownTime = DateTime.now(); + isPopupShown = true; + } + } else { + if (DateTime.now().difference(lastGPSDataReceivedTime).inSeconds >= 600) { + // 前回GPS信号を受信してから10分経過。 + if (!isPopupShown && DateTime.now().difference(lastPopupShownTime).inMinutes >= 3) { + // 前回ポップアップしてから3分経過してなければ + showGPSDataNotReceivedPopup(); + lastPopupShownTime = DateTime.now(); + isPopupShown = true; + } + } else { + isPopupShown = false; + } + } + + } + + // 日時をフォーマットされた文字列に変換する関数です。 + // String getFormatedTime(DateTime datetime) { return DateFormat('yyyy-MM-dd HH:mm:ss').format(datetime); } - Destination festuretoDestination(GeoJsonFeature fs) { - GeoJsonMultiPoint mp = fs.geometry as GeoJsonMultiPoint; - LatLng pt = LatLng(mp.geoSerie!.geoPoints[0].latitude, - mp.geoSerie!.geoPoints[0].longitude); + // 追加:Akira 2024-4-5 + // GPS信号の精度が一定値以上の場合、GPS信号が弱いと判断する + // + bool isGpsSignalWeak() { + final accuracy = locationController.currentPosition.value?.accuracy; + if (accuracy == null) { + return true; // 位置情報が取得できていない場合、GPS信号が弱いと見なす + } + return accuracy > 60; + //return locationController.currentPosition.value?.accuracy ?? double.infinity > 50; + } + + // + Destination festuretoDestination(GeoJSONFeature fs) { + GeoJSONMultiPoint mp = fs.geometry as GeoJSONMultiPoint; + LatLng pt = LatLng(mp.coordinates[0][1], mp.coordinates[0][0]); //print("----- ${indexController.currentFeature[0].properties} -----"); return Destination( name: fs.properties!["location_name"], + sub_loc_id: fs.properties!["sub_loc_id"], address: fs.properties!["address"], phone: fs.properties!["phone"], email: fs.properties!["email"], @@ -98,263 +264,520 @@ class DestinationController extends GetxController { buy_point: fs.properties!["buy_point"], selected: false, checkedin: false, - hidden_location: fs.properties!["hidden_location"] == true ? 1 : 0); + hidden_location: fs.properties!["hidden_location"] == true ? 1 : 0, + tags: fs.properties!["tags"]); } - void startTimerLocation(GeoJsonFeature fs, double distance) { - print("---- in startTimer ----"); - //skip_gps = true; + // 指定された目的地の位置情報に基づいてタイマーを開始する関数です。 + // CP情報(fs)と現在位置からCPまでの距離distance を引数として渡します。 + // + Future startTimerLocation(GeoJSONFeature fs, double distance) async { + //print("---- in startTimer ----"); + // print("---- is in rog is $is_in_rog ----"); + double checkinRadious = fs.properties!['checkin_radius'] ?? double.infinity; + // CPのcheckin_radiusを取得し、checkinRadius に代入。値がなければinfinityとする。 + if (checkinRadious >= distance) { + // checkinRadious以内に入ったら、 + indexController.currentFeature.clear(); + // indexController.currentFeatureを空にします。 + Destination d = festuretoDestination(fs); - for (Destination de in destinations) { - if (de.location_id == d.location_id) { - d = de; - break; - } - } + // festuretoDestination(fs)を呼び出し、GeoJSONFeatureオブジェクトfsからDestinationオブジェクトdを作成します。 + + // print("----- destination lenght is ${destinations.length} -----"); + indexController.currentFeature.add(fs); - print("---- before calling startTimer ----"); - startTimer(d, distance); + // indexController.currentFeatureにfsを追加します。 + + //print("---- before calling startTimer ----"); + await startTimer(d, distance); + // startTimer(d, distance)を非同期で呼び出し、その完了を待機します。 + + return; } } - void CallforCheckin(Destination d) { - bool autoCheckin = d.auto_checkin == 0 ? false : true; - if (autoCheckin) { - if (!checking_in) { - print( - "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ make checkin ${d.sub_loc_id}@@@@@@@@@@@"); - makeCheckin(d, true, ""); - if (d.cp != -1) { - rogaining_counted.value = true; - } - skip_gps = false; - } - } else { - print("--- hidden loc ${d.hidden_location} ----"); - // ask for checkin - if (d.hidden_location != null && - d.hidden_location == 0 && - is_in_rog.value == true && - d.cp != -1) { - chekcs = 3; - is_in_checkin.value = true; - photos.clear(); - showModalBottomSheet( - context: Get.context!, - isScrollControlled: true, - builder: ((context) => CameraPage( - destination: d, - ))).whenComplete(() { - skip_gps = false; - rogaining_counted.value = true; - chekcs = 0; - is_in_checkin.value = false; - }); - } else { - Get.snackbar("始まっていない", "ロゲイニングを始める必要があります"); - } - //else if(is_in_rog.value == true && d.cp != -1){ - // chekcs = 4; - // is_in_checkin.value = true; - // showMaterialModalBottomSheet( - // expand: true, - // context: Get.context!, - // backgroundColor: Colors.transparent, - // builder: (context) => BottomSheetNew() - // ).whenComplete(() { - // skip_gps = false; - // chekcs = 0; - // is_in_checkin.value = false; - // }); - // showModalBottomSheet(context: Get.context!, isScrollControlled: true, - // builder:((context) => BottomSheetNew()) - // ).whenComplete((){ - // skip_gps = false; - // chekcs = 0; - // is_in_checkin.value = false; - // }); - //} - } - } - - void startTimer(Destination d, double distance) async { - print("=== passed dest is ${d.location_id} ${d.checkedin} ===="); - skip_gps = true; - print("---- in startTimer ----"); - double checkinRadious = d.checkin_radious ?? double.infinity; - bool autoCheckin = d.auto_checkin == 0 ? false : true; - bool locationAlreadyCheckedIn = d.checkedin ?? false; - bool isuserLoggedIn = indexController.currentUser.isNotEmpty ? true : false; - //make current destination - print("---- checkin_radious $checkinRadious ----"); - print("---- distance $distance ----"); - if (checkinRadious >= distance) { - //currentSelectedDestinations.add(d); - indexController.currentDestinationFeature.clear(); - indexController.currentDestinationFeature.add(d); - - print( - "---- checked in as ${indexController.currentDestinationFeature[0].checkedin.toString()} ----"); - } else { - skip_gps = false; - return; - } - - if (is_photo_shoot.value == true) { - photos.clear(); - showModalBottomSheet( - context: Get.context!, - isScrollControlled: true, - builder: ((context) => CameraPage())).whenComplete(() { - skip_gps = false; - chekcs = 0; - is_in_checkin.value = false; - }); - return; - } + // 指定された目的地に対してタイマーを開始する関数です。 + // 目的地の位置情報を取得し、チェックイン半径内にいるかどうかを確認します。 + // 写真撮影モードの場合は、ボトムシートを表示して写真撮影を行います。 + // 目的地がデータベースに存在しない場合は、新しい目的地としてデータベースに挿入します。 + // 目的地に応じて、チェックイン、ゴール、買い物ポイントの処理を行います。 + // + // 2024-4-8 akira: GPS信号が弱い場合でも、最後に取得した位置情報を使用してチェックインやゴールの処理を続行できるようになります。また、チェックインやゴールの処理では、GPS信号の精度チェックを緩和することで、GPS信号が弱い場合でもボタンを押せるようになります。 + // + // 要検討:エラーが発生した場合のエラーハンドリングを追加し、適切なメッセージを表示することを検討してください。 + // + // 引数:CPオブジェクトと現在地からCPまでの距離を渡す。 + // + Future startTimer(Destination d, double distance) async { + //print("=== passed dest is ${d.location_id} ${d.checkedin} ===="); + skipGps = true; + //debugPrint("---- in startTimer ----"); DatabaseHelper db = DatabaseHelper.instance; List ds = await db.getDestinationByLatLon(d.lat!, d.lon!); - if (ds.isEmpty) { - print("----- in location popup cp - ${d.cp}----"); - if (d.cp == -1 && DateTime.now().difference(last_goal_at).inHours >= 24) { - chekcs = 1; - //start - print("---- in start -----"); - chekcs = 1; - is_in_checkin.value = true; - is_at_start.value = true; - showModalBottomSheet( - context: Get.context!, - isScrollControlled: true, - builder: ((context) => BottomSheetNew())).whenComplete(() { - skip_gps = false; - chekcs = 0; - is_at_start.value = false; - is_in_checkin.value = false; - }); - } else if (is_in_rog.value == true && indexController.rog_mode == 1) { - print("----- in location popup checkin cp - ${d.cp}----"); - chekcs = 2; - is_in_checkin.value = true; - showModalBottomSheet( - context: Get.context!, - isScrollControlled: true, - builder: ((context) => BottomSheetNew())).whenComplete(() { - skip_gps = false; - chekcs = 0; - is_in_checkin.value = false; - }); + //指定位置のオブジェクトのリストを取得。 + Destination? dss; + if (ds.isNotEmpty) { + dss = ds.first; // 取得したリストが空でない場合、dss変数に最初の要素を代入します。 + } + + // 変数を計算 + double checkinRadious = d.checkin_radious ?? double.infinity; // 反応半径 + bool autoCheckin = d.auto_checkin == 0 ? false : true; // 自動チェックイン + bool buyPoint = dss != null && dss.buy_point != null && dss.buy_point! > 0 // 買い物ポイント + ? true + : false; + bool buyPointImageAdded = // 買い物画像 + dss != null && dss.buypoint_image != null ? true : false; + bool buyPointCanceled = // 買い物キャンセル + dss != null && dss.buy_point != null && dss.buy_point == 0 + ? true + : false; + bool locationAlreadyCheckedIn = // チェックイン済みか + ds.isNotEmpty && ds[0].checkedin == true ? true : false; + bool isuserLoggedIn = indexController.currentUser.isNotEmpty ? true : false; // ログイン済みか + + /* + // スタートとゴールは除外 + debugPrint("startTimer CP=${d.cp}"); + if (d.cp == -1 || d.cp == 0 || d.cp == -2) { + skipGps = false; + return; + } + + */ + + // 初期化。GPS信号が強くても弱くても + if (checkinRadious >= distance || checkinRadious == -1) { + //currentSelectedDestinations.add(d); + // 目的地として登録する。 + //debugPrint("目的地の初期化"); + indexController.currentDestinationFeature.clear(); + indexController.currentDestinationFeature.add(d); + + // print( + // "---- checked in as ${indexController.currentDestinationFeature[0].checkedin.toString()} ----"); + } else { + // ここには来ないのでは? + debugPrint("検出範囲外..."); + + // GPS信号が弱い場合でも、チェックインやゴールの処理を続行する + // comment out by Akira, 2024-4-5 + // skipGps = false; + // return; + // GPS信号が弱い場合、最後に取得した高いまたは中程度の位置情報を使用 + if (okToUseGPS) { + double lastValidDistance = Geolocator.distanceBetween( + lastValidLat, lastValidLon, + d.lat!, d.lon! + ); + /* + double lastValidDistance = distance.as( + LengthUnit.Meter, + LatLng(lastValidLat, lastValidLon), + LatLng(d.lat!, d.lon!), + ); + */ + + if (checkinRadious >= lastValidDistance || checkinRadious == -1) { // 反応半径内か、距離無視CPなら + indexController.currentDestinationFeature.clear(); + indexController.currentDestinationFeature.add(d); + } else { + skipGps = false; + return; + } + } else { + skipGps = false; + return; } } - print("---- location checkin radious ${d.checkin_radious} ----"); - print("---- already checked in $locationAlreadyCheckedIn ----"); - if (checkinRadious >= distance && - locationAlreadyCheckedIn == false && - is_in_rog.value == true) { - CallforCheckin(d); + if (isPhotoShoot.value == true) { // 写真撮影するなら ... isPhotoShoot=True にしてる場所がない。 + debugPrint("isPhotoShoot.value == true ... will camera popup"); + photos.clear(); // まず既存の写真をクリア + if (shouldShowBottomSheet) { // ボトムシートを使うべきなら + shouldShowBottomSheet = false; + if (d.cp == -1) return; // CPは開始点なら戻る。 + + // カメラページをポップアップ + await showModalBottomSheet( + constraints: + BoxConstraints.loose(Size(Get.width, Get.height * 0.75)), + context: Get.context!, + isScrollControlled: true, + builder: ((context) => CameraPage(destination: d))) + .whenComplete(() { + shouldShowBottomSheet = true; + skipGps = false; + chekcs = 0; + isInCheckin.value = false; + }); + } + return; } - print("---- cp --- ${d.cp} -----"); - print("--- at goal $is_at_goal ---"); - print("--- rog counted $rogaining_counted ---"); - print("--- loc already checked in $locationAlreadyCheckedIn ---"); - print( - "==== date diff is ${DateTime.now().difference(last_goal_at).inHours} ===="); - if (isuserLoggedIn && - d.cp == -1 && - locationAlreadyCheckedIn && - skip_10s == false) { - //check for rogaining - if (is_at_goal.value == false && rogaining_counted.value) { - //goal - print("---- in goal -----"); - chekcs = 5; - is_at_goal.value = true; - photos.clear(); - showModalBottomSheet( + + // 写真撮影モードでない場合 + + if (ds.isEmpty) { + debugPrint("* 目的地がない場合 ==> 検知半径=-1の場合"); + + // print("----- in location popup cp - ${d.cp}----"); + if ((d.cp == -1 || d.cp==0 ) && DateTime.now().difference(lastGoalAt).inHours >= 10) { + debugPrint("**1: 開始CPで、最後にゴールしてから24時間経過していれば、"); + + chekcs = 1; + //start + // print("~~~~ calling start ~~~~"); + print("---- in start -----"); + + chekcs = 1; // スタート地点で前のゴールから24時間経過 + + isInCheckin.value = true; + isAtStart.value = true; + if (shouldShowBottomSheet) { + shouldShowBottomSheet = false; // bottom_sheet を起動させない。 + + Widget bottomSheet = BottomSheetNew(destination: d); + + await showModalBottomSheet( + constraints: + BoxConstraints.loose(Size(Get.width, Get.height * 0.85)), + context: Get.context!, + isScrollControlled: true, + builder: ((context) => bottomSheet) + ).whenComplete(() { + shouldShowBottomSheet = true; // bottom_sheet 起動許可 + skipGps = false; + chekcs = 0; // ボトムシートモード=1, + isAtStart.value = false; + isInCheckin.value = false; + }); + } + return; + // 以下の条件分岐を追加 + } else if (ds.isNotEmpty && ds[0].checkedin == true) { + // 目的地がDBに存在し、すでにチェックインしている場合は自動ポップアップを表示しない + debugPrint("チェックイン済み"); + return; + + } else if (isInRog.value == true && + indexController.rogMode.value == 1 && + (locationAlreadyCheckedIn==false) && + d.cp != -1 && d.cp != 0 && d.cp != -2) { + + debugPrint("**2: 標準CP まだチェックインしていない。"); + + // print("----- in location popup checkin cp - ${d.cp}----"); + chekcs = 2; // 標準CP + + isInCheckin.value = true; + if (shouldShowBottomSheet) { + shouldShowBottomSheet = false; + + Widget bottomSheet = BottomSheetNew(destination: d); + + await showModalBottomSheet( + constraints: + BoxConstraints.loose(Size(Get.width, Get.height * 0.75)), + context: Get.context!, + isScrollControlled: true, + builder: ((context) => bottomSheet) + ).whenComplete(() { + shouldShowBottomSheet = true; + skipGps = false; + chekcs = 0; + isInCheckin.value = false; + }); + } + return; + } + } + + // 以降、検知範囲にある場合。 + //debugPrint("検知範囲にある場合"); + + // print("---- location checkin radious ${d.checkin_radious} ----"); + // print("---- already checked in $locationAlreadyCheckedIn ----"); + if ((checkinRadious >= distance || checkinRadious == -1) && + locationAlreadyCheckedIn == false && + isInRog.value == true && + !isCheckingIn.value) { + + debugPrint("* 検知範囲または距離無視CPで、ゲーム中でまだチェックインしていない。"); + + if (autoCheckin) { // 自動チェックインなら + if (!checkingIn) { + debugPrint("** 自動チェックインの場合"); + //print( + // "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ make checkin ${d.sub_loc_id}@@@@@@@@@@@"); + makeCheckin(d, true, ""); // チェックインして + if (d.cp != -1 && d.cp != -2 && d.cp != 0 ) { + rogainingCounted.value = true; // ゴール用チェックイン済み + } + skipGps = false; + } + return; // 戻る + + } else { + // それ以外 + debugPrint("* 自動チェックイン以外の場合"); + + // print("--- hidden loc ${d.hidden_location} ----"); + // ask for checkin + if (d.hidden_location != null && + d.hidden_location == 0 && // 隠しCPフラグ==0 ... 通常CP + isInRog.value == true && + d.cp != -1 && d.cp != -2 && d.cp != 0) { + // 隠しCPの場合、 + debugPrint("**3 通常CPの場合"); + + chekcs = 3; + isInCheckin.value = true; + isCheckingIn.value = true; + photos.clear(); + // print("--- calling checkin ---"); + if (shouldShowBottomSheet) { + shouldShowBottomSheet = false; + await showModalBottomSheet( + constraints: + BoxConstraints.loose(Size(Get.width, Get.height * 0.75)), + context: Get.context!, + isScrollControlled: true, + builder: ((context) => CameraPage( + destination: d, + ))).whenComplete(() { + shouldShowBottomSheet = true; + skipGps = false; + rogainingCounted.value = true; + chekcs = 0; + isInCheckin.value = false; + isCheckingIn.value = false; + }); + } + return; + + } else if (isInRog.value == true && + (locationAlreadyCheckedIn==false) && + d.cp != -1 && d.cp != -2 && d.cp != 0) { + // 通常CP + + debugPrint("**4 通常CP以外の場合....どんな場合?"); + + chekcs = 4; + isInCheckin.value = true; + if (shouldShowBottomSheet) { + shouldShowBottomSheet = false; + + Widget bottomSheet = BottomSheetNew(destination: d); + + await showMaterialModalBottomSheet( + expand: true, + context: Get.context!, + backgroundColor: Colors.transparent, + builder: (context) => bottomSheet + ).whenComplete(() { + shouldShowBottomSheet = true; + skipGps = false; + chekcs = 0; + isInCheckin.value = false; + }); + } + return; + } + } + } else if ((checkinRadious >= distance || checkinRadious == -1) && + locationAlreadyCheckedIn == true && + buyPointImageAdded == false && + ds.isNotEmpty && + buyPoint == true && + buyPointCanceled == false && + isInRog.value == true) { + // チェックイン後で買い物ポイントの場合。 + + debugPrint("**5 チェックイン後で買い物ポイントの場合"); + + + chekcs = 5; + isInCheckin.value = true; + photos.clear(); + //print("--- open buy point $buyPointImageAdded ${d.buypoint_image} ----"); + if (shouldShowBottomSheet) { + shouldShowBottomSheet = false; + if (d.cp == -1 && d.cp != -2 && d.cp != 0) return; + await showModalBottomSheet( + constraints: + BoxConstraints.loose(Size(Get.width, Get.height * 0.75)), context: Get.context!, isScrollControlled: true, builder: ((context) => CameraPage( - destination: d, - ))).whenComplete(() { - skip_gps = false; + destination: d, + buyPointPhoto: true, + dbDest: ds.first, + ))).whenComplete(() { + shouldShowBottomSheet = true; + skipGps = false; + rogainingCounted.value = true; chekcs = 0; - is_at_goal.value = false; - }); - } else if (is_in_rog.value == false && - indexController.rog_mode == 1 && - DateTime.now().difference(last_goal_at).inHours >= 24) { - //start - print("---- in start -----"); - chekcs = 6; - is_at_start.value = true; - showModalBottomSheet( - context: Get.context!, - isScrollControlled: true, - builder: ((context) => BottomSheetNew())).whenComplete(() { - print("----- finished start -------"); - skip_gps = false; - chekcs = 0; - is_at_start.value = false; + isInCheckin.value = false; }); } + return; } - print("==== _chekcs $chekcs ===="); + // print("---- cp --- ${d.cp} -----"); + // print("--- at goal $is_at_goal ---"); + // print("--- rog counted $rogaining_counted ---"); + // print("--- loc already checked in $locationAlreadyCheckedIn ---"); + // print( + // "==== date diff is ${DateTime.now().difference(last_goal_at).inHours} ===="); + if (isuserLoggedIn && + (d.cp == -2 || d.cp == 0 || d.cp == -1 ) && // Goal CP + locationAlreadyCheckedIn && + skip_10s == false) { + //check for rogaining + if (isAtGoal.value == false && rogainingCounted.value) { + //goal + //print("---- in goal -----"); + + debugPrint("**5 ゴールで時計撮影の場合"); + + chekcs = 5; // Goal 時計撮影 + isAtGoal.value = true; + photos.clear(); + if (shouldShowBottomSheet) { + shouldShowBottomSheet = false; + if (d.cp == -1) return; + await showModalBottomSheet( + constraints: + BoxConstraints.loose(Size(Get.width, Get.height * 0.75)), + context: Get.context!, + isScrollControlled: true, + builder: ((context) => CameraPage( + destination: d, + ))).whenComplete(() { + shouldShowBottomSheet = true; + skipGps = false; + chekcs = 0; + isAtGoal.value = false; + }); + } + return; + + } else if (isInRog.value == false && + indexController.rogMode.value == 1 && + DateTime.now().difference(lastGoalAt).inHours >= 10) { + //start + //print("---- in start -----"); + + debugPrint("**5 スタートの場合で最後のゴールから10時間経過している場合"); + + + chekcs = 6; // start point + isAtStart.value = true; + if (shouldShowBottomSheet) { + shouldShowBottomSheet = false; + + if (d.cp != -1 && d.cp != 0) return; + Widget bottomSheet = BottomSheetNew(destination: d); + + await showModalBottomSheet( + constraints: + BoxConstraints.loose(Size(Get.width, Get.height * 0.75)), + context: Get.context!, + isScrollControlled: true, + builder: ((context) => bottomSheet) + ).whenComplete(() { + shouldShowBottomSheet = true; + //print("----- finished start -------"); + skipGps = false; + chekcs = 0; + isAtStart.value = false; + }); + } + return; + } + } + //print("==== _chekcs $chekcs ===="); if (chekcs == 0) { - skip_gps = false; + //debugPrint("いずれにも当てはまらないので、処理スキップ"); + skipGps = false; } + return; } - void resetRogaining() async { - print("----- resetting --------"); + // ロゲイニングをリセットする関数です。 + // ゲームの状態をリセットし、データベースからデータを削除します。 + // + Future resetRogaining({bool isgoal = false}) async { + //print("----- resetting --------"); - is_in_checkin.value = false; - is_in_rog.value = false; - is_at_start.value = false; - is_at_goal.value = false; - is_gps_selected.value = true; - skip_gps = false; + isInCheckin.value = false; + isInRog.value = false; + isAtStart.value = false; + isAtGoal.value = false; + isGpsSelected.value = true; + skipGps = false; + ready_for_goal = false; _start = 0; chekcs = 0; - rogaining_counted.value = false; + rogainingCounted.value = false; DatabaseHelper db = DatabaseHelper.instance; - int? latgoal = await db.latestGoal(); - if (latgoal != null) { - last_goal_at = DateTime.fromMicrosecondsSinceEpoch(latgoal); - print("===== last goal : $last_goal_at ====="); + + if (isgoal == false) { + await db.deleteAllDestinations(); + await db.deleteAllRogaining(); } - db.deleteAllDestinations().then((value) { - PopulateDestinations(); - initGPS(); - }); - - // currentSelectedDestinations.forEach((element) { - // deleteDestination(element); - // }); + int? latgoal = await db.latestGoal(); + lastGoalAt = DateTime.fromMicrosecondsSinceEpoch(latgoal!); + //print("===== last goal : $last_goal_at ====="); + dbService.updateDatabase(); } - void openCamera(BuildContext context) { + // すべての目的地を削除する関数です。 + // + void deleteAllDestinations() { + DatabaseHelper db = DatabaseHelper.instance; + db.deleteAllDestinations().then((value) { + populateDestinations(); + }); + } + + // カメラを開いて写真を撮影する関数です。 + // + void openCamera(BuildContext context, Destination? destination) { photos.clear(); Navigator.push( context, MaterialPageRoute( + builder: (_) => CustomCameraView( + onImageCaptured: (imagePath) { + photos.add(File(imagePath)); + }, + destination: destination, + ), + /* builder: (_) => CameraCamera( + resolutionPreset: ResolutionPreset.medium, onFile: (file) { photos.add(file); Navigator.pop(context); - print("----image file is : $file----"); + //print("----image file is : $file----"); //setState(() {}); }, - ))); + ) + */ + ), + ); } + // ルートポイントを取得する関数です。 + // void getRoutePoints() { indexController.routePoints = []; indexController.routePointLenght.value = 0; @@ -365,6 +788,8 @@ class DestinationController extends GetxController { }); } + // 指定された緯度と経度に対応する目的地を取得する関数です。 + // Future getDestinationForLatLong(double lat, double long) async { for (final d in destinations) { if (lat == d.lat && long == d.lon) { @@ -374,89 +799,365 @@ class DestinationController extends GetxController { return null; } - void checkForCheckin(double la, double ln) { - print("--- skip_gps ---- $skip_gps----"); - - for (final d in destinations) { - print("--- check checkin for--loc_id- ${d.sub_loc_id}----"); - - double lat = d.lat!; - double lon = d.lon!; - LatLng p = LatLng(lat, lon); - getDestinationForLatLong(lat, lon).then((value) { - var distance = const Distance(); - double dist = - distance.as(LengthUnit.Meter, LatLng(lat, lon), LatLng(la, ln)); - //double checkin_radious = value!.checkin_radious ?? double.infinity; - //bool auto_checkin = value.auto_checkin == 0 ? false : true; - //bool location_already_checked_id = d.checkedin ?? false; - - // print("-----rogaining_counted---${rogaining_counted.value}-----"); - // print("-----is_in_rog---${is_in_rog}-----"); - // print("-----dist is ---${dist}-----"); - //print("----- ${indexController.currentUser} ----"); - - if (dist <= 250 && skip_gps == false) { - //near a destination - print("---- time with ${d.location_id} ----"); - startTimer(d, dist); + // チェックインの呼び出しを行う関数です。 + // 指定された目的地に対してチェックインの処理を行います。 + // + Future callforCheckin(Destination d) async { + bool autoCheckin = d.auto_checkin == 0 ? false : true; + print("---- f- checkin ${d.sub_loc_id} ----"); + if (autoCheckin) { + if (!checkingIn) { + makeCheckin(d, true, ""); + if (d.cp != -1 && d.cp != 0 && d.cp != -2) { + rogainingCounted.value = true; } - }); - } - - if (indexController.locations.isEmpty) return; - - //check for location in bounds - for (GeoJsonFeature fs in indexController.locations[0].collection) { - GeoJsonMultiPoint mp = fs.geometry as GeoJsonMultiPoint; - LatLng pt = LatLng(mp.geoSerie!.geoPoints[0].latitude, - mp.geoSerie!.geoPoints[0].longitude); - - double latFs = pt.latitude; - double lonFs = pt.longitude; - LatLng pFs = LatLng(latFs, lonFs); - var distanceFs = const Distance(); - double distFs = - distanceFs.as(LengthUnit.Meter, LatLng(latFs, lonFs), LatLng(la, ln)); - - if (distFs <= 250 && skip_gps == false) { - //near a location - print("---- before call startTimerLocation ----"); - startTimerLocation(fs, distFs); } + } else { + //print("--- hidden loc ${d.hidden_location} ----"); + // ask for checkin + //print("is rog ---- ${is_in_rog.value} ----"); + if (d.hidden_location != null && + d.hidden_location == 0 && + (isInRog.value == true || (d.buy_point != null && d.buy_point! > 0)) && + d.cp != -1 && d.cp != 0 && d.cp != -2) { + chekcs = 3; + photos.clear(); + isInCheckin.value = true; + + final result = await showModalBottomSheet( + constraints: + BoxConstraints.loose(Size(Get.width, Get.height * 0.75)), + context: Get.context!, + isScrollControlled: true, + builder: ((context) => CameraPage( + manulaCheckin: true, + destination: d, + ))); + + if (result ?? false) { + debugPrint("==> Checkin complete...."); + if (d.buy_point != null && d.buy_point! > 0) { + skipGps = true; + photos.clear(); + DatabaseHelper db = DatabaseHelper.instance; + List ds = + await db.getDestinationByLatLon(d.lat!, d.lon!); + Destination? dss; + if (ds.isNotEmpty) { + dss = ds.first; + } + + await showModalBottomSheet( + constraints: + BoxConstraints.loose(Size(Get.width, Get.height * 0.75)), + context: Get.context!, + isScrollControlled: true, + builder: ((context) => + CameraPage( + buyPointPhoto: true, + destination: d, + dbDest: dss, + ))).whenComplete(() { + skipGps = false; + rogainingCounted.value = true; + chekcs = 0; + isInCheckin.value = false; + //Get.back(); + }); + } + } else { + debugPrint("キャンセルされました"); + Get.snackbar( + "キャンセルされました", + "チェックインしていません。必要ならもう一度チェックポイントをタップして下さい。", + backgroundColor: Colors.yellow, + colorText: Colors.black, + icon: const Icon( + Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue), + snackPosition: SnackPosition.TOP, + duration: const Duration(seconds: 3), + ); + } + } else { + Get.snackbar( + "ロゲが始まっていません", + "ロゲ開始ボタンをタップして、ロゲイニングを始める必要があります", + backgroundColor: Colors.yellow, + colorText: Colors.black, + icon: const Icon( + Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue), + snackPosition: SnackPosition.TOP, + duration: const Duration(seconds: 3), + ); + } + /* + await showModalBottomSheet( + constraints: + BoxConstraints.loose(Size(Get.width, Get.height * 0.75)), + context: Get.context!, + isScrollControlled: true, + builder: ((context) => CameraPage( + manulaCheckin: true, + destination: d, + ))).whenComplete(() async { + if (d.buy_point != null && d.buy_point! > 0) { + skipGps = true; + photos.clear(); + DatabaseHelper db = DatabaseHelper.instance; + List ds = + await db.getDestinationByLatLon(d.lat!, d.lon!); + Destination? dss; + if (ds.isNotEmpty) { + dss = ds.first; + } + + await showModalBottomSheet( + constraints: + BoxConstraints.loose(Size(Get.width, Get.height * 0.75)), + context: Get.context!, + isScrollControlled: true, + builder: ((context) => CameraPage( + buyPointPhoto: true, + destination: d, + dbDest: dss, + ))).whenComplete(() { + skipGps = false; + rogainingCounted.value = true; + chekcs = 0; + isInCheckin.value = false; + //Get.back(); + }); + } else { + skipGps = false; + chekcs = 0; + isInCheckin.value = false; + } + }); + } else { + Get.snackbar( + "ロゲが始まっていません", + "ロゲ開始ボタンをタップして、ロゲイニングを始める必要があります", + backgroundColor: Colors.yellow, + colorText: Colors.white, + icon: const Icon( + Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue), + snackPosition: SnackPosition.TOP, + duration: const Duration(seconds: 3) +// backgroundColor: Colors.yellow, + ); + } + */ + } } + // GPSデータをデータベースに追加する関数です。 + // + Future addGPStoDB(double la, double ln, {isCheckin = 0}) async { + //debugPrint("in addGPStoDB ${indexController.currentUser}"); + try { + GpsDatabaseHelper db = GpsDatabaseHelper.instance; + if(indexController.currentUser.isNotEmpty){ + final teamName = indexController.currentUser[0]["user"]['team_name']; + final eventCode = indexController.currentUser[0]["user"]["event_code"]; + GpsData gpsData = GpsData( + id: 0, + team_name: teamName, + event_code: eventCode, + lat: la, + lon: ln, + is_checkin: isCheckin, + created_at: DateTime.now().millisecondsSinceEpoch); + var res = await db.insertGps(gpsData); + //debugPrint("Saved GPS data into DB...:${gps_data}"); + } + } catch (err) { + print("errr ready gps $err"); + return; + } + } + + // チェックインを確認する関数です。 + // ゲームが開始されていない場合は、ゲームを開始します。 + // 目的地のリストを走査し、現在位置がチェックイン半径内にある場合は、チェックインの処理を行います。 + // GPSデータの送信を開始します。 + // + // 2024-4-8 Akira : See 2809 + // checkForCheckinメソッドの再帰呼び出しをunawaitedで囲んで、非同期処理の結果を待たずに先に進むようにしました。また、再帰呼び出しの前に一定時間待機するようにしました。 + // + Future checkForCheckin() async { + //print("--- Start of checkForCheckin function ---"); + dbService.updateDatabase(); + await Future.delayed(const Duration(milliseconds: 3000)); + game_started = true; + + try { + // ここで、エラー + if( indexController.locations.isNotEmpty ) { + indexController.locations[0].features.forEach((fs) async { + GeoJSONMultiPoint mp = fs!.geometry as GeoJSONMultiPoint; + LatLng pt = LatLng(mp.coordinates[0][1], mp.coordinates[0][0]); + + double latFs = pt.latitude; + double lonFs = pt.longitude; + var distanceFs = const Distance(); + double distFs = distanceFs.as(LengthUnit.Meter, LatLng(latFs, lonFs), + LatLng(currentLat, currentLon)); + Destination des = festuretoDestination(fs); + + if (distFs <= des.checkin_radious! + && skipGps == false + //&& des.isCheckedIn == false + && des.cp!=0 && des.cp!=-1 && des.cp!=-2) { + await startTimerLocation(fs, distFs); + // Note: You cannot break out of forEach. If you need to stop processing, you might have to reconsider using forEach. + } + }); + + if (gps_push_started == false) { + unawaited(pushGPStoServer()); + } + } + //print("--- 123 ---- $skip_gps----"); + } catch (e) { + print("An error occurred: $e"); + // await checkForCheckin(); + } finally { + await Future.delayed(const Duration(seconds: 1)); // 一定時間待機してから再帰呼び出し + //print("--- End of checkForCheckin function, calling recursively ---"); + unawaited( checkForCheckin() ); + } + } + + // GPSデータをサーバーにプッシュする関数です。 + // + Future pushGPStoServer() async { + // print( + // "^^^^^^^^ ${DateFormat('kk:mm:ss \n EEE d MMM').format(DateTime.now())}"); + try { + gps_push_started = true; + ExternalService().pushGPS(); + } catch (e) { + //print("An error occurred: $e"); + //await pushGPStoServer(); + } finally { + //print("--- End of pushGPStoServer function, calling recursively ---"); + await Future.delayed(const Duration(seconds: 5 * 60)); + await pushGPStoServer(); + } + } + + + + // ロゲイニングにデータを追加する関数です。 + // void addToRogaining(double lat, double lon, int destinationId) async { DatabaseHelper db = DatabaseHelper.instance; List d = await db.getDestinationById(destinationId); if (d.isEmpty) { Destination df = festuretoDestination(indexController.currentFeature[0]); - print("--- made checkin ${df.location_id} ----"); + //print("--- made checkin ${df.location_id} ----"); makeCheckin(df, true, ""); } - is_in_rog.value = true; + isInRog.value = true; + + saveGameState(); } - void makeCheckin( - Destination destination, bool action, String imageurl) async { - print( - "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ressssss ${destination.sub_loc_id}@@@@@@@@@@@"); + // 買い物ポイントをキャンセルする関数です。 + // + Future cancelBuyPoint(Destination destination) async { DatabaseHelper db = DatabaseHelper.instance; - List ddd = - await db.getDestinationByLatLon(destination.lat!, destination.lon!); + await db.updateCancelBuyPoint(destination); + populateDestinations(); + } - if (ddd.isEmpty) { - destination.checkedin = true; - await db.insertDestination(destination); + // 指定されたパスの画像をギャラリーに保存する関数です。 + // + _saveImageFromPath(String imagePath) async { + try { + // Read the image file from the given path + File imageFile = File(imagePath); + Uint8List imageBytes = await imageFile.readAsBytes(); + + // Save the image to the gallery + final result = await ImageGallerySaver.saveImage(imageBytes); + //print("--- save result --- ${result}"); + } catch(e, stackTrace){ + print('エラーが発生しました: $e'); + print('スタックトレース: $stackTrace'); + } + } + + Future _saveImageToGallery(String imagePath) async { + final status = await PermissionController.checkStoragePermission(); + if(!status){ + await PermissionController.requestStoragePermission(); } - PopulateDestinations(); + /* + final status = await Permission.storage.status; + if (!status.isGranted) { + final result = await Permission.storage.request(); + if (!result.isGranted) { + // ユーザーがストレージの権限を拒否した場合の処理 + showDialog( + context: Get.context!, + builder: (BuildContext context) { + return AlertDialog( + title: Text('ストレージの権限が必要です'), + content: Text( + '画像をギャラリーに保存するには、ストレージの権限が必要です。アプリの設定画面で権限を許可してください。'), + actions: [ + TextButton( + child: Text('キャンセル'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: Text('設定'), + onPressed: () { + Navigator.of(context).pop(); + openAppSettings(); // アプリの設定画面を開く + }, + ), + ], + ); + } + ); + + return; + } + } + */ + + try { + final result = await ImageGallerySaver.saveFile(imagePath); + print('Image saved to gallery: $result'); + } catch (e) { + print('Failed to save image to gallery: $e'); + } + } + + // 買い物ポイントを作成する関数です。 指定された目的地に対して買い物ポイントの処理を行います。 + // + // 買い物ポイントの作成に失敗した場合のエラーハンドリングを追加することを検討してください。 + // + Future makeBuyPoint(Destination destination, String imageurl) async { + DatabaseHelper db = DatabaseHelper.instance; + await db.updateBuyPoint(destination, imageurl); + populateDestinations(); + //await _saveImageFromPath(imageurl); + await _saveImageToGallery(imageurl); + + - /// post to NATNAT if (indexController.currentUser.isNotEmpty) { double cpNum = destination.cp!; + //int teamId = indexController.teamId.value; // teamIdを使用 + int userId = indexController.currentUser[0]["user"]["id"]; //print("--- Pressed -----"); String team = indexController.currentUser[0]["user"]['team_name']; @@ -468,160 +1169,571 @@ class DestinationController extends GetxController { DateTime now = DateTime.now(); String formattedDate = DateFormat('yyyy-MM-dd HH:mm:ss').format(now); - print("------ checkin event $eventCode ------"); + //print("------ checkin event $eventCode ------"); ExternalService() .makeCheckpoint(userId, token, formattedDate, team, cpNum.round(), eventCode, imageurl) .then((value) { - print("------Ext service check point $value ------"); + //print("------Ext service check point $value ------"); }); } } - void initGPS() { - checkPermission(); - PopulateDestinations(); - //print("------ in iniit"); - - if (defaultTargetPlatform == TargetPlatform.android) { - print("---- GPS android -----"); - locationSettings = AndroidSettings( - accuracy: LocationAccuracy.best, - distanceFilter: 3, - forceLocationManager: true, - intervalDuration: const Duration(seconds: 1), - //(Optional) Set foreground notification config to keep the app alive - //when going to the background - foregroundNotificationConfig: const ForegroundNotificationConfig( - notificationText: - " App will continue to receive your location even when you aren't using it", - notificationTitle: "Running in Background", - enableWakeLock: true, - )); - } else if (defaultTargetPlatform == TargetPlatform.iOS || - defaultTargetPlatform == TargetPlatform.macOS) { - locationSettings = AppleSettings( - accuracy: LocationAccuracy.bestForNavigation, - activityType: ActivityType.fitness, - distanceFilter: 0, - pauseLocationUpdatesAutomatically: false, - // Only set to true if our app will be started up in the background. - showBackgroundLocationIndicator: true); - } else { - locationSettings = const LocationSettings( - accuracy: LocationAccuracy.high, - distanceFilter: 0, - ); - } + // チェックインを行う関数です。 指定された目的地に対してチェックインの処理を行います。 + // + // 要検討:チェックインのリクエストが失敗した場合のエラーハンドリングを追加することをお勧めします。 + // + Future makeCheckin( + Destination destination, bool action, String imageurl) async { try { - StreamSubscription positionStream = - Geolocator.getPositionStream(locationSettings: locationSettings) - .listen((Position? position) { - current_lat = position != null ? position.latitude : 0; - current_lon = position != null ? position.longitude : 0; + // print("~~~~ calling checkin function ~~~~"); + // print( + // "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ressssss ${destination.sub_loc_id}@@@@@@@@@@@"); + DatabaseHelper db = DatabaseHelper.instance; + List ddd = + await db.getDestinationByLatLon(destination.lat!, destination.lon!); - print( - "==== gps skip is : ${skip_gps.toString()}, selected is $is_gps_selected , $current_lat"); - - if (is_gps_selected.value) { - double czoom = indexController.rogMapController.zoom; - indexController.rogMapController - .move(LatLng(position!.latitude, position.longitude), czoom); - //String user_id = indexController.currentUser[0]["user"]["id"].toString(); - //TrackingService.addTrack(user_id, position!.latitude, position.longitude).then((val){ - //print("---- postion is ${position.latitude}, ${position.longitude}"); - gps.clear(); - gps.add( - "-- lat : ${position.latitude}, lon : ${position.longitude} --"); - checkForCheckin(position.latitude, position.longitude); - print("--- call check checkin"); - print("---- skip gps is ${skip_gps.toString()} ----"); - //}); - } - //print(position == null ? 'Unknown' : 'current position is ${position.latitude.toString()}, ${position.longitude.toString()}'); - }); - } catch (err) { - locationPermission.clear(); - locationPermission.add(err.toString()); - } - - ever(indexController.connectionStatusName, connectionChanged); - } - - @override - void onInit() async { - initGPS(); - - super.onInit(); - } - - void loadInitPoints(String token) async { - await indexController.loadUserDetailsForToken(token); - LocationService.getLocationsExt(token).then((value) { - if (value != null) { - print("--- loc ext is - $value ----"); - LatLngBounds bnds = LatLngBounds( - LatLng(value[1], value[0]), LatLng(value[3], value[2])); - print("--- bnds is - $bnds ----"); - indexController.mapController.fitBounds( - bnds, - ); - indexController.currentBound.clear(); - indexController.currentBound.add(bnds); - indexController.loadLocationsBound(); + if (ddd.isEmpty) { + destination.checkedin = true; + destination.checkin_image = imageurl; + await db.insertDestination(destination); + // print("~~~~ inserted into db ~~~~"); } + + if (imageurl.isEmpty) { + if (photos.isNotEmpty) { + // imageurlが空の場合は、destinationのcheckin_imageプロパティを使用する + debugPrint("photos = $photos"); + imageurl = photos[0].path; + } + debugPrint("imageurl = $imageurl"); + //await _saveImageFromPath(imageurl!); + } + if (imageurl.isNotEmpty) { + await _saveImageToGallery(imageurl); + } + + populateDestinations(); + + /// post to NATNAT + if (indexController.currentUser.isNotEmpty) { + double cpNum = destination.cp!; + + //int teamId = indexController.teamId.value; // teamIdを使用 + //Team team0 = teamController.teams[0]; + //print("team={team0}"); + + + int userId = indexController.currentUser[0]["user"]["id"]; + //print("--- Pressed -----"); + String team = indexController.currentUser[0]["user"]['team_name']; + //print("--- _team : ${_team}-----"); + String eventCode = indexController.currentUser[0]["user"]["event_code"]; + //print("--- _event_code : ${_event_code}-----"); + String token = indexController.currentUser[0]["token"]; + //print("--- _token : ${_token}-----"); + DateTime now = DateTime.now(); + String formattedDate = DateFormat('yyyy-MM-dd HH:mm:ss').format(now); + + await addGPStoDB(currentLat, currentLon, isCheckin: 1); + + // print("------ checkin event $eventCode ------"); + ExternalService() + .makeCheckpoint( + userId, // teamIdを使用 + token, + formattedDate, + team, + cpNum.round(), + eventCode, + imageurl) + .then((value) { + // print("------Ext service check point $value ------"); + }); + } + // dbService.updateDatabase(); + + }catch(e){ + print("エラー:$e"); + //print("stack : ${stacktrace}"); + }finally{ + dbService.updateDatabase(); + } + + + } + + // チェックインを削除する関数です。 + // + Future removeCheckin(int cp) { + dbService.updateDatabase(); + return ExternalService().removeCheckin(cp); + } + + // ゲームを開始する関数です。 + // + Future startGame() async { + debugPrint("------ starting game ------"); + if (game_started == false) { + await checkForCheckin(); + } + } + + Timer? gpsCheckTimer; // 一定間隔でGPSデータの受信状態をチェックするタイマー + + void startGPSCheckTimer() { + gpsCheckTimer = Timer.periodic(const Duration(seconds: 5), (timer) { + checkGPSDataReceived(); }); } - readUserToken() async{ + // コントローラーの初期化時に呼び出されるライフサイクルメソッドです。 + // + bool inError=false; + bool isRunningBackgroundGPS=false; + int activeEngineCount = 0; + + @override + void onInit() async { + super.onInit(); + + /* + WidgetsBinding.instance.addPostFrameCallback((_) async { + await PermissionController.checkAndRequestPermissions(); + }); + */ + + startGPSCheckTimer(); + + // MapControllerの初期化完了を待機するフラグを設定 + WidgetsBinding.instance.addPostFrameCallback((_) { + //checkGPSDataReceived(); removed 2024-5-4 + + isMapControllerReady = true; + }); + + // 要検討:エラーメッセージを表示するなどの適切な処理を追加することを検討してください。 + // + // locationController からデバイスの受け取るGPS情報を取得し、 + // handleLocationUpdate を呼び出している。 + // + locationController.locationMarkerPositionStream.listen( + (locationMarkerPosition) { + //if (locationMarkerPosition != null) { + handleLocationUpdate(locationMarkerPosition); + //} + }, onError: (err) { + if(inError==false){ + inError = true; + debugPrint("Location Error: $err"); + // エラーが発生した場合、locationMarkerPositionStreamControllerにエラーを追加します。 + locationController.locationMarkerPositionStreamController.addError(err); + + // ここにエラー発生時の処理を追加します。 + if (err is LocationServiceDisabledException) { + // 位置情報サービスが無効になっている場合の処理 + print('Location services are disabled'); + Get.snackbar( + 'エラー', + '位置情報サービスが無効になっています。設定画面から位置情報サービスを有効にして下さい。不明な場合にはエンジニアスタッフにお問い合わせください。', + backgroundColor: Colors.red, + colorText: Colors.white, + duration: const Duration(seconds: 3), + ); + inError = false; + /* + } else if (err is PermissionDeniedException) { + // 位置情報の権限がない場合の処理 + print('Location permissions are denied'); + Get.snackbar( + 'エラー', + '位置情報サービスが許可されていません。設定画面から岐阜ナビの位置情報サービスを許可して下さい。不明な場合にはエンジニアスタッフにお問い合わせください。', + backgroundColor: Colors.red, + colorText: Colors.white, + duration: const Duration(seconds: 3), + ); + inError = false; + */ + } else { + // その他のエラーの場合の処理 + print('Location Error: $err'); + Get.snackbar( + 'エラー', + '位置情報サービスに問題が発生しました。位置情報サービスを再起動していますので少しお待ちください。', + backgroundColor: Colors.red, + colorText: Colors.white, + duration: const Duration(seconds: 3), + ); + + // GPSデータのListenを再開する処理を追加 + if( isRunningBackgroundGPS==false && inError ) { + restartGPS(); + } + } + } + //print("Location Error: $err"); + }); + + startGame(); + + //checkGPSDataReceived(); + } + + void restartGPS(){ + // GPSデータのListenを再開する処理を追加 + Future.delayed(const Duration(seconds: 5), () { + locationController.startPositionStream(); + inError=false; + }); + } + + // コントローラーのクローズ時に呼び出されるライフサイクルメソッドです。 + // + @override + void onClose() { + gpsCheckTimer?.cancel(); + locationController.stopPositionStream(); + super.onClose(); + } + + // 位置情報の更新を処理する関数です。 + // 現在位置とスタート地点との距離を計算します。 + // 現在位置と前回の位置情報との距離と時間差を確認し、一定の条件を満たす場合はGPSデータをデータベースに追加します。 + // + // 要検討:GPSデータの追加に失敗した場合のエラーハンドリングを追加することをお勧めします。 + // + double prevLat = 0.0; // 直前の位置 + double prevLon = 0.0; + bool gpsDebugMode=false; + + void handleLocationUpdate(LocationMarkerPosition? position) async { + //debugPrint("DestinationController.handleLocationUpdate"); + + try { + //final DestinationController destinationController = Get.find(); + //final signalStrength = locationController.getGpsSignalStrength(); + + okToUseGPS = false; + + if (position != null) { + currentLat = position.latitude; + currentLon = position.longitude; + if( prevLat==0.0 ){ + prevLat = currentLat; + prevLon = currentLon; + } + lastValidGPSLocation = LatLng(currentLat, currentLon); + lastValidLat = currentLat; + lastValidLon = currentLon; + okToUseGPS = true; + lastGPSDataReceivedTime = DateTime.now(); + hasReceivedGPSData = true; + + } else { + debugPrint("....position is null...."); + checkGPSDataReceived(); + + // 信号強度が低い場合、最後に取得した高いまたは中程度の位置情報を使用 + // 但し、最初から高精度のものがない場合、どうするか? + // + // GPSデータが受信できない場合、最後に有効なGPSデータを使用 + position = LocationMarkerPosition( + latitude: lastValidGPSLocation.latitude, + longitude: lastValidGPSLocation.longitude, + accuracy: 0, + ); + currentLat = position.latitude; + currentLon = position.longitude; + okToUseGPS = false; + + /* + if (lastValidLat != 0.0 && lastValidLon != 0.0) { + currentLat = lastValidLat; + currentLon = lastValidLon; + okToUseGPS = true; + } else { + // GPSの届く場所に行って、信号を拾ってください。とメッセージを出す。 + position = null; + print("GPSの届く場所に行って、信号を拾ってください。"); + Get.snackbar( + "GPS信号を正確に拾えていません", + "空が大きく見えるところへ行ってGPS信号を拾ってください。", + icon: const Icon( + Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue), + snackPosition: SnackPosition.TOP, + duration: const Duration(seconds: 3), + backgroundColor: Colors.yellow, + ); + } + */ + } + + if (okToUseGPS) { + // スタート位置から150m離れたら、ready_for_goal + if (distanceToStart() >= 150) { + ready_for_goal = true; + } + + var distance = const Distance(); + double distanceToDest = distance.as( + LengthUnit.Meter, + LatLng(position.latitude, position.longitude), + LatLng(prevLat, prevLon) + ); + + Duration difference = lastGPSCollectedTime.difference(DateTime.now()) + .abs(); + // 最後にGPS信号を取得した時刻から10秒以上経過、かつ10m以上経過(普通に歩くスピード) + //debugPrint("時間差:${difference.inSeconds}, 距離差:${distanceToDest}"); + if (difference.inSeconds >= 10 || distanceToDest >= 30) { + // print( + // "^^^^^^^^ GPS data collected ${DateFormat('kk:mm:ss \n EEE d MMM').format(DateTime.now())}, ^^^ ${position.latitude}, ${position.longitude}"); + + LogManager().addLog( + "GPS : $currentLat, $currentLon - ${DateTime + .now() + .hour}:${DateTime + .now() + .minute}:${DateTime + .now() + .second}:${DateTime + .now() + .microsecond}"); + if (isInRog.value) { + await addGPStoDB(position.latitude, position.longitude); + lastGPSCollectedTime = DateTime.now(); + prevLat = position.latitude; + prevLon = position.longitude; + gpsDebugMode ? debugPrint("フロントエンドでのGPS保存(時間差:${difference.inSeconds}, 距離差:$distanceToDest) : Time=$lastGPSCollectedTime"):null; + } + } + } + } catch(e) { + debugPrint("handleLocationUpdate Error: $e"); + } finally { + /* Akira , 2024-4-5 + if (position != null && + (position.latitude != 0 && position.longitude != 0)) { + currentLat = position.latitude; + currentLon = position.longitude; + } + */ + if (okToUseGPS) { + // 位置情報が取得できた場合、精度に関わらず最後の位置情報を更新 + //currentLat = position.latitude; + //currentLon = position.longitude; + } + } + } + + // スタート地点までの距離を計算する関数です。 + // + double distanceToStart() { + if (indexController.locations.isEmpty) { + return 1000000000; + } + //print("=== gfs len == ${indexController.locations[0].collection.length}"); + double distanceToDest = double.infinity; + if (indexController.locations[0].features.isEmpty) { + return distanceToDest; + } + GeoJSONFeature? gfs = indexController.locations[0].features.firstWhere( + (element) => festuretoDestination(element!).cp == -1, + orElse: () => null, // Provide a null value if no element is found + ); + + //print("gfs : ${gfs}"); + + if (gfs == null) { + return distanceToDest; + } + + //final currentLocation = getCurrentLocation(); // GPS信号中以上での現在位置 + + Destination des = festuretoDestination(gfs); + + //print("=== gfs == ${des.toMap()}"); + + var distance = const Distance(); + distanceToDest = distance.as(LengthUnit.Meter, + LatLng(currentLat,currentLon), LatLng(des.lat!, des.lon!)); +// LatLng(currentLat, currentLon), LatLng(des.lat!, des.lon!)); + //print("==== dist==${distanceToDest}"); + return distanceToDest; + } + + // 強制チェックイン距離を取得する関数です。 + // + int getForcedChckinDistance(Destination dest) { + if (dest.checkin_radious == -1) { + return 10000000000000000; + } + + int retValue = 100; + if (dest.cp == -1) { + return 500; + } + Destination? ds; + GeoJSONFeature? gfs = indexController.locations[0].features.firstWhere( + (element) => festuretoDestination(element!).cp == -1, + orElse: () => null, // Provide a null value if no element is found + ); + + if (gfs == null) { + return retValue; + } + + ds = festuretoDestination(gfs); + var distance = const Distance(); + double distanceToDest = distance.as(LengthUnit.Meter, + LatLng(dest.lat!, dest.lon!), LatLng(ds.lat!, ds.lon!)); + if (distanceToDest <= 500) { + return 500; + } + //print("==== forced dist ==${distanceToDest}"); + return retValue; + } + + // ユーザートークンを読み取る関数です。 + // + readUserToken() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); indexController.userToken = prefs.getString("user_token"); } + // コントローラーの準備完了時に呼び出されるライフサイクルメソッドです。 + // @override void onReady() async { await readUserToken(); - if(indexController.userToken != null && indexController.userToken!.isNotEmpty){ - loadInitPoints(indexController.userToken!); - indexController.switchPage(AppPages.INITIAL); - return; + final token = indexController.userToken; + if (token != null && token.isNotEmpty) { + await indexController.loadUserDetailsForToken(token); + fixMapBound(token); + }else { + Get.toNamed(AppPages.LOGIN)!.then((value) { + if (indexController.currentUser.isNotEmpty) { + final tk = indexController.currentUser[0]["token"]; + fixMapBound(tk); + } else { + Get.toNamed(AppPages.TRAVEL); + PerfectureService.getSubExt("9").then((value) { + if (value != null) { + LatLngBounds bnds = LatLngBounds( + LatLng(value[1], value[0]), LatLng(value[3], value[2])); + indexController.mapController + .fitBounds(bnds); //.centerZoomFitBounds(bnds); + } + }); + } + }); } - Get.toNamed(AppPages.LOGIN)!.then((value) { - if (indexController.currentUser.isNotEmpty) { - String token = indexController.currentUser[0]["token"]; - indexController.switchPage(AppPages.INITIAL); - loadInitPoints(token); - } else { - Get.toNamed(AppPages.TRAVEL); - PerfectureService.getSubExt("9").then((value) { - if (value != null) { - LatLngBounds bnds = LatLngBounds( - LatLng(value[1], value[0]), LatLng(value[3], value[2])); - indexController.mapController - .fitBounds(bnds); //.centerZoomFitBounds(bnds); - } - }); + + // 地図のイベントリスナーを設定 + indexController.mapController.mapEventStream.listen((MapEvent mapEvent) { + if (mapEvent is MapEventMoveEnd) { + indexController.loadLocationsBound(indexController.currentUser[0]["user"]["event_code"]); } }); + super.onReady(); } + // 地図の境界を修正する関数です。 + // + void fixMapBound(String token) { + //String _token = indexController.currentUser[0]["token"]; + indexController.switchPage(AppPages.INDEX); + + if (isMapControllerReady) { + LocationService.getLocationsExt(token).then((value) { + if (value != null) { + //print("--- loc ext is - $value ----"); + LatLngBounds bnds = LatLngBounds( + LatLng(value[1], value[0]), LatLng(value[3], value[2])); + //print("--- bnds is - $bnds ----"); + indexController.mapController.fitBounds( + bnds, + ); + indexController.currentBound.clear(); + indexController.currentBound.add(bnds); + indexController.loadLocationsBound(indexController.currentUser[0]["user"]["event_code"]); + centerMapToCurrentLocation(); + } + }); + } else { + // MapControllerの初期化が完了していない場合は、遅延して再試行 + Future.delayed(const Duration(milliseconds: 100), () { + fixMapBound(token); + }); + } + } + + +/* + void fixMapBound(String token) { + indexController.switchPage(AppPages.INDEX); + LocationService.getLocationsExt(token).then((value) { + if (value != null) { + LatLngBounds bnds = LatLngBounds( + LatLng(value[1], value[0]), + LatLng(value[3], value[2]), + ); + if (indexController.isMapControllerReady.value) { + indexController.mapController.fitBounds( + bnds, + ); + indexController.currentBound.clear(); + indexController.currentBound.add(bnds); + indexController.loadLocationsBound(); + centerMapToCurrentLocation(); + } else { + // MapControllerが初期化されるまで待機し、その後fitBoundsを実行 + WidgetsBinding.instance.addPostFrameCallback((_) { + indexController.mapController.fitBounds( + bnds, + ); + indexController.currentBound.clear(); + indexController.currentBound.add(bnds); + indexController.loadLocationsBound(); + centerMapToCurrentLocation(); + }); + } + } + }); + } +*/ + + // 地図を現在位置に中央揃えする関数です。 + // + void centerMapToCurrentLocation() { + //print("center is ${currentLat}, ${currentLon}"); + // Akira ... 状況によって呼ぶか呼ばないか + if (currentLat != 0 || currentLon != 0) { + indexController.mapController.move(LatLng(currentLat, currentLon), 17.0); + } + } + + // 接続状態が変更されたときに呼び出される関数です。 + // void connectionChanged(String val) { - print('----- %%%%%%%%%%%%%%%%%%%%% ----- $val'); + //print('----- %%%%%%%%%%%%%%%%%%%%% ----- $val'); Map res = {}; if (val == "wifi" || val == "mobile") { + //int teamId = indexController.teamId.value; // teamIdを使用 + String token = indexController.currentUser[0]["token"]; DatabaseHelper db = DatabaseHelper.instance; db.allRogianing().then((value) { value.forEach((e) async { if (e.rog_action_type == 0) { - res = await ExternalService().StartRogaining(); + res = await ExternalService().startRogaining(); } else if (e.rog_action_type == 1) { var datetime = DateTime.fromMicrosecondsSinceEpoch(e.checkintime!); res = await ExternalService().makeCheckpoint( - e.user_id!, + e.user_id!, // teamId??? token, getFormatedTime(datetime), e.team_name!, @@ -631,7 +1743,7 @@ class DestinationController extends GetxController { } else if (e.rog_action_type == 2) { var datetime = DateTime.fromMicrosecondsSinceEpoch(e.checkintime!); res = await ExternalService().makeGoal( - e.user_id!, + e.user_id!, // // teamId??? token, e.team_name!, e.image!, @@ -647,6 +1759,9 @@ class DestinationController extends GetxController { } } + /* + // 位置情報の許可を確認する関数です。 + // void checkPermission() async { LocationPermission permission = await Geolocator.checkPermission(); if (permission != LocationPermission.whileInUse || @@ -656,12 +1771,15 @@ class DestinationController extends GetxController { permission = await Geolocator.requestPermission(); } } + */ + // IDに基づいて目的地を取得する関数です。 + // Destination? destinationById(int id) { Destination? d; - print("--- target des - $id ----"); + //print("--- target des - $id ----"); for (Destination ss in destinations) { - print("--- des - ${ss.location_id} ----"); + //print("--- des - ${ss.location_id} ----"); if (ss.location_id == id) { d = ss; break; @@ -670,6 +1788,8 @@ class DestinationController extends GetxController { return d; } + // 目的地を削除する関数です。 + // void deleteDestination(Destination d) { //int id = destinations[index].location_id!; //print("---- index ${destinations[index].location_id!}-----"); @@ -681,61 +1801,85 @@ class DestinationController extends GetxController { } DatabaseHelper db = DatabaseHelper.instance; db.deleteDestination(d.location_id!).then((value) { - PopulateDestinations(); + populateDestinations(); }); + dbService.updateDatabase(); } - void deleteAllDestinations() { + // データベースからすべての目的地を削除する関数です。 + // + void deleteDBDestinations() { DatabaseHelper db = DatabaseHelper.instance; db.deleteAllDestinations().then((value) { - PopulateDestinations(); + populateDestinations(); }); + dbService.updateDatabase(); } // ---------- database ------------------/// + // 目的地を追加する関数です。 + // void addDestinations(Destination dest) { - print( - '------ destination controller in add destination ${dest.checkin_radious} ---- :::::'); - DatabaseHelper db = DatabaseHelper.instance; db.getDestinationByLatLon(dest.lat!, dest.lon!).then((value) { if (value.isNotEmpty) { db.deleteDestination(value[0].location_id!).then((value) { db.insertDestination(dest).then((value) { - print( - "----- destination controller deleted and inserted destination id $value ---- :::::"); - PopulateDestinations(); + //print( + // "----- destination controller deleted and inserted destination id $value ---- :::::"); + populateDestinations(); }); }); } else { db.insertDestination(dest).then((value) { - print("----- destination controller added as new $value--- :::::"); - PopulateDestinations(); + //print("----- destination controller added as new $value--- :::::"); + populateDestinations(); }); } }); + dbService.updateDatabase(); } + // 目的地の選択状態を切り替える関数です。 + // void toggleSelection(Destination dest) async { - DatabaseHelper db = DatabaseHelper.instance; - await db.toggleSelecttion(dest); - destinations.clear(); - db.getDestinations().then((value) { - destinationCount.value = 0; - currentSelectedDestinations.clear(); - for (Destination d in value) { - //print("------ destination controller populating destination-------- ${d.checkedin}-------- :::::"); - //print("-----populated----- ${d.toMap()}"); - if (d.selected!) { - currentSelectedDestinations.add(d); + try { + DatabaseHelper db = DatabaseHelper.instance; + await db.toggleSelecttion(dest); + destinations.clear(); + db.getDestinations().then((value) { + destinationCount.value = 0; + currentSelectedDestinations.clear(); + for (Destination d in value) { + //print("------ destination controller populating destination-------- ${d.checkedin}-------- :::::"); + //print("-----populated----- ${d.toMap()}"); + if (d.selected!) { + currentSelectedDestinations.add(d); + } + destinations.add(d); } - destinations.add(d); - } - destinationCount.value = destinations.length; - }); + destinationCount.value = destinations.length; + }); + } catch( e ){ + print('Error in toggleSelection: $e'); + Get.snackbar( + "画面切り替えでエラー", + "画面の切り替えができませんでした", + backgroundColor: Colors.red, + colorText: Colors.white, + icon: const Icon( + Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue), + snackPosition: SnackPosition.TOP, + duration: const Duration(seconds: 3), + //backgroundColor: Colors.yellow, + ); + + } } + // ダイアログを表示する関数です。 + // buildShowDialog(BuildContext context) { return showDialog( context: context, @@ -747,10 +1891,12 @@ class DestinationController extends GetxController { }); } + // 現在地点からの目的地の行列を計算する関数です。 + // void destinationMatrixFromCurrentPoint(List points) { - buildShowDialog(Get.context!); + //buildShowDialog(Get.context!); MatrixService.getDestinations(points).then((mat) { - print(" matrix is ------- $mat"); + //print(" matrix is ------- $mat"); matrix = mat; try { @@ -760,94 +1906,42 @@ class DestinationController extends GetxController { indexController.routePoints = value; indexController.routePointLenght.value = indexController.routePoints.length; - Get.toNamed(AppPages.TRAVEL); + //Get.toNamed(AppPages.TRAVEL); }); destinationCount.value = destinations.length; + } catch (_) { - skip_gps = false; + skipGps = false; return; } finally { - Get.back(); + //Get.back(); + isRouteShowing.value = true; } }); } - void PopulateDestinations() { - print( - "--------- destination controller populsting destinations ----------- ::::::"); - + // 目的地のリストを取得してObservable変数を更新する関数です。 + // + void populateDestinations() { DatabaseHelper db = DatabaseHelper.instance; destinations.clear(); db.getDestinations().then((value) { destinationCount.value = 0; for (Destination d in value) { - print( - "------ destination controller populating destination-------- ${d.checkedin}-------- :::::"); - print("-----populated----- ${d.toMap()}"); destinations.add(d); } - // destinationCount.value = 0; - print( - "------ destination controller destinationcount-------- $destinationCount-------- :::::"); - - MatrixService.getDestinations(value).then((mat) { - print(" matrix is ------- $mat"); - matrix = mat; - - try { - getRoutePoints(); - destinationCount.value = destinations.length; - } catch (_) { - skip_gps = false; - return; - } - }); + if (destinations.isEmpty) { + rogainingCounted.value = false; + } }); } + // 目的地の順序を変更する関数です。 + // void makeOrder(Destination d, int dir) { DatabaseHelper db = DatabaseHelper.instance; db.updateOrder(d, dir).then((value) { - PopulateDestinations(); + populateDestinations(); }); } - - void makeNext(Destination pt) { - for (int i = 0; i <= destinations.length - 1; i++) { - Destination p = destinations[i]; - - if (p.lat == pt.lat && p.lon == pt.lon) { - if (indexController.currentDestinationFeature.isNotEmpty) { - indexController.currentDestinationFeature.clear(); - } - if (i >= destinations.length - 1) { - indexController.currentDestinationFeature.add(destinations[0]); - //getAction(); - } else { - indexController.currentDestinationFeature.add(destinations[i + 1]); - //getAction(); - } - } - } - } - - void makePrevious(Destination pt) { - for (int i = 0; i <= destinations.length - 1; i++) { - Destination p = destinations[i]; - - if (p.lat == pt.lat && p.lon == pt.lon) { - if (indexController.currentDestinationFeature.isNotEmpty) { - indexController.currentDestinationFeature.clear(); - } - if (i <= 0) { - indexController.currentDestinationFeature - .add(destinations[destinations.length - 1]); - //getAction(); - } else { - indexController.currentDestinationFeature.add(destinations[i - 1]); - //getAction(); - } - } - } - } } diff --git a/lib/pages/destination/destination_page.dart b/lib/pages/destination/destination_page.dart deleted file mode 100644 index 49a7ebc..0000000 --- a/lib/pages/destination/destination_page.dart +++ /dev/null @@ -1,212 +0,0 @@ - -import 'package:flutter/material.dart'; -import 'package:geolocator/geolocator.dart'; -import 'package:get/get.dart'; -import 'package:latlong2/latlong.dart'; -import 'package:rogapp/pages/destination/destination_controller.dart'; -import 'package:rogapp/pages/destination_map/destination_map_page.dart'; -import 'package:rogapp/pages/drawer/drawer_page.dart'; -import 'package:rogapp/pages/index/index_controller.dart'; -import 'package:rogapp/routes/app_pages.dart'; -import 'package:rogapp/widgets/destination_widget.dart'; - -class DestnationPage extends StatelessWidget { - DestnationPage({Key? key}) : super(key: key); - - final DestinationController destinationController = Get.find(); - - final IndexController indexController = Get.find(); - - final List _items = List.generate(50, (int index) => index); - - Future showCurrentPosition() async { - LocationPermission permission = await Geolocator.checkPermission(); - if (permission != LocationPermission.whileInUse || - permission != LocationPermission.always) { - permission = await Geolocator.requestPermission(); - } - Position position = await Geolocator.getCurrentPosition( - desiredAccuracy: LocationAccuracy.high); - indexController.rogMapController.move(LatLng(position.latitude, position.longitude), 14); - } - - Image getImage(int index){ - if(destinationController.destinations[index].photos == null || destinationController.destinations[index].photos == ""){ - return const Image(image: AssetImage('assets/images/empty_image.png')); - } - else{ - return Image(image: NetworkImage(destinationController.destinations[index].photos!)); - } - } - - Widget getRoutingImage(int route){ - switch (route) { - case 0: - return const Image(image: AssetImage('assets/images/p4_9_man.png'), width: 35.0,); - case 1: - return const Image(image: AssetImage('assets/images/p4_8_car.png'), width: 35.0,); - case 2: - return const Image(image: AssetImage('assets/images/p4_10_train.png'), width: 35.0,); - default: - return const Image(image: AssetImage('assets/images/p4_9_man.png'), width: 35.0,); - } - } - -@override - Widget build(BuildContext context) { - final ColorScheme colorScheme = Theme.of(context).colorScheme; - final Color oddItemColor = colorScheme.primary.withOpacity(0.05); - final Color evenItemColor = colorScheme.primary.withOpacity(0.15); - return WillPopScope( - onWillPop: () async { - indexController.switchPage(AppPages.INITIAL); - return false; - }, - child: Scaffold( - - drawer: DrawerPage(), - bottomNavigationBar: BottomAppBar( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: const EdgeInsets.only(left:13.0), - child: InkWell( - child: Obx((() => getRoutingImage(destinationController.travelMode.value))), - onTap: (){ - Get.bottomSheet( - Obx(() => - ListView( - children: [ - Padding( - padding: const EdgeInsets.only(top:30.0, bottom: 30), - child: Center(child: Text("select_travel_mode".tr, style: const TextStyle(fontSize: 22.0, color:Colors.red, fontWeight:FontWeight.bold),),), - ), - ListTile( - selected: destinationController.travelMode == 0 ? true : false, - selectedTileColor: Colors.amber.shade200, - leading: const Image(image: AssetImage('assets/images/p4_9_man.png'),), - title: Text("walking".tr), - onTap:(){ - destinationController.travelMode.value = 0; - destinationController.PopulateDestinations(); - Get.back(); - }, - ), - ListTile( - selected: destinationController.travelMode == 1 ? true : false, - selectedTileColor: Colors.amber.shade200, - leading: const Image(image: AssetImage('assets/images/p4_8_car.png'),), - title: Text("driving".tr), - onTap:(){ - destinationController.travelMode.value = 1; - destinationController.PopulateDestinations(); - Get.back(); - }, - ), - // ListTile( - // selected: destinationController.travelMode == 2 ? true : false, - // selectedTileColor: Colors.amber.shade200, - // leading: Image(image: AssetImage('assets/images/p4_10_train.png'),), - // title: Text("transit".tr), - // onTap:(){ - // destinationController.travelMode.value = 2; - // destinationController.PopulateDestinations(); - // Get.back(); - // }, - // ), - ], - - ), - ), - isScrollControlled:false, - backgroundColor: Colors.white, - ); - //destinationController.PopulateDestinations(); - } - ), - ) - , - IconButton( - icon: const Icon(Icons.travel_explore, size: 35,), - onPressed: (){ - indexController.switchPage(AppPages.INITIAL); - } - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: (){ - //print("######"); - indexController.toggleDestinationMode(); - }, - tooltip: 'Increment', - elevation: 4.0, - child: Obx(() => - indexController.desination_mode == 1 ? - const Image(image: AssetImage('assets/images/list2.png')) - : - const Image(image: AssetImage('assets/images/map.png')) - ), - ), - floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, - appBar:AppBar( - automaticallyImplyLeading: true, - title: Text("app_title".tr), - actions: [ - InkWell( - onTap: (){ - Get.toNamed(AppPages.CAMERA_PAGE); - }, - child: destinationController.is_in_rog == true ? - Image.asset("assets/images/basic-walking.gif",height: 10.0,) - : - destinationController.is_at_goal == true ? - IconButton( - onPressed:(){Get.toNamed(AppPages.CAMERA_PAGE);}, - icon: const Icon(Icons.assistant_photo), - ) - : - IconButton( - onPressed:(){Get.toNamed(AppPages.CAMERA_PAGE);}, - icon: const Icon(Icons.accessibility), - ), - ), - // Obx(() => - // Text(indexController.connectionStatusName.value) - // ), - Obx(() => - ToggleButtons( - disabledColor: Colors.grey.shade200, - selectedColor: Colors.red, - onPressed: (int index) { - destinationController.is_gps_selected.value = !destinationController.is_gps_selected.value; - if(destinationController.is_gps_selected.value){ - destinationController.chekcs = 0; - destinationController.skip_gps = false; - //destinationController.resetRogaining(); - } - }, - isSelected: [destinationController.is_gps_selected.value], - children: const [ - Icon(Icons.explore, size: 35.0, - )], - ), - ), - // IconButton(onPressed: (){ - // showCurrentPosition(); - // }, - // icon: Icon(Icons.location_on_outlined)) - ], - ), - body: Obx(() => - indexController.desination_mode.value == 0 ? - DestinationWidget(): - DestinationMapPage() - ) - ), - ); - - } -} diff --git a/lib/pages/destination_map/destination_map_controller.dart b/lib/pages/destination_map/destination_map_controller.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/pages/destination_map/destination_map_page.dart b/lib/pages/destination_map/destination_map_page.dart index 63bd1ab..1310c9c 100644 --- a/lib/pages/destination_map/destination_map_page.dart +++ b/lib/pages/destination_map/destination_map_page.dart @@ -1,429 +1,207 @@ - import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter_map/plugin_api.dart'; +import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_location_marker/flutter_map_location_marker.dart'; -import 'package:flutter_map_marker_popup/flutter_map_marker_popup.dart'; +//import 'package:flutter_map_marker_popup/flutter_map_marker_popup.dart'; import 'package:flutter_polyline_points/flutter_polyline_points.dart'; import 'package:get/get.dart'; import 'package:latlong2/latlong.dart'; -import 'package:rogapp/model/destination.dart'; -import 'package:rogapp/pages/destination/destination_controller.dart'; -import 'package:rogapp/pages/index/index_controller.dart'; -import 'package:rogapp/utils/text_util.dart'; -import 'package:rogapp/widgets/base_layer_widget.dart'; -import 'package:rogapp/widgets/bottom_sheet_new.dart'; - +import 'package:gifunavi/model/destination.dart'; +import 'package:gifunavi/pages/destination/destination_controller.dart'; +import 'package:gifunavi/pages/index/index_controller.dart'; +import 'package:gifunavi/utils/text_util.dart'; +import 'package:gifunavi/widgets/base_layer_widget.dart'; +import 'package:gifunavi/widgets/bottom_sheet_new.dart'; +//import 'package:gifunavi/widgets/bottom_sheets/bottom_sheet_start.dart'; +//import 'package:gifunavi/widgets/bottom_sheets/bottom_sheet_goal.dart'; +//import 'package:gifunavi/widgets/bottom_sheets/bottom_sheet_normal_point.dart'; +// FlutterMapウィジェットを使用して、地図を表示します。 +// IndexControllerから目的地のリストを取得し、マーカーとしてマップ上に表示します。 +// マーカーがタップされると、BottomSheetウィジェットを表示します。 +// 現在地の表示、ルートの表示、ベースレイヤーの表示などの機能を提供します。 +// 主なロジック: +// FlutterMapウィジェットを使用して、地図を表示します。 +// IndexControllerから目的地のリストを取得し、MarkerLayerを使用してマーカーを表示します。 +// getMarkerShapeメソッドを使用して、マーカーの見た目をカスタマイズします。目的地の種類に応じて、異なるマーカーを表示します。 +// マーカーがタップされると、festuretoDestinationメソッドを使用してGeoJSONFeatureをDestinationオブジェクトに変換し、showModalBottomSheetを使用してBottomSheetウィジェットを表示します。 +// CurrentLocationLayerを使用して、現在地をマップ上に表示します。 +// PolylineLayerを使用して、ルートをマップ上に表示します。getPointsメソッドを使用して、ルートの座標を取得します。 +// BaseLayerを使用して、マップのベースレイヤーを表示します。 +// class DestinationMapPage extends StatelessWidget { - DestinationMapPage({Key? key}) : super(key: key); + DestinationMapPage({super.key}); final IndexController indexController = Get.find(); - final DestinationController destinationController = Get.find(); + final DestinationController destinationController = + Get.find(); StreamSubscription? subscription; - final PopupController _popupLayerController = PopupController(); + //final PopupController _popupLayerController = PopupController(); - List? getPoints(){ - print("##### --- route point ${indexController.routePoints.length}"); - List pts = []; - for(PointLatLng p in indexController.routePoints){ - LatLng l = LatLng(p.latitude, p.longitude); - pts.add(l); - } - return pts; + List? getPoints() { + //print("##### --- route point ${indexController.routePoints.length}"); + List pts = []; + for (PointLatLng p in indexController.routePoints) { + LatLng l = LatLng(p.latitude, p.longitude); + pts.add(l); } + return pts; + } - List? getMarkers() { - List pts = []; - int index = -1; - for (int i = 0; i < destinationController.destinations.length; i++) { - Destination d = destinationController.destinations[i]; - print("^^^^ $d ^^^^"); - Marker m = Marker( + // 要検討:マーカーのタップイベントを処理する際に、エラーハンドリングが不十分です。例外が発生した場合の処理を追加することをお勧めします。 + // + List? getMarkers() { + List pts = []; + //int index = -1; + for (int i = 0; i < destinationController.destinations.length; i++) { + Destination d = destinationController.destinations[i]; + //print("^^^^ $d ^^^^"); + Marker m = Marker( point: LatLng(d.lat!, d.lon!), - anchorPos: AnchorPos.align(AnchorAlign.center), - builder:(cts){ + alignment: Alignment.center, + child: InkWell( + onTap: () { + //print("-- Destination is --- ${d.name} ------"); + if (indexController.currentDestinationFeature.isNotEmpty) { + indexController.currentDestinationFeature.clear(); + } + indexController.currentDestinationFeature.add(d); + //indexController.getAction(); - return InkWell( - onTap: (){ - print("-- Destination is --- ${d.name} ------"); - if(indexController.currentDestinationFeature.isNotEmpty) { - indexController.currentDestinationFeature.clear(); - } - indexController.currentDestinationFeature.add(d); - //indexController.getAction(); - - showModalBottomSheet(context: Get.context!, isScrollControlled: true, - builder:((context) => BottomSheetNew()) - ).whenComplete((){ - print("---- set skip gps to false -----"); - destinationController.skip_gps = false; - }); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - width:20, - height:20, - decoration: BoxDecoration( - color: Colors.red, - shape: BoxShape.circle, - border: Border.all( - color: Colors.white, - width: d.checkin_radious != null ? d.checkin_radious! : 1, - ), - ), - child: Center( - child: Text( - (i + 1).toString(), - style: const TextStyle(color: Colors.white), - ), + Widget bottomSheet = BottomSheetNew(destination: d); + /* + if (d.cp == -1 || d.cp == 0) { + bottomSheet = BottomSheetStart(destination: d); + } else if (d.cp == -2 || d.cp == 0) { + bottomSheet = BottomSheetGoal(destination: d); + } else { + bottomSheet = BottomSheetNormalPoint(destination: d); + } + */ + + showModalBottomSheet( + context: Get.context!, + isScrollControlled: true, + constraints: + BoxConstraints.loose(Size(Get.width, Get.height * 0.85)), + builder: ((context) => bottomSheet ), + + ).whenComplete(() { + //print("---- set skip gps to false -----"); + destinationController.skipGps = false; + }); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + color: Colors.red, + shape: BoxShape.circle, + border: Border.all( + color: Colors.white, + width: d.checkin_radious != null ? d.checkin_radious! : 1, ), ), - Container( color: Colors.yellow, child: Text(TextUtils.getDisplayText(d), style: const TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold, overflow: TextOverflow.visible),)), - ], - ), - ); - - }); - - pts.add(m); - } - return pts; + child: Center( + child: Text( + (i + 1).toString(), + style: const TextStyle(color: Colors.white), + ), + ), + ), + Container( + color: Colors.yellow, + child: Text( + TextUtils.getDisplayText(d), + style: const TextStyle( + fontSize: 15.0, + fontWeight: FontWeight.bold, + overflow: TextOverflow.visible), + )), + ], + ), + )); + + pts.add(m); } + return pts; + } @override Widget build(BuildContext context) { - return Obx((() => - Stack( - children: [ - // indexController.is_rog_mapcontroller_loaded.value == false ? - // Center(child: CircularProgressIndicator()) - // : - // Padding( - // padding: const EdgeInsets.only(left:8.0), - // child: BreadCrumbWidget(mapController:indexController.rogMapController), - // ), - Padding( - padding: const EdgeInsets.only(top:0.0), - //child: TravelMap(), - child: - TravelMap(), - ), - ], - ) - )); + return Obx((() => Stack( + children: [ + // indexController.is_rog_mapcontroller_loaded.value == false ? + // Center(child: CircularProgressIndicator()) + // : + // Padding( + // padding: const EdgeInsets.only(left:8.0), + // child: BreadCrumbWidget(mapController:indexController.rogMapController), + // ), + Padding( + padding: const EdgeInsets.only(top: 0.0), + //child: TravelMap(), + child: travelMap(), + ), + ], + ))); } - FlutterMap TravelMap() { + // 要検討:MapOptionsのboundsプロパティにハードコードされた座標が使用されています。これを動的に設定できるようにすることを検討してください。 + // + FlutterMap travelMap() { return FlutterMap( mapController: indexController.rogMapController, - options: MapOptions( - onMapReady: (){ - indexController.is_rog_mapcontroller_loaded.value = true; - subscription = indexController.rogMapController.mapEventStream.listen((MapEvent mapEvent) { - if (mapEvent is MapEventMoveStart) { - } + options: MapOptions( + onMapReady: () { + indexController.isRogMapcontrollerLoaded.value = true; + subscription = indexController.rogMapController.mapEventStream + .listen((MapEvent mapEvent) { + if (mapEvent is MapEventMoveStart) {} if (mapEvent is MapEventMoveEnd) { //destinationController.is_gps_selected.value = true; //indexController.mapController!.move(c.center, c.zoom); LatLngBounds bounds = indexController.rogMapController.bounds!; indexController.currentBound.clear(); indexController.currentBound.add(bounds); - if(indexController.currentUser.isEmpty){ - indexController.loadLocationsBound(); + if (indexController.currentUser.isEmpty) { + indexController.loadLocationsBound(indexController.currentUser[0]["user"]["event_code"]); } } }); - } , - bounds: indexController.currentBound.isNotEmpty ? indexController.currentBound[0]: LatLngBounds.fromPoints([LatLng(35.03999881162295, 136.40587119778962), LatLng(36.642756778706904, 137.95226720406063)]), - zoom: 1, - maxZoom: 42, - interactiveFlags: InteractiveFlag.pinchZoom | InteractiveFlag.drag, - ), - children: [ - const BaseLayer(), - Obx(() => - indexController.routePointLenght > 0 ? - PolylineLayer( - polylines: [ - Polyline( - points: getPoints()!, - strokeWidth: 6.0, - color: Colors.indigo - ), - ], - ) - - : - Container(), + }, + bounds: indexController.currentBound.isNotEmpty + ? indexController.currentBound[0] + : LatLngBounds.fromPoints([ + const LatLng(35.03999881162295, 136.40587119778962), + const LatLng(36.642756778706904, 137.95226720406063) + ]), + initialZoom: 1, + maxZoom: 42, + interactiveFlags: InteractiveFlag.pinchZoom | InteractiveFlag.drag, ), - CurrentLocationLayer(), - MarkerLayer( - markers: getMarkers()! - ), - ], - - ); + children: [ + const BaseLayer(), + Obx( + () => indexController.routePointLenght > 0 + ? PolylineLayer( + polylines: [ + Polyline( + points: getPoints()!, + strokeWidth: 6.0, + color: Colors.indigo), + ], + ) + : Container(), + ), + CurrentLocationLayer(), + MarkerLayer(markers: getMarkers()!), + ], + ); } } - - - - -// class DestinationMapPage extends StatefulWidget { -// DestinationMapPage({ Key? key }) : super(key: key); - - -// @override -// State createState() => _DestinationMapPageState(); -// } - -//class _DestinationMapPageState extends State { - // final IndexController indexController = Get.find(); - - // final DestinationController destinationController = Get.find(); - // StreamSubscription? subscription; - // final PopupController _popupLayerController = PopupController(); - - // List? getPoints(List ptts){ - // //print("##### --- route point ${indexController.routePoints.length}"); - // List pts = []; - // for(PointLatLng p in ptts){ - // LatLng l = LatLng(p.latitude, p.longitude); - // pts.add(l); - // } - // return pts; - // } - - // String getDisplaytext(Destination dp){ - // String txt = ""; - // if(dp.cp! > 0){ - // txt = "${dp.cp}"; - // if(dp.checkin_point != null && dp.checkin_point! > 0){ - // txt = txt + "{${dp.checkin_point}}"; - // } - // if(dp.buy_point != null && dp.buy_point! > 0){ - // txt = txt + "[${dp.buy_point}]"; - // } - // } - // return txt; - // } - - // List? getMarkers() { - // List pts = []; - // int index = -1; - // for (int i = 0; i < destinationController.destinations.length; i++) { - // Destination d = destinationController.destinations[i]; - // //for(Destination d in destinationController.destinations){ - // //print("-----lat ${lat}, ----- lon ${lan}"); - // Marker m = Marker( - // point: LatLng(d.lat!, d.lon!), - // anchorPos: AnchorPos.align(AnchorAlign.center), - // builder:(cts){ - - // return InkWell( - // onTap: (){ - // print("-- Destination is --- ${d.name} ------"); - // if(d != null){ - // if(indexController.currentDestinationFeature.length > 0) { - // indexController.currentDestinationFeature.clear(); - // } - // indexController.currentDestinationFeature.add(d); - // //indexController.getAction(); - - // showModalBottomSheet(context: context, isScrollControlled: true, - // //builder:((context) => BottomSheetWidget()) - // builder:((context) => BottomSheetNew()) - // ); - // } - // }, - // child: Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // Container( - // width:20, - // height:20, - // decoration: BoxDecoration( - // color: Colors.red, - // shape: BoxShape.circle, - // border: new Border.all( - // color: Colors.white, - // width: d.checkin_radious != null ? d.checkin_radious! : 1, - // ), - // ), - // child: new Center( - // child: new Text( - // (i + 1).toString(), - // style: TextStyle(color: Colors.white), - // ), - // ), - // ), - // Container( color: Colors.yellow, child: Text(getDisplaytext(d), style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold, overflow: TextOverflow.visible),)), - // ], - // ), - // ); - - // //return Icon(Icons.pin_drop); - // // return IconButton( - // // onPressed: ()async { - // // Destination? fs = await destinationController.getDEstinationForLatLong(d.lat!, d.lon!); - // // print("-- Destination is --- ${fs!.name} ------"); - // // if(fs != null){ - // // if(indexController.currentDestinationFeature.length > 0) { - // // indexController.currentDestinationFeature.clear(); - // // } - // // indexController.currentDestinationFeature.add(fs); - // // //indexController.getAction(); - - // // showModalBottomSheet(context: context, isScrollControlled: true, - // // //builder:((context) => BottomSheetWidget()) - // // builder:((context) => BottomSheetNew()) - // // ); - // // } - // // }, - // // icon: Container( - // // width: 60, - // // height: 60, - // // decoration: BoxDecoration( - // // borderRadius: BorderRadius.circular(d.checkin_radious ?? 0), - // // color: Colors.transparent, - // // border: BoxBorder() - // // ), - // // child: Icon(Icons.pin_drop) - // // ) - // // ); - - // }); - - // pts.add(m); - // } - // return pts; - // } - - // @override - // void initState() { - - // //indexController.routePoints.clear(); - // DestinationService.getDestinationLine(destinationController.destinations)?.then((value){ - // //print("---- loading destination points ------ ${value}"); - // setState(() { - // indexController.routePoints = value; - // }); - // }); - // super.initState(); - // } - - // void reload(){ - // setState(() { - - // }); - // } - - // @override - // Widget build(BuildContext context) { - // return Obx((() => - // Stack( - // children: [ - // indexController.is_rog_mapcontroller_loaded.value == false ? - // Center(child: CircularProgressIndicator()) - // : - // BreadCrumbWidget(mapController:indexController.rogMapController), - // Padding( - // padding: const EdgeInsets.only(top:50.0), - // //child: TravelMap(), - // child: - // TravelMap(indexController.routePoints), - // ), - // // Positioned( - // // bottom: 200, - // // left: 10, - // // child: Container( - // // color: Colors.white, - // // child: Row( - // // children: [ - // // Text(destinationController.gps[0]), - // // Text(destinationController.locationPermission[0]) - // // ], - // // ), - // // ) - // // ), - // ], - // ) - // )); - // } - - // FlutterMap TravelMap(List ptts) { - // return FlutterMap( - // options: MapOptions( - // onMapCreated: (c){ - // indexController.rogMapController = c; - // indexController.rogMapController!.onReady.then((_) { - // indexController.is_rog_mapcontroller_loaded.value = true; - // subscription = indexController.rogMapController!.mapEventStream.listen((MapEvent mapEvent) { - // if (mapEvent is MapEventMoveStart) { - // //print(DateTime.now().toString() + ' [MapEventMoveStart] START'); - // // do something - // } - // if (mapEvent is MapEventMoveEnd) { - // destinationController.isSelected.value = false; - // //print(DateTime.now().toString() + ' [MapEventMoveStart] END'); - // //indexController.loadLocationsBound(); - // } - // }); - // }); - // } , - // bounds: indexController.currentBound.length > 0 ? indexController.currentBound[0]: LatLngBounds.fromPoints([LatLng(35.03999881162295, 136.40587119778962), LatLng(36.642756778706904, 137.95226720406063)]), - // zoom: 1, - // maxZoom: 42, - // interactiveFlags: InteractiveFlag.pinchZoom | InteractiveFlag.drag, - // //plugins: [LocationMarkerPlugin(),] - // ), - // children: [ - // TileLayerWidget( - // options: TileLayerOptions( - // urlTemplate: 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', - // subdomains: ['a', 'b', 'c'], - // ), - // ), - // //Obx(() => - // indexController.routePoints.length > 0 ? - // PolylineLayerWidget( - // options: PolylineLayerOptions( - // polylines: [ - // Polyline( - // points: getPoints(ptts)!, - // strokeWidth: 6.0, - // color: Colors.indigo - // ), - // ], - // ), - // ) - // : - // Container(), - // //), - // // PopupMarkerLayerWidget( - // // options: PopupMarkerLayerOptions( - // // popupController: _popupLayerController, - // // markers: _markers, - // // markerRotateAlignment: - // // PopupMarkerLayerOptions.rotationAlignmentFor(AnchorAlign.top), - // // popupBuilder: (BuildContext context, Marker marker) => - - // // examplePopup(marker), - // // ), - // // ), - // LocationMarkerLayerWidget(), - // MarkerLayerWidget( - // options: MarkerLayerOptions( - // markers: getMarkers()! - // ), - // ), - // ], - - // ); - // } -//} \ No newline at end of file diff --git a/lib/pages/drawer/drawer_binding.dart b/lib/pages/drawer/drawer_binding.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/pages/drawer/drawer_page.dart b/lib/pages/drawer/drawer_page.dart index 169cbad..fd1e806 100644 --- a/lib/pages/drawer/drawer_page.dart +++ b/lib/pages/drawer/drawer_page.dart @@ -1,128 +1,274 @@ - import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:rogapp/pages/index/index_controller.dart'; -import 'package:rogapp/routes/app_pages.dart'; -import 'package:rogapp/services/auth_service.dart'; +import 'package:gifunavi/pages/destination/destination_controller.dart'; +import 'package:gifunavi/pages/index/index_controller.dart'; +import 'package:gifunavi/routes/app_pages.dart'; +import 'package:gifunavi/services/auth_service.dart'; +import 'package:gifunavi/utils/database_helper.dart'; +import 'package:gifunavi/widgets/debug_widget.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'package:gifunavi/pages/WebView/WebView_page.dart'; +// SafeAreaウィジェットを使用して、画面の安全領域内にメニューを表示しています。 +// Columnウィジェットを使用して、メニューアイテムを縦に並べています。 +// class DrawerPage extends StatelessWidget { - DrawerPage({ Key? key }) : super(key: key); + DrawerPage({super.key}); final IndexController indexController = Get.find(); + LogManager logManager = LogManager(); + + // 要検討:URLの起動に失敗した場合のエラーハンドリングが不十分です。適切なエラーメッセージを表示するなどの処理を追加してください。 + // + /* void _launchURL(url) async { - if (!await launch(url)) throw 'Could not launch $url'; + if (!await launchUrl(url)) throw 'Could not launch $url'; } + */ + + void _launchURL(BuildContext context,String urlString) async { + try { + logManager.addOperationLog('User clicked $urlString on the drawer'); + Uri url = Uri.parse(urlString); + if (await canLaunchUrl(url)) { + await launchUrl(url); + } else { + // URLを開けない場合のフォールバック動作 + // 例えば、WebViewを使用してアプリ内でURLを開く + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => WebViewPage(url: urlString), + ), + ); + } + }catch(e){ + // エラーメッセージを表示する + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('URLを開けませんでした: $e')), + ); + } + } + @override Widget build(BuildContext context) { return SafeArea( child: Drawer( - // Add a ListView to the drawer. This ensures the user can scroll - // through the options in the drawer if there isn't enough vertical - // space to fit everything. child: Column( children: [ Container( height: 100, color: Colors.amber, - child: Obx(() => - Center( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: - indexController.currentUser.isEmpty ? - Flexible(child: Text("drawer_title".tr, style: const TextStyle(color: Colors.black, fontSize: 20),)) - : - Text(indexController.currentUser[0]['user']['email'], style: const TextStyle(color: Colors.black, fontSize: 20),), - ), - ) - ), + child: Obx(() => Center( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: indexController.currentUser.isEmpty + ? Flexible( + child: Text( + "drawer_title".tr, + style: const TextStyle( + color: Colors.black, fontSize: 20), + )) + : Text( + indexController.currentUser[0]['user']['email'], + style: const TextStyle( + color: Colors.black, fontSize: 20), + ), + ), + )), ), - Obx(() => - indexController.currentUser.isEmpty ? - ListTile( - leading: const Icon(Icons.login), - title: Text("login".tr), - onTap: (){ - Get.toNamed(AppPages.LOGIN); - }, - ) : - ListTile( - leading: const Icon(Icons.login), - title: Text("logout".tr), - onTap: (){ - indexController.logout(); - Get.toNamed(AppPages.TRAVEL); - }, - ) - ), - indexController.currentUser.isNotEmpty ? + ListTile( - leading: const Icon(Icons.password), - title: Text("change_password".tr), - onTap: (){ - Get.toNamed(AppPages.CHANGE_PASSWORD); + leading: const Icon(Icons.group), + title: const Text('チーム管理'), + onTap: () async{ + //Get.back(); + // スナックバーを安全に閉じる + await _safelyCloseSnackbar(); + Get.toNamed(AppPages.TEAM_LIST); }, - ) : - const SizedBox(width: 0, height: 0,), - indexController.currentUser.isEmpty ? + ), + ListTile( + leading: const Icon(Icons.event), + title: const Text('エントリー管理'), + onTap: () { + Get.back(); + Get.toNamed(AppPages.ENTRY_LIST); + }, + ), + ListTile( + leading: const Icon(Icons.event), + title: const Text('イベント参加'), + onTap: () { + Get.back(); // ドロワーを閉じる + Get.toNamed(AppPages.EVENT_ENTRY); + }, + ), ListTile( leading: const Icon(Icons.person), - title: Text("sign_up".tr), - onTap: (){ - Get.toNamed(AppPages.REGISTER); + title: const Text("個人情報の修正"), + onTap: () { + Get.back(); // Close the drawer + Get.toNamed(AppPages.USER_DETAILS_EDIT); }, - ) : - const SizedBox(width: 0, height: 0,), - indexController.currentUser.isNotEmpty ? - ListTile( - leading: const Icon(Icons.delete_forever), - title: Text("delete_account".tr), - onTap: (){ - String token = indexController.currentUser[0]['token']; - AuthService.deleteUser(token).then((value){ - if(value.isNotEmpty){ - indexController.logout(); - Get.toNamed(AppPages.TRAVEL); - Get.snackbar("accounted_deleted".tr, "account_deleted_message".tr); - } - }); - }, - ) : - const SizedBox(width: 0, height: 0,), - // ListTile( - // leading: const Icon(Icons.person), - // title: Text("profile".tr), - // onTap: (){}, - // ), - // ListTile( - // leading: const Icon(Icons.route), - // title: Text("recommended_route".tr), - // onTap: (){}, - // ), - // ListTile( - // leading: const Icon(Icons.favorite_rounded), - // title: Text("point_rank".tr), - // onTap: (){}, - // ), - indexController.currentUser.isNotEmpty ? - ListTile( - leading: const Icon(Icons.featured_video), - title: Text("rog_web".tr), - onTap: (){ - _launchURL("https://www.gifuai.net/?page_id=17397"); - }, - ) : - const SizedBox(width: 0, height: 0,), + ), + + Obx(() => indexController.currentUser.isEmpty + ? ListTile( + leading: const Icon(Icons.login), + title: Text("login".tr), + onTap: () { + Get.toNamed(AppPages.LOGIN); + }, + ) + : ListTile( + leading: const Icon(Icons.login), + title: Text("logout".tr), + onTap: () { + indexController.logout(); + Get.toNamed(AppPages.LOGIN); + }, + )), + indexController.currentUser.isNotEmpty + ? ListTile( + leading: const Icon(Icons.password), + title: Text("change_password".tr), + onTap: () { + Get.toNamed(AppPages.CHANGE_PASSWORD); + }, + ) + : const SizedBox( + width: 0, + height: 0, + ), + indexController.currentUser.isEmpty + ? ListTile( + leading: const Icon(Icons.person), + title: Text("sign_up".tr), + onTap: () { + Get.toNamed(AppPages.REGISTER); + }, + ) + : const SizedBox( + width: 0, + height: 0, + ), + indexController.currentUser.isNotEmpty + ? ListTile( + leading: const Icon(Icons.password), + title: Text('reset_button'.tr), + onTap: () { + logManager.addOperationLog('User clicked RESET button on the drawer'); + // 要検討:リセット操作の確認メッセージをローカライズすることを検討してください。 + // + Get.defaultDialog( + title: "reset_title".tr, + middleText: "reset_message".tr, + textConfirm: "confirm".tr, + textCancel: "cancel".tr, + onCancel: () => Get.back(), + onConfirm: () async { + DestinationController destinationController = + Get.find(); + DatabaseHelper databaseHelper = DatabaseHelper.instance; + + // ゲーム中のデータを削除 + await databaseHelper.deleteAllRogaining(); + await databaseHelper.deleteAllDestinations(); + destinationController.resetRogaining(); + + //destinationController.resetRogaining(); + //destinationController.deleteDBDestinations(); + Get.back(); + Get.snackbar( + "reset_done".tr, + "reset_explain".tr, + backgroundColor: Colors.green, + colorText: Colors.white, + duration: const Duration(seconds: 3), + ); + }, + ); + }, + ) + : const SizedBox( + width: 0, + height: 0, + ), + indexController.currentUser.isNotEmpty + ? ListTile( + leading: const Icon(Icons.delete_forever), + title: Text("delete_account".tr), + onTap: () { + Get.defaultDialog( + title: "delete_account_title".tr, + middleText: "delete_account_middle".tr, + textConfirm: "confirm".tr, + textCancel: "cancel".tr, + onCancel: () => Get.back(), + onConfirm: () { + logManager.addOperationLog('User clicked Confirm button on the account delete dialog'); + String token = indexController.currentUser[0]['token']; + AuthService.deleteUser(token).then((value) { + if (value.isNotEmpty) { + indexController.logout(); + Get.toNamed(AppPages.TRAVEL); + Get.snackbar("accounted_deleted".tr, + "account_deleted_message".tr, + backgroundColor: Colors.green, + colorText: Colors.white + ); + } + }); + }, + ); + }, + ) + : const SizedBox( + width: 0, + height: 0, + ), + indexController.currentUser.isNotEmpty + ? ListTile( + leading: const Icon(Icons.featured_video), + title: Text("rog_web".tr), + onTap: () { + _launchURL(context, "https://www.gifuai.net/?page_id=60043"); + }, + ) + : const SizedBox( + width: 0, + height: 0, + ), + ListTile( leading: const Icon(Icons.privacy_tip), title: Text("privacy".tr), - onTap: (){ - _launchURL("https://rogaining.sumasen.net/api/privacy/"); + onTap: () { + _launchURL(context, "https://rogaining.sumasen.net/api/privacy/"); }, - ) + ), + ListTile( + leading: const Icon(Icons.settings), + title: Text('open_settings'.tr), + onTap: () { + Get.back(); // ドロワーを閉じる + Get.toNamed(Routes.SETTINGS); + }, + ), + + //ListTile( + // leading: const Icon(Icons.developer_mode), + // title: const Text('open_settings'), + // onTap: () { + // Get.back(); // ドロワーを閉じる + // Get.toNamed('/debug'); // デバッグ画面に遷移 + // }, + //), + + // ListTile( // leading: const Icon(Icons.router), // title: Text("my_route".tr), @@ -138,4 +284,14 @@ class DrawerPage extends StatelessWidget { ), ); } -} \ No newline at end of file + + Future _safelyCloseSnackbar() async { + if (Get.isSnackbarOpen) { + try { + await Get.closeCurrentSnackbar(); + } catch (e) { + print('Error closing snackbar: $e'); + } + } + } +} diff --git a/lib/pages/entry/entry_binding.dart b/lib/pages/entry/entry_binding.dart new file mode 100644 index 0000000..b29ec11 --- /dev/null +++ b/lib/pages/entry/entry_binding.dart @@ -0,0 +1,11 @@ +import 'package:get/get.dart'; +import 'package:gifunavi/pages/entry/entry_controller.dart'; +import 'package:gifunavi/services/api_service.dart'; + +class EntryBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => ApiService()); + Get.lazyPut(() => EntryController()); + } +} \ No newline at end of file diff --git a/lib/pages/entry/entry_controller.dart b/lib/pages/entry/entry_controller.dart new file mode 100644 index 0000000..4e71d86 --- /dev/null +++ b/lib/pages/entry/entry_controller.dart @@ -0,0 +1,310 @@ +// lib/entry/entry_controller.dart + +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:gifunavi/model/entry.dart'; +import 'package:gifunavi/model/event.dart'; +import 'package:gifunavi/model/team.dart'; +import 'package:gifunavi/model/category.dart'; +import 'package:gifunavi/services/api_service.dart'; + +import '../index/index_controller.dart'; +import 'package:timezone/timezone.dart' as tz; + +class EntryController extends GetxController { + late ApiService _apiService; + + final entries = [].obs; + final events = [].obs; + final teams = [].obs; + final categories = [].obs; + + final selectedEvent = Rx(null); + final selectedTeam = Rx(null); + final selectedCategory = Rx(null); + final selectedDate = Rx(null); + + final currentEntry = Rx(null); + final isLoading = true.obs; + + @override + void onInit() async { + super.onInit(); + await initializeApiService(); + await loadInitialData(); + } + + Future initializeApiService() async { + try { + _apiService = await Get.putAsync(() => ApiService().init()); + } catch (e) { + print('Error initializing ApiService: $e'); + Get.snackbar('Error', 'Failed to initialize API service'); + } + } + + Future loadInitialData() async { + try { + isLoading.value = true; + await Future.wait([ + fetchEntries(), + fetchEvents(), + fetchTeams(), + fetchCategories(), + ]); + if (Get.arguments != null && Get.arguments['entry'] != null) { + currentEntry.value = Get.arguments['entry']; + initializeEditMode(currentEntry.value!); + } else { + // 新規作成モードの場合、最初のイベントを選択 + if (events.isNotEmpty) { + selectedEvent.value = events.first; + selectedDate.value = events.first.startDatetime; + } + } + } catch(e) { + print('Error initializing data: $e'); + Get.snackbar('Error', 'Failed to load initial data'); + } finally { + isLoading.value = false; + } + } + + + void initializeEditMode(Entry entry) { + currentEntry.value = entry; + selectedEvent.value = entry.event; + selectedTeam.value = entry.team; + selectedCategory.value = entry.category; + selectedDate.value = entry.date; + } + + void updateEvent(Event? value) { + selectedEvent.value = value; + if (value != null) { + // イベント変更時に日付を調整 + if (selectedDate.value == null || + selectedDate.value!.isBefore(value.startDatetime) || + selectedDate.value!.isAfter(value.endDatetime)) { + selectedDate.value = value.startDatetime; + } + } + } + + void updateTeam(Team? value) { + selectedTeam.value = value; + if (value != null) { + selectedCategory.value = value.category; + } + } + //void updateTeam(Team? value) => selectedTeam.value = value; + void updateCategory(NewCategory? value) => selectedCategory.value = value; + //void updateDate(DateTime value) => selectedDate.value = value; + void updateDate(DateTime value) { + selectedDate.value = tz.TZDateTime.from(value, tz.getLocation('Asia/Tokyo')); + } + /* + void updateDate(DateTime value){ + selectedDate.value = DateFormat('yyyy-MM-dd').format(value!) as DateTime?; + } + + */ + + void _initializeEntryData() { + if (currentEntry.value != null) { + selectedEvent.value = currentEntry.value!.event; + selectedTeam.value = currentEntry.value!.team; + selectedCategory.value = currentEntry.value!.category; + selectedDate.value = currentEntry.value!.date; + } + } + + Future fetchEntries() async { + try { + final fetchedEntries = await _apiService.getEntries(); + entries.assignAll(fetchedEntries); + } catch (e) { + print('Error fetching entries: $e'); + Get.snackbar('Error', 'Failed to fetch entries'); + } + } + + Future fetchEvents() async { + try { + final fetchedEvents = await _apiService.getEvents(); + events.assignAll(fetchedEvents.map((event) { + // end_dateの7日前を締め切りとして設定 + final deadlineDateTime = event.endDatetime.subtract(const Duration(days: 7)); + return Event( + id: event.id, + eventName: event.eventName, + startDatetime: event.startDatetime, + endDatetime: event.endDatetime, + deadlineDateTime: deadlineDateTime, + ); + }).toList()); + } catch (e) { + print('Error fetching events: $e'); + Get.snackbar('Error', 'Failed to fetch events'); + } + } + + Future fetchEvents_old() async { + try { + final fetchedEvents = await _apiService.getEvents(); + events.assignAll(fetchedEvents); + } catch (e) { + print('Error fetching events: $e'); + Get.snackbar('Error', 'Failed to fetch events'); + } + } + + Future fetchTeams() async { + try { + final fetchedTeams = await _apiService.getTeams(); + teams.assignAll(fetchedTeams); + } catch (e) { + print('Error fetching teams: $e'); + Get.snackbar('Error', 'Failed to fetch team'); + } + } + + Future fetchCategories() async { + try { + final fetchedCategories = await _apiService.getCategories(); + categories.assignAll(fetchedCategories); + } catch (e) { + print('Error fetching categories: $e'); + Get.snackbar('Error', 'Failed to fetch categories'); + } + } + + Future createEntry() async { + if (selectedEvent.value == null || selectedTeam.value == null || + selectedCategory.value == null || selectedDate.value == null) { + Get.snackbar('Error', 'Please fill all fields'); + return; + } + try { + isLoading.value = true; + // Get zekken number + final updatedCategory = await _apiService.getZekkenNumber(selectedCategory.value!.id); + final zekkenNumber = updatedCategory.categoryNumber.toString(); + + final newEntry = await _apiService.createEntry( + selectedTeam.value!.id, + selectedEvent.value!.id, + selectedCategory.value!.id, + selectedDate.value!, + zekkenNumber, + ); + entries.add(newEntry); + Get.back(); + } catch (e) { + print('Error creating entry: $e'); + Get.snackbar('Error', 'Failed to create entry'); + } finally { + isLoading.value = false; + } + } + + Future updateEntryAndRefreshMap() async { + await updateEntry(); + + // エントリーが正常に更新された後、マップをリフレッシュ + final indexController = Get.find(); + final eventCode = currentEntry.value?.event.eventName ?? ''; + indexController.reloadMap(eventCode); + } + + bool isEntryEditable(Event event) { + return DateTime.now().isBefore(event.deadlineDateTime); + } + + Future updateEntry() async { + if (currentEntry.value == null) { + Get.snackbar('Error', 'No entry selected for update'); + return; + } + + if (!isEntryEditable(currentEntry.value!.event)) { + Get.dialog( + AlertDialog( + title: Text('エントリー変更不可'), + content: Text('締め切りを過ぎているため、エントリーの変更はできません。変更が必要な場合は事務局にお問い合わせください。'), + actions: [ + TextButton( + child: Text('OK'), + onPressed: () => Get.back(), + ), + ], + ), + ); + return; + } + + try { + isLoading.value = true; + final updatedEntry = await _apiService.updateEntry( + currentEntry.value!.id, + currentEntry.value!.team.id, + selectedEvent.value!.id, + selectedCategory.value!.id, + selectedDate.value!, + currentEntry.value!.zekkenNumber, + ); + final index = entries.indexWhere((entry) => entry.id == updatedEntry.id); + if (index != -1) { + entries[index] = updatedEntry; + } + Get.back(); + } catch (e) { + print('Error updating entry: $e'); + Get.snackbar('Error', 'Failed to update entry'); + } finally { + isLoading.value = false; + } + } + + Future updateEntryCategory(int entryId, int newCategoryId) async { + try { + //await _apiService.updateEntryCategory(entryId, newCategoryId); + final updatedEntry = await _apiService.updateEntry( + currentEntry.value!.id, + currentEntry.value!.team.id, + selectedEvent.value!.id, + newCategoryId, + currentEntry.value!.date!, + currentEntry.value!.zekkenNumber, + ); + await fetchEntries(); + } catch (e) { + print('Error updating entry category: $e'); + Get.snackbar('エラー', 'エントリーのカテゴリ更新に失敗しました'); + } + } + + Future deleteEntry() async { + if (currentEntry.value == null) { + Get.snackbar('Error', 'No entry selected for deletion'); + return; + } + try { + isLoading.value = true; + await _apiService.deleteEntry(currentEntry.value!.id); + entries.removeWhere((entry) => entry.id == currentEntry.value!.id); + Get.back(); + } catch (e) { + print('Error deleting entry: $e'); + Get.snackbar('Error', 'Failed to delete entry'); + } finally { + isLoading.value = false; + } + } + + + bool isOwner() { + // Implement logic to check if the current user is the owner of the entry + return true; // Placeholder + } +} \ No newline at end of file diff --git a/lib/pages/entry/entry_detail_page.dart b/lib/pages/entry/entry_detail_page.dart new file mode 100644 index 0000000..f16e050 --- /dev/null +++ b/lib/pages/entry/entry_detail_page.dart @@ -0,0 +1,193 @@ +// lib/pages/entry/entry_detail_page.dart + +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:gifunavi/pages/entry/entry_controller.dart'; +import 'package:gifunavi/model/event.dart'; +import 'package:gifunavi/model/category.dart'; +import 'package:gifunavi/model/team.dart'; +import 'package:intl/intl.dart'; + +import 'package:timezone/timezone.dart' as tz; + +class EntryDetailPage extends GetView { + const EntryDetailPage({super.key}); + + @override + Widget build(BuildContext context) { + final Map arguments = Get.arguments ?? {}; + final mode = Get.arguments['mode'] as String? ?? 'new'; + final entry = Get.arguments['entry']; + + if (mode == 'edit' && entry != null) { + controller.initializeEditMode(entry); + } + + return Scaffold( + appBar: AppBar( + title: Text(mode == 'new' ? 'エントリー登録' : 'エントリー詳細'), + ), + body: Obx(() { + if (controller.isLoading.value) { + return const Center(child: CircularProgressIndicator()); + } + return Padding( + padding: const EdgeInsets.all(16.0), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildDropdown( + label: 'イベント', + items: controller.events, + selectedId: controller.selectedEvent.value?.id, + onChanged: (eventId) => controller.updateEvent( + controller.events.firstWhere((e) => e.id == eventId) + ), + getDisplayName: (event) => event.eventName, + getId: (event) => event.id, + ), + const SizedBox(height: 16), + _buildDropdown( + label: 'チーム', + items: controller.teams, + selectedId: controller.selectedTeam.value?.id, + onChanged: (teamId) => controller.updateTeam( + controller.teams.firstWhere((t) => t.id == teamId) + ), + getDisplayName: (team) => team.teamName, + getId: (team) => team.id, + ), + const SizedBox(height: 16), + _buildCategoryDropdown(), + /* + _buildDropdown() + label: 'カテゴリ', + items: controller.categories, + selectedId: controller.selectedCategory.value?.id, + onChanged: (categoryId) => controller.updateCategory( + controller.categories.firstWhere((c) => c.id == categoryId) + ), + getDisplayName: (category) => category.categoryName, + getId: (category) => category.id, + ), + + */ + const SizedBox(height: 16), + ListTile( + title: const Text('日付'), + subtitle: Text( + controller.selectedDate.value != null + ? DateFormat('yyyy-MM-dd').format(tz.TZDateTime.from(controller.selectedDate.value!, tz.getLocation('Asia/Tokyo'))) + : '日付を選択してください', + ), + onTap: () async { + if (controller.selectedEvent.value == null) { + Get.snackbar('Error', 'Please select an event first'); + return; + } + final tz.TZDateTime now = tz.TZDateTime.now(tz.getLocation('Asia/Tokyo')); + final tz.TZDateTime eventStart = tz.TZDateTime.from(controller.selectedEvent.value!.startDatetime, tz.getLocation('Asia/Tokyo')); + final tz.TZDateTime eventEnd = tz.TZDateTime.from(controller.selectedEvent.value!.endDatetime, tz.getLocation('Asia/Tokyo')); + + final tz.TZDateTime initialDate = controller.selectedDate.value != null + ? tz.TZDateTime.from(controller.selectedDate.value!, tz.getLocation('Asia/Tokyo')) + : (now.isAfter(eventStart) ? now : eventStart); + + // 選択可能な最初の日付を設定(今日かイベント開始日のうち、より後の日付) + final tz.TZDateTime firstDate = now.isAfter(eventStart) ? now : eventStart; + + final DateTime? picked = await showDatePicker( + context: context, + initialDate: initialDate.isAfter(firstDate) ? initialDate : firstDate, + firstDate: firstDate, + lastDate: eventEnd, + ); + if (picked != null) { + controller.updateDate(tz.TZDateTime.from(picked, tz.getLocation('Asia/Tokyo'))); + } + }, + ), + const SizedBox(height: 32), + if (mode == 'new') + ElevatedButton( + onPressed: () => controller.createEntry(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + minimumSize: const Size(double.infinity, 50), + ), + child: const Text('エントリーを作成'), + ) + else + Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: () => controller.deleteEntry(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + foregroundColor: Colors.white, + minimumSize: const Size(0, 50), + ), + child: const Text('エントリーを削除'), + ), + ), + const SizedBox(width: 16), + Expanded( + child: ElevatedButton( + onPressed: () => controller.updateEntry(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.lightBlue, + foregroundColor: Colors.white, + minimumSize: const Size(0, 50), + ), + child: const Text('エントリーを更新'), + ), + ), + ], + ), + ], + ), + ), + ); + }), + ); + } + + Widget _buildDropdown({ + required String label, + required List items, + required int? selectedId, + required void Function(int?) onChanged, + required String Function(T) getDisplayName, + required int Function(T) getId, + }) { + return DropdownButtonFormField( + decoration: InputDecoration(labelText: label), + value: selectedId, + items: items.map((item) => DropdownMenuItem( + value: getId(item), + child: Text(getDisplayName(item)), + )).toList(), + onChanged: onChanged, + ); + } + + Widget _buildCategoryDropdown() { + final eligibleCategories = controller.categories.where((c) => + c.baseCategory == controller.selectedCategory.value?.baseCategory + ).toList(); + + return DropdownButtonFormField( + decoration: InputDecoration(labelText: 'カテゴリ'), + value: controller.selectedCategory.value, + items: eligibleCategories.map((category) => DropdownMenuItem( + value: category, + child: Text(category.categoryName), + )).toList(), + onChanged: (value) => controller.updateCategory(value), + ); + } + +} \ No newline at end of file diff --git a/lib/pages/entry/entry_list_page.dart b/lib/pages/entry/entry_list_page.dart new file mode 100644 index 0000000..1a87226 --- /dev/null +++ b/lib/pages/entry/entry_list_page.dart @@ -0,0 +1,116 @@ +// lib/pages/entry/entry_list_page.dart + +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import 'package:gifunavi/pages/entry/entry_controller.dart'; +import 'package:gifunavi/routes/app_pages.dart'; +import 'package:timezone/timezone.dart' as tz; + +class EntryListPage extends GetView { + const EntryListPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('エントリー管理'), + actions: [ + IconButton( + icon: const Icon(Icons.add), + onPressed: () => Get.toNamed(AppPages.ENTRY_DETAIL, arguments: {'mode': 'new'}), + ), + ], + ), + body: Obx(() { + if (controller.entries.isEmpty) { + return const Center( + child: Text('表示するエントリーがありません。'), + ); + } + return ListView.builder( + itemCount: controller.entries.length, + itemBuilder: (context, index) { + final entry = controller.entries[index]; + return ListTile( + title: Row( + children: [ + Expanded( + child: Text('${_formatDate(entry.date)}: ${entry.event.eventName}'), + ), + Text(entry.team.teamName, style: const TextStyle(fontWeight: FontWeight.bold)), + ], + ), + subtitle: Row( + children: [ + Expanded( + child: Text('カテゴリー: ${entry.category.categoryName}'), + ), + Text('ゼッケン: ${entry.zekkenNumber ?? "未設定"}'), + ], + ), + onTap: () => + Get.toNamed(AppPages.ENTRY_DETAIL, + arguments: {'mode': 'edit', 'entry': entry}), + ); + }, + ); + }), + ); + } + + String _formatDate(DateTime? date) { + if (date == null) { + return '日時未設定'; + } + final jstDate = tz.TZDateTime.from(date, tz.getLocation('Asia/Tokyo')); + return DateFormat('yyyy-MM-dd').format(jstDate); + } +} + +class EntryListPage_old extends GetView { + const EntryListPage_old({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('エントリー管理'), + actions: [ + IconButton( + icon: const Icon(Icons.add), + onPressed: () => Get.toNamed(AppPages.ENTRY_DETAIL, arguments: {'mode': 'new'}), + ), + ], + ), + body: Obx((){ + if (controller.isLoading.value) { + return const Center(child: CircularProgressIndicator()); + } + + // エントリーを日付昇順にソート + final sortedEntries = controller.entries.toList() + ..sort((a, b) => (a.date ?? DateTime(0)).compareTo(b.date ?? DateTime(0))); + + return ListView.builder( + itemCount: sortedEntries.length, + itemBuilder: (context, index) { + final entry = sortedEntries[index]; + return ListTile( + title: Text(entry.event.eventName ?? 'イベント未設定'), + subtitle: Text( + '${entry.team.teamName ?? 'チーム未設定'} - ${entry.category + .categoryName ?? 'カテゴリ未設定'}'), + trailing: Text( + entry.date?.toString().substring(0, 10) ?? '日付未設定'), + onTap: () => + Get.toNamed(AppPages.ENTRY_DETAIL, + arguments: {'mode': 'edit', 'entry': entry}), + ); + }, + ); + }), + ); + + } +} \ No newline at end of file diff --git a/lib/pages/entry/event_entries_binding.dart b/lib/pages/entry/event_entries_binding.dart new file mode 100644 index 0000000..e736b76 --- /dev/null +++ b/lib/pages/entry/event_entries_binding.dart @@ -0,0 +1,9 @@ +import 'package:get/get.dart'; +import 'package:gifunavi/pages/entry/event_entries_controller.dart'; + +class EventEntriesBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => EventEntriesController()); + } +} diff --git a/lib/pages/entry/event_entries_controller.dart b/lib/pages/entry/event_entries_controller.dart new file mode 100644 index 0000000..60d7970 --- /dev/null +++ b/lib/pages/entry/event_entries_controller.dart @@ -0,0 +1,129 @@ +import 'package:get/get.dart'; +import 'package:gifunavi/model/entry.dart'; +import 'package:gifunavi/pages/index/index_controller.dart'; +import 'package:gifunavi/pages/destination/destination_controller.dart'; +import 'package:gifunavi/services/api_service.dart'; +import 'package:flutter/material.dart'; +import 'package:timezone/timezone.dart' as tz; +import 'package:timezone/data/latest.dart' as tz; + +class EventEntriesController extends GetxController { + final ApiService _apiService = Get.find(); + final IndexController _indexController = Get.find(); + late final DestinationController _destinationController; + + final entries = [].obs; + final filteredEntries = [].obs; + final showTodayEntries = true.obs; + + static bool _timezoneInitialized = false; + + @override + void onInit() { + super.onInit(); + _initializeTimezone(); + // DestinationControllerが登録されていない場合に備えて、lazyPutを使用 + Get.lazyPut(() => DestinationController(), fenix: true); + _destinationController = Get.find(); + + fetchEntries(); + } + + void _initializeTimezone() { + if (!_timezoneInitialized) { + tz.initializeTimeZones(); + _timezoneInitialized = true; + } + } + + Future fetchEntries() async { + try { + final fetchedEntries = await _apiService.getEntries(); + entries.assignAll(fetchedEntries); + filterEntries(); + } catch (e) { + print('Error fetching entries: $e'); + // エラー処理を追加 + } + } + + void filterEntries() { + if (showTodayEntries.value) { + filterEntriesForToday(); + } else { + filteredEntries.assignAll(entries); + } + } + + void filterEntriesForToday() { + final now = tz.TZDateTime.now(tz.getLocation('Asia/Tokyo')); + filteredEntries.assignAll(entries.where((entry) { + final entryDate = tz.TZDateTime.from(entry.date!, tz.getLocation('Asia/Tokyo')); + return entryDate.year == now.year && + entryDate.month == now.month && + entryDate.day == now.day; + })); + } + + void filterEntriesForToday_old() { + final now = DateTime.now(); + filteredEntries.assignAll(entries.where((entry) => + entry.date?.year == now.year && + entry.date?.month == now.month && + entry.date?.day == now.day + )); + } + + void toggleShowTodayEntries() { + showTodayEntries.toggle(); + filterEntries(); + } + + void refreshMap() { + final tk = _indexController.currentUser[0]["token"]; + if (tk != null) { + + _destinationController.fixMapBound(tk); + } + } + + Future joinEvent(Entry entry) async { + //final now = DateTime.now(); + final now = tz.TZDateTime.now(tz.getLocation('Asia/Tokyo')); + final entryDate = tz.TZDateTime.from(entry.date!, tz.getLocation('Asia/Tokyo')); + bool isToday = entryDate.year == now.year && + entryDate.month == now.month && + entryDate.day == now.day; + + _indexController.setReferenceMode(!isToday); + _indexController.setSelectedEventName(entry.event.eventName); + + final userid = _indexController.currentUser[0]["user"]["id"]; + + await _apiService.updateUserInfo(userid,entry); + + _indexController.currentUser[0]["user"]["event_date"] = entryDate; // 追加2024-8-9 + _indexController.currentUser[0]["user"]["event_code"] = entry.event.eventName; + _indexController.currentUser[0]["user"]["team_name"] = entry.team.teamName; + _indexController.currentUser[0]["user"]["group"] = entry.team.category.categoryName; + _indexController.currentUser[0]["user"]["zekken_number"] = entry.zekkenNumber; + + Get.back(); // エントリー一覧ページを閉じる + //_indexController.isLoading.value = true; + _indexController.reloadMap(entry.event.eventName); + + refreshMap(); + + if (isToday) { + Get.snackbar('成功', 'イベントに参加しました。', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.green, + colorText: Colors.white); + } else { + Get.snackbar('参照モード', '過去または未来のイベントを参照しています。ロゲの開始やチェックインはできません。', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Colors.orange, + colorText: Colors.white); + } + } +} \ No newline at end of file diff --git a/lib/pages/entry/event_entries_page.dart b/lib/pages/entry/event_entries_page.dart new file mode 100644 index 0000000..75da284 --- /dev/null +++ b/lib/pages/entry/event_entries_page.dart @@ -0,0 +1,108 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import 'package:gifunavi/pages/entry/event_entries_controller.dart'; +import 'package:timezone/timezone.dart' as tz; + +class EventEntriesPage_old extends GetView { + const EventEntriesPage_old({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('イベント参加')), + body: Obx(() => ListView.builder( + itemCount: controller.entries.length, + itemBuilder: (context, index) { + final entry = controller.entries[index]; + return ListTile( + title: Text(entry.event.eventName), + subtitle: Text('${entry.category.categoryName} - ${entry.date}'), + onTap: () => controller.joinEvent(entry), + ); + }, + )), + ); + } +} + +class EventEntriesPage extends GetView { + const EventEntriesPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Obx(() => Text(controller.showTodayEntries.value ? 'イベント参加' : 'イベント参照')), + ), + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Obx(() => Text( + controller.showTodayEntries.value ? '本日のエントリー' : 'すべてのエントリー', + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + )), + Obx(() => Switch( + value: !controller.showTodayEntries.value, + onChanged: (value) { + controller.toggleShowTodayEntries(); + }, + activeColor: Colors.blue, + )), + ], + ), + ), + Expanded( + child: Obx(() { + if (controller.filteredEntries.isEmpty) { + return const Center( + child: Text('表示するエントリーがありません。'), + ); + } + return ListView.builder( + itemCount: controller.filteredEntries.length, + itemBuilder: (context, index) { + final entry = controller.filteredEntries[index]; + return ListTile( + title: Row( + children: [ + Expanded( + child: Text('${_formatDate(entry.date)}: ${entry.event.eventName}'), + ), + Text(entry.team.teamName, style: const TextStyle(fontWeight: FontWeight.bold)), + ], + ), + subtitle: Row( + children: [ + Expanded( + child: Text('カテゴリー: ${entry.category.categoryName}'), + ), + Text('ゼッケン: ${entry.zekkenNumber ?? "未設定"}'), + ], + ), + onTap: () async { + await controller.joinEvent(entry); + }, + ); + }, + ); + }), + ), + ], + ), + ); + } + + String _formatDate(DateTime? date) { + if (date == null) { + return '日時未設定'; + } + final jstDate = tz.TZDateTime.from(date, tz.getLocation('Asia/Tokyo')); + return DateFormat('yyyy-MM-dd').format(jstDate); + } +} + diff --git a/lib/pages/gps/gps_page.dart b/lib/pages/gps/gps_page.dart new file mode 100644 index 0000000..897215e --- /dev/null +++ b/lib/pages/gps/gps_page.dart @@ -0,0 +1,159 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:get/get.dart'; +import 'package:latlong2/latlong.dart'; +import 'package:gifunavi/model/gps_data.dart'; +import 'package:gifunavi/pages/destination/destination_controller.dart'; +import 'package:gifunavi/pages/index/index_controller.dart'; +import 'package:gifunavi/utils/database_gps.dart'; +import 'package:gifunavi/widgets/base_layer_widget.dart'; + +class GpsPage extends StatefulWidget { + const GpsPage({super.key}); + + @override + State createState() => _GpsPageState(); +} + +class _GpsPageState extends State { + var gpsData = [].obs; + MapController? mapController; + StreamSubscription? subscription; + final IndexController indexController = Get.find(); + final DestinationController destinationController = + Get.find(); + + @override + void initState() { + super.initState(); + loadGpsData(); + } + + // 要検討:GPSデータの読み込みに失敗した場合のエラーハンドリングが不十分です。適切なエラーメッセージを表示するなどの処理を追加してください。 + // + void loadGpsData() async { + final teamName = indexController.currentUser[0]["user"]['team_name']; + final eventCode = indexController.currentUser[0]["user"]["event_code"]; + GpsDatabaseHelper db = GpsDatabaseHelper.instance; + var data = await db.getGPSData(teamName, eventCode); + gpsData.value = data; + + //print("--- gps data ${data} ----"); + } + + // 要検討:マーカーの形状を決定する際に、マジックナンバーが使用されています。定数を使用するなどして、コードの可読性を向上させることを検討してください。 + // + Widget getMarkerShape(GpsData i) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + InkWell( + onTap: () {}, + child: Container( + height: 22, + width: 22, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.transparent, + border: Border.all( + color: + i.is_checkin == 0 ? Colors.blueAccent : Colors.green, + width: i.is_checkin == 0 ? 0.4 : 2, + style: BorderStyle.solid)), + child: const Stack( + alignment: Alignment.center, + children: [ + Icon( + Icons.circle, + size: 6.0, + ), + ], + )), + ), + /* + Container( + color: Colors.transparent, + child: i.is_checkin == 1 + ? Text( + DateTime.fromMicrosecondsSinceEpoch(i.created_at) + .hour + .toString() + + ":" + + DateTime.fromMicrosecondsSinceEpoch(i.created_at) + .minute + .toString(), + // ":" + + // DateTime.fromMicrosecondsSinceEpoch(i.created_at) + // .second + // .toString(), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.black, + )) + : Container()), + + */ + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("movement_history".tr), + ), + body: Container( + child: Obx( + () => FlutterMap( + mapController: mapController, + options: MapOptions( + maxZoom: 18.4, + onMapReady: () {}, + //center: LatLng(37.15319600454702, 139.58765950528198), + bounds: indexController.currentBound.isNotEmpty + ? indexController.currentBound[0] + : LatLngBounds.fromPoints([ + const LatLng(35.03999881162295, 136.40587119778962), + const LatLng(36.642756778706904, 137.95226720406063) + ]), + zoom: 1, + interactiveFlags: InteractiveFlag.pinchZoom | InteractiveFlag.drag, + onPositionChanged: (MapPosition pos, bool hasGesture) { + if (hasGesture) { + indexController.currentBound = [pos.bounds!]; + } + }, + onTap: (tapPos, cord) {}, // Hide popup when the map is tapped. + ), + children: [ + const BaseLayer(), + MarkerLayer( + markers: gpsData.map((i) { + return Marker( + width: 30.0, // Fixed width + height: 30.0, // Fixed height + point: LatLng(i.lat, i.lon), + child: getMarkerShape(i), + alignment: Alignment.center); + }).toList(), + ), + // MarkerLayer( + // markers: gpsData.map((i) { + // return Marker( + // alignment: Alignment.center, + // height: 32.0, + // width: 120.0, + // point: LatLng(i.lat, i.lon), + // child: getMarkerShape(i)); + // }).toList(), + // ) + ], + ), + )), + ); + } +} diff --git a/lib/pages/history/history_page.dart b/lib/pages/history/history_page.dart index 11e6781..acc462c 100644 --- a/lib/pages/history/history_page.dart +++ b/lib/pages/history/history_page.dart @@ -1,8 +1,9 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:rogapp/model/destination.dart'; -import 'package:rogapp/utils/database_helper.dart'; +import 'package:gifunavi/model/destination.dart'; +import 'package:gifunavi/utils/database_helper.dart'; +import 'package:get/get.dart'; class HistoryPage extends StatefulWidget { const HistoryPage({super.key}); @@ -18,12 +19,14 @@ class _HistoryPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("History"), + title: Text("pass_history".tr), ), body: SingleChildScrollView( child: Column( children: [ FutureBuilder( + // 要検討:スナップショットのエラーハンドリングが行われていますが、具体的なエラーメッセージを表示するようにすることをお勧めします。 + // future: db.getDestinations(), builder: (BuildContext context, AsyncSnapshot> snapshot) { @@ -32,28 +35,48 @@ class _HistoryPageState extends State { return Center( child: Text( '${snapshot.error} occurred', - style: TextStyle(fontSize: 18), + style: const TextStyle(fontSize: 18), ), ); } else if (snapshot.hasData) { final dests = snapshot.data; - if (dests!.length > 0) { - return Center( - child: ListView.builder(itemBuilder:(ctx, index){ - return ListTile( - title: Text(dests[index].name?? ""), - subtitle: Text(dests[index].address ?? ""), - leading: dests[0].photos != null ? Image.file(File(dests[0].photos!)) : Container(), - ); - }), - ); + if (dests!.isNotEmpty) { + debugPrint("----- 通過履歴表示 -----"); + return SizedBox( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: ListView.builder( + itemCount: dests.length, + itemBuilder: (ctx, index) { + //print("--- photo ${dests[index].checkin_image!} ----"); + return Padding( + padding: const EdgeInsets.all(8.0), + child: CustomWidget( + // 要検討:画像のサイズがハードコードされています。画像のサイズを動的に設定できるようにすることを検討してください。 + title: dests[index].name!, + subtitle: + "${dests[index].sub_loc_id} : ${dests[index].name}", + image1: dests[index].checkin_image != null + ? Image.file( + File(dests[index].checkin_image!)) + : null, + image2: + dests[index].buypoint_image != null + ? Image.file(File( + dests[index].buypoint_image!)) + : null, + ), + ); + })); } else { - return Center(child: Text("No checkin yet")); + return Center(child: Text("no_checkin_yet".tr)); } } - } - else if(snapshot.connectionState == ConnectionState.waiting){ - return Center(child: CircularProgressIndicator(),); + } else if (snapshot.connectionState == + ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator(), + ); } return Container(); }), @@ -63,3 +86,69 @@ class _HistoryPageState extends State { ); } } + +class CustomWidget extends StatelessWidget { + final Image? image1; + final Image? image2; + final String title; + final String subtitle; + + const CustomWidget({ + super.key, + this.image1, + this.image2, + required this.title, + required this.subtitle, + }); + + @override + Widget build(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: + 104, // 50 (width of each image) + 2 (space between images) + 2*1 (padding on both sides) + child: Row( + children: [ + if (image1 != null) + SizedBox( + width: 50, + height: 100, + child: image1, + ), + if (image1 != null && image2 != null) const SizedBox(width: 2), + if (image2 != null) + SizedBox( + width: 50, + height: 100, + child: image2, + ), + ], + ), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: + const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + maxLines: + null, // Allows the text to wrap onto an unlimited number of lines + ), + Text( + subtitle, + style: const TextStyle(fontSize: 16), + maxLines: + null, // Allows the text to wrap onto an unlimited number of lines + ), + ], + ), + ), + ], + ); + } +} diff --git a/lib/pages/home/home_binding.dart b/lib/pages/home/home_binding.dart index 9e044a2..f32609e 100644 --- a/lib/pages/home/home_binding.dart +++ b/lib/pages/home/home_binding.dart @@ -1,7 +1,7 @@ import 'package:get/get.dart'; -import 'package:rogapp/pages/home/home_controller.dart'; +import 'package:gifunavi/pages/home/home_controller.dart'; class HomeBinding extends Bindings{ @override diff --git a/lib/pages/home/home_page.dart b/lib/pages/home/home_page.dart index 779b2ee..a3e2e10 100644 --- a/lib/pages/home/home_page.dart +++ b/lib/pages/home/home_page.dart @@ -1,44 +1,82 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:rogapp/pages/search/search_page.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:gifunavi/routes/app_pages.dart'; -class HomePage extends GetView{ - const HomePage({Key? key}) : super(key: key); +class HomePage extends StatefulWidget { + const HomePage({super.key}); + + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State { + bool _isLocationServiceEnabled = true; + + @override + void initState() { + super.initState(); + /* + WidgetsBinding.instance.addPostFrameCallback((_) { + _checkLocationService(); // 非同期的に呼び出す + }); + */ + _checkLocationService(); + } + + Future _checkLocationService() async { + final serviceEnabled = await Permission.location.serviceStatus.isEnabled; + setState(() { + _isLocationServiceEnabled = serviceEnabled; + }); + } + + void _showLocationDisabledDialog() { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('location_disabled_title'.tr), + content: Text('location_disabled_message'.tr), + actions: [ + TextButton( + child: Text('ok'.tr), + onPressed: () => Navigator.of(context).pop(), + ), + TextButton( + child: Text('open_settings'.tr), + onPressed: () { + Navigator.of(context).pop(); + openAppSettings(); + }, + ), + ], + ); + }, + ); + } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - elevation: 0, - backgroundColor: Colors.white, - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text("app_title".tr, - style: const TextStyle( - color: Colors.blue - ), - ), - InkWell( - onTap: (){ - Navigator.push(context, MaterialPageRoute(builder: (context) => SearchPage())); - }, - child: Container( - height: 32, - width: 75, - decoration: BoxDecoration( - color: Colors.blue, - borderRadius: BorderRadius.circular(25), - - ), - child: const Center(child: Icon(Icons.search),), - ), - ), - ], - ), + title: Text('home'.tr), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('welcome'.tr), + const SizedBox(height: 20), + ElevatedButton( + onPressed: _isLocationServiceEnabled + ? () => Get.offNamed(AppPages.INDEX) + : () => _showLocationDisabledDialog(), + child: Text('start_app'.tr), + ), + ], ), - body: Container(), + ), ); } - } \ No newline at end of file diff --git a/lib/pages/index/index_binding.dart b/lib/pages/index/index_binding.dart index 298fc61..3d4d69c 100644 --- a/lib/pages/index/index_binding.dart +++ b/lib/pages/index/index_binding.dart @@ -1,17 +1,14 @@ - import 'package:get/get.dart'; -import 'package:rogapp/pages/index/index_controller.dart'; +import 'package:gifunavi/pages/destination/destination_controller.dart'; +import 'package:gifunavi/pages/index/index_controller.dart'; +import 'package:gifunavi/utils/location_controller.dart'; class IndexBinding extends Bindings { - - IndexBinding(this.token); - - String? token; - @override void dependencies() { - final IndexController indexController = IndexController(); - indexController.userToken = token; - Get.put
Generated by IcoMoon