Update dependencies and improve code structure

apilevel_raise
taha 3 days ago
parent 9284d0e100
commit a6be594b3b

@ -19,6 +19,11 @@ analyzer:
unnecessary_null_checks: error unnecessary_null_checks: error
unnecessary_brace_in_string_interps: error unnecessary_brace_in_string_interps: error
unnecessary_final: error unnecessary_final: error
exclude:
- build/**
- android/**
- ios/**
- web/**
linter: linter:

@ -18,7 +18,7 @@ if (keystorePropertiesFile.exists()) {
android { android {
namespace 'hmg.cloudSolutions.mohem' namespace 'hmg.cloudSolutions.mohem'
compileSdk 36 compileSdk 37
ndkVersion '28.2.13676358' ndkVersion '28.2.13676358'
@ -34,7 +34,7 @@ android {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "hmg.cloudSolutions.mohem" applicationId "hmg.cloudSolutions.mohem"
minSdkVersion 30 minSdkVersion 30
targetSdk 35 targetSdk 37
targetSdk = flutter.targetSdkVersion targetSdk = flutter.targetSdkVersion
versionCode flutter.versionCode versionCode flutter.versionCode
versionName flutter.versionName versionName flutter.versionName
@ -60,12 +60,14 @@ android {
buildTypes { buildTypes {
debug { debug {
signingConfig signingConfigs.debug signingConfig signingConfigs.debug
minifyEnabled false
shrinkResources false
} }
release { release {
signingConfig signingConfigs.release signingConfig signingConfigs.release
minifyEnabled true minifyEnabled true
shrinkResources true shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles 'proguard-rules.pro'
} }
} }
} }

@ -43,7 +43,6 @@
android:allowBackup="false" android:allowBackup="false"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="MoheM" android:label="MoheM"
android:extractNativeLibs="true"
android:networkSecurityConfig="@xml/network_security_config" android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"> android:roundIcon="@mipmap/ic_launcher_round">
<provider <provider

@ -70,7 +70,16 @@ allprojects {
} }
} }
subprojects {
afterEvaluate { project ->
if (project.name == "huawei_push") {
project.android {
compileSdk 36
}
}
}
}
rootProject.buildDir = '../build' rootProject.buildDir = '../build'
subprojects { subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}" project.buildDir = "${rootProject.buildDir}/${project.name}"

@ -5,3 +5,7 @@ android.enableJetifier=true
apmsInstrumentationEnabled=false apmsInstrumentationEnabled=false
#org.gradle.java.home=/Users/amirs/Library/Java/JavaVirtualMachines/jbr-17.0.12/Contents/Home/ #org.gradle.java.home=/Users/amirs/Library/Java/JavaVirtualMachines/jbr-17.0.12/Contents/Home/
# This builtInKotlin flag was added automatically by Flutter migrator
android.builtInKotlin=false
# This newDsl flag was added automatically by Flutter migrator
android.newDsl=false

@ -1,6 +1,6 @@
#Tue Jul 08 11:08:34 AST 2025 #Tue Jul 08 11:08:34 AST 2025
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

@ -30,8 +30,10 @@ pluginManagement {
plugins { plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version '8.11.0' apply false // id "com.android.application" version '8.11.0' apply false
id("org.jetbrains.kotlin.android") version "2.2.0" apply false // id("org.jetbrains.kotlin.android") version "2.2.0" apply false
id "com.android.application" version '9.0.1' apply false
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
id("com.google.gms.google-services") version "4.4.3" apply false id("com.google.gms.google-services") version "4.4.3" apply false
id("com.google.firebase.crashlytics") version "3.0.4" apply false id("com.google.firebase.crashlytics") version "3.0.4" apply false
id('org.gradle.toolchains.foojay-resolver-convention') version '0.9.0' apply false id('org.gradle.toolchains.foojay-resolver-convention') version '0.9.0' apply false

@ -0,0 +1,699 @@
#!/bin/bash
# Enhanced ELF Alignment Checker for Android 16KB Page Size Compatibility
# This script checks if your app's native libraries are compatible with 16KB page size devices
# as required by Google Play starting November 1st, 2025 for apps targeting Android 15+
progname="${0##*/}"
progname="${progname%.sh}"
# Color codes and formatting - detect if colors are supported
if [[ -t 1 ]] && command -v tput >/dev/null 2>&1 && tput colors >/dev/null 2>&1 && [[ $(tput colors) -ge 8 ]]; then
RED="\033[31m"
GREEN="\033[32m"
YELLOW="\033[33m"
BLUE="\033[34m"
CYAN="\033[36m"
PURPLE="\033[35m"
BOLD="\033[1m"
DIM="\033[2m"
ENDCOLOR="\033[0m"
USE_COLORS=true
else
# No color support - use plain text
RED=""
GREEN=""
YELLOW=""
BLUE=""
CYAN=""
PURPLE=""
BOLD=""
DIM=""
ENDCOLOR=""
USE_COLORS=false
fi
# Unicode symbols with fallbacks
if [[ $USE_COLORS == true ]]; then
CHECK_MARK="✓"
CROSS_MARK="✗"
WARNING_MARK="⚠"
INFO_MARK="ℹ"
ROCKET="🚀"
GEAR="⚙"
PACKAGE="📦"
DOCUMENT="📄"
ALERT="🚨"
PARTY="🎉"
TOOLS="🔧"
BOOKS="📚"
ARROW="▶"
else
CHECK_MARK="[OK]"
CROSS_MARK="[X]"
WARNING_MARK="[!]"
INFO_MARK="[i]"
ROCKET="[>]"
GEAR="[*]"
PACKAGE="[P]"
DOCUMENT="[D]"
ALERT="[!]"
PARTY="[*]"
TOOLS="[T]"
BOOKS="[B]"
ARROW=">"
fi
# Enhanced formatting functions
print_banner() {
local title="$1"
local subtitle="$2"
local width=88
echo
printf "%*s\n" $width | tr ' ' '═'
# Main title
local title_len=${#title}
local title_padding=$(( (width - title_len - 4) / 2 ))
printf "║${BOLD}${BLUE}%*s %s %*s${ENDCOLOR}║\n" \
$title_padding "" "$title" $title_padding ""
# Subtitle if provided
if [[ -n "$subtitle" ]]; then
local subtitle_len=${#subtitle}
local subtitle_padding=$(( (width - subtitle_len - 4) / 2 ))
printf "║${DIM}%*s %s %*s${ENDCOLOR}║\n" \
$subtitle_padding "" "$subtitle" $subtitle_padding ""
fi
printf "%*s\n" $width | tr ' ' '═'
echo
}
print_section() {
local icon="$1"
local title="$2"
local description="$3"
echo
echo -e "${BOLD}${BLUE}$icon $title${ENDCOLOR}"
if [[ -n "$description" ]]; then
echo -e "${DIM}$description${ENDCOLOR}"
fi
printf "${BLUE}%*s${ENDCOLOR}\n" 80 | tr ' ' '─'
}
print_status() {
local status="$1"
local message="$2"
local detail="$3"
case $status in
"success")
printf " ${GREEN}${CHECK_MARK}${ENDCOLOR} %s" "$message"
;;
"error")
printf " ${RED}${CROSS_MARK}${ENDCOLOR} %s" "$message"
;;
"warning")
printf " ${YELLOW}${WARNING_MARK}${ENDCOLOR} %s" "$message"
;;
"info")
printf " ${CYAN}${INFO_MARK}${ENDCOLOR} %s" "$message"
;;
"processing")
printf " ${PURPLE}${GEAR}${ENDCOLOR} %s" "$message"
;;
esac
if [[ -n "$detail" ]]; then
printf "\n ${DIM}%s${ENDCOLOR}" "$detail"
fi
echo
}
print_subsection() {
local title="$1"
echo
echo -e " ${BOLD}${PURPLE}${ARROW} $title${ENDCOLOR}"
printf " ${PURPLE}%*s${ENDCOLOR}\n" 50 | tr ' ' '┈'
}
print_table_header() {
echo
local line1="┌$(printf '─%.0s' {1..29})┬$(printf '─%.0s' {1..18})┬$(printf '─%.0s' {1..15})┬$(printf '─%.0s' {1..13})┐"
local line2="│ $(printf '%-27s' 'Library') │ $(printf '%-16s' 'Architecture') │ $(printf '%-13s' 'Alignment') │ $(printf '%-11s' 'Status') │"
local line3="├$(printf '─%.0s' {1..29})┼$(printf '─%.0s' {1..18})┼$(printf '─%.0s' {1..15})┼$(printf '─%.0s' {1..13})┤"
echo -e "${CYAN}$line1${ENDCOLOR}"
echo -e "${BOLD}${CYAN}$line2${ENDCOLOR}"
echo -e "${CYAN}$line3${ENDCOLOR}"
}
print_table_row() {
local lib="$1"
local arch="$2"
local alignment="$3"
local status="$4"
local is_critical="$5"
local status_symbol
local status_color
local status_text
if [[ $status == "ALIGNED" ]]; then
status_symbol="$CHECK_MARK"
status_color="$GREEN"
status_text="PASS"
else
status_symbol="$CROSS_MARK"
if [[ $is_critical == "true" ]]; then
status_color="$RED"
status_text="FAIL"
else
status_color="$YELLOW"
status_text="WARN"
fi
fi
# Truncate library name if too long
local display_lib="$lib"
if [[ ${#lib} -gt 27 ]]; then
display_lib="${lib:0:24}..."
fi
printf "${CYAN}│${ENDCOLOR} %-27s ${CYAN}│${ENDCOLOR} %-16s ${CYAN}│${ENDCOLOR} %-13s ${CYAN}│${ENDCOLOR} ${status_color}%s %-7s${ENDCOLOR} ${CYAN}│${ENDCOLOR}\n" \
"$display_lib" "$arch" "$alignment" "$status_symbol" "$status_text"
if [[ $is_critical == "true" && $status == "UNALIGNED" ]]; then
printf "${CYAN}│${ENDCOLOR} ${RED}%-75s${ENDCOLOR} ${CYAN}│${ENDCOLOR}\n" " ${ALERT} CRITICAL: Required for Google Play compliance!"
fi
}
print_table_footer() {
local line="└$(printf '─%.0s' {1..29})┴$(printf '─%.0s' {1..18})┴$(printf '─%.0s' {1..15})┴$(printf '─%.0s' {1..13})┘"
echo -e "${CYAN}$line${ENDCOLOR}"
echo
}
print_summary_box() {
local title="$1"
local status="$2" # success, warning, error
shift 2
local lines=("$@")
local box_color
local title_color
case $status in
"success") box_color="$GREEN"; title_color="$GREEN" ;;
"warning") box_color="$YELLOW"; title_color="$YELLOW" ;;
"error") box_color="$RED"; title_color="$RED" ;;
*) box_color="$BLUE"; title_color="$BLUE" ;;
esac
local width=78
echo
printf "${box_color}┌%*s┐${ENDCOLOR}\n" $((width-2)) | tr ' ' '─'
# Title
local title_len=${#title}
local title_padding=$(( (width - title_len - 4) / 2 ))
printf "${box_color}│${ENDCOLOR}${BOLD}${title_color}%*s %s %*s${ENDCOLOR}${box_color}│${ENDCOLOR}\n" \
$title_padding "" "$title" $title_padding ""
printf "${box_color}├%*s┤${ENDCOLOR}\n" $((width-2)) | tr ' ' '─'
# Content lines
for line in "${lines[@]}"; do
printf "${box_color}│${ENDCOLOR} %-*s ${box_color}│${ENDCOLOR}\n" $((width-4)) "$line"
done
printf "${box_color}└%*s┘${ENDCOLOR}\n" $((width-2)) | tr ' ' '─'
echo
}
cleanup_trap() {
if [ -n "${tmp}" -a -d "${tmp}" ]; then
print_status "processing" "Cleaning up temporary files..."
rm -rf "${tmp}"
fi
exit $1
}
usage() {
print_banner "Android 16KB Page Size Compatibility Checker" "Google Play Compliance Tool"
echo -e "${BOLD}DESCRIPTION:${ENDCOLOR}"
echo " This tool verifies that your Android app's native libraries are compatible"
echo " with 16KB page size devices, as required by Google Play starting November 1st, 2025."
echo
echo -e "${BOLD}USAGE:${ENDCOLOR}"
echo -e " ${GREEN}$progname${ENDCOLOR} ${CYAN}[input-path|input-APK|input-APEX]${ENDCOLOR}"
echo
echo -e "${BOLD}EXAMPLES:${ENDCOLOR}"
echo -e " ${DIM}# Check an APK file${ENDCOLOR}"
echo -e " $progname ${CYAN}app/build/outputs/apk/release/app-release.apk${ENDCOLOR}"
echo
echo -e " ${DIM}# Check a directory of native libraries${ENDCOLOR}"
echo -e " $progname ${CYAN}/path/to/native/libs/${ENDCOLOR}"
echo
echo -e "${BOLD}WHAT THIS TOOL CHECKS:${ENDCOLOR}"
echo -e " ${CHECK_MARK} APK zip-alignment for 16KB boundaries (requires build-tools 35.0.0+)"
echo -e " ${CHECK_MARK} ELF segment alignment in native libraries (arm64-v8a and x86_64)"
echo -e " ${CHECK_MARK} Compliance with Android 16KB page size requirements"
echo
echo -e "${BOLD}RESULT MEANINGS:${ENDCOLOR}"
echo -e " ${GREEN}${CHECK_MARK} PASS${ENDCOLOR} - Library is compatible with 16KB page sizes (2**14 or higher)"
echo -e " ${RED}${CROSS_MARK} FAIL${ENDCOLOR} - Library needs recompilation with 16KB ELF alignment"
echo -e " ${YELLOW}${WARNING_MARK} WARN${ENDCOLOR} - Non-critical architecture but should be fixed"
echo
}
# Enhanced dependency checking
check_dependencies() {
print_section "$GEAR" "Dependency Check" "Verifying required tools are available"
local missing_tools=()
local available_tools=()
# Check each tool
local tools=("objdump" "unzip" "file")
for tool in "${tools[@]}"; do
if command -v "$tool" >/dev/null 2>&1; then
available_tools+=("$tool")
print_status "success" "$tool" "$(which "$tool")"
else
missing_tools+=("$tool")
print_status "error" "$tool not found"
fi
done
if [ ${#missing_tools[@]} -gt 0 ]; then
echo
print_subsection "Installation Instructions"
for tool in "${missing_tools[@]}"; do
case $tool in
objdump)
print_status "info" "Install objdump:" "macOS: Xcode Command Line Tools or Android NDK's llvm-objdump"
print_status "info" "" "Linux: sudo apt-get install binutils (Ubuntu/Debian)"
;;
unzip)
print_status "info" "Install unzip:" "Usually pre-installed on most systems"
print_status "info" "" "Linux: sudo apt-get install unzip"
;;
file)
print_status "info" "Install file:" "Usually pre-installed on most systems"
print_status "info" "" "Linux: sudo apt-get install file"
;;
esac
echo
done
exit 1
fi
print_status "success" "All dependencies satisfied" "${#available_tools[@]} tools available"
}
# Validate input arguments
if [ ${#} -ne 1 ]; then
usage
exit 1
fi
case ${1} in
--help | -h | -\?)
usage
exit 0
;;
*)
dir="${1}"
;;
esac
# Validate input file/directory
if ! [ -f "${dir}" -o -d "${dir}" ]; then
print_status "error" "Invalid input: ${dir}"
echo " Please provide a valid APK file, APEX file, or directory containing native libraries."
exit 1
fi
# Check dependencies before proceeding
check_dependencies
print_banner "ANALYSIS IN PROGRESS" "Checking 16KB Page Size Compatibility"
print_status "processing" "Target: $(basename "${dir}")"
print_status "info" "Compliance Deadline: November 1st, 2025"
print_status "info" "Requirement: Apps targeting Android 15+ must support 16KB pages"
# APK Processing
if [[ "${dir}" == *.apk ]]; then
trap 'cleanup_trap' EXIT
print_section "$PACKAGE" "APK Analysis" "Processing Android Package file"
# Enhanced zipalign check
if command -v zipalign >/dev/null 2>&1; then
if { zipalign --help 2>&1 | grep -q "\-P <pagesize_kb>"; }; then
print_status "processing" "Checking APK zip-alignment for 16KB boundaries..."
zip_result=$(zipalign -v -c -P 16 4 "${dir}" 2>&1)
zip_exit_code=$?
if [ $zip_exit_code -eq 0 ]; then
print_status "success" "APK zip-alignment verification passed"
else
print_status "error" "APK zip-alignment verification failed"
echo " ${DIM}Details:${ENDCOLOR}"
echo "$zip_result" | grep -E 'lib/arm64-v8a|lib/x86_64|Verification|would be' | sed 's/^/ /' || echo " $zip_result"
fi
else
print_status "warning" "zipalign version doesn't support 16KB alignment checks"
print_status "info" "Solution: Update to Android SDK build-tools 35.0.0+"
echo " ${DIM}Update via Android Studio → SDK Manager → SDK Tools${ENDCOLOR}"
echo " ${DIM}Or run: sdkmanager \"build-tools;35.0.0\"${ENDCOLOR}"
fi
else
print_status "warning" "zipalign not found in PATH"
print_status "info" "Ensure Android SDK build-tools are installed and in PATH"
fi
# Extract APK
dir_filename=$(basename "${dir}")
tmp=$(mktemp -d -t "${dir_filename%.apk}_out_XXXXX")
if [ ! -d "${tmp}" ]; then
print_status "error" "Failed to create temporary directory"
exit 1
fi
print_status "processing" "Extracting native libraries from APK..."
if ! unzip -q "${dir}" "lib/*" -d "${tmp}" 2>/dev/null; then
print_summary_box "NO NATIVE LIBRARIES FOUND" "success" \
"${PARTY} Your app contains only Java/Kotlin code!" \
"" \
"${CHECK_MARK} Apps without native libraries automatically support 16KB devices" \
"${CHECK_MARK} No additional changes required for Google Play compliance" \
"${CHECK_MARK} You're all set for the November 1st, 2025 deadline!"
cleanup_trap 0
fi
dir="${tmp}"
fi
# APEX Processing
if [[ "${dir}" == *.apex ]]; then
trap 'cleanup_trap' EXIT
print_section "$DOCUMENT" "APEX Analysis" "Processing Android Pony EXpress file"
if ! command -v deapexer >/dev/null 2>&1; then
print_status "error" "deapexer tool not found"
echo " Please ensure Android SDK tools are properly installed and in your PATH."
exit 1
fi
dir_filename=$(basename "${dir}")
tmp=$(mktemp -d -t "${dir_filename%.apex}_out_XXXXX")
if [ ! -d "${tmp}" ]; then
print_status "error" "Failed to create temporary directory"
exit 1
fi
print_status "processing" "Extracting APEX contents..."
if ! deapexer extract "${dir}" "${tmp}"; then
print_status "error" "Failed to extract APEX file"
cleanup_trap 1
fi
dir="${tmp}"
fi
# Track libraries for enhanced summary
unaligned_libs=()
aligned_libs=()
critical_unaligned_libs=()
non_critical_unaligned_libs=()
total_libs=0
critical_libs=0
print_section "$GEAR" "ELF Segment Analysis" "Scanning native libraries for 16KB alignment compliance"
# Find all native libraries
matches="$(find "${dir}" -type f -name '*.so' 2>/dev/null)"
if [ -z "$matches" ]; then
# Also check for ELF files without .so extension
matches="$(find "${dir}" -type f -exec file {} \; 2>/dev/null | grep 'ELF' | cut -d: -f1)"
fi
if [ -z "$matches" ]; then
print_summary_box "NO NATIVE LIBRARIES DETECTED" "success" \
"${PARTY} Your app uses only Java/Kotlin code!" \
"" \
"${CHECK_MARK} No native library alignment issues to worry about" \
"${CHECK_MARK} Already compatible with 16KB page size devices" \
"${CHECK_MARK} Ready for Google Play's November 1st, 2025 requirement!"
cleanup_trap 0
fi
print_status "info" "Found native libraries - analyzing ELF segment alignment..."
print_table_header
IFS=$'\n'
for match in $matches; do
# Skip non-ELF files and nested packages
if [[ "${match}" == *".apk" ]]; then
continue
fi
if [[ "${match}" == *".apex" ]]; then
continue
fi
# Verify it's actually an ELF file
if ! [[ $(file "${match}" 2>/dev/null) == *"ELF"* ]]; then
continue
fi
total_libs=$((total_libs + 1))
# Determine architecture and criticality
arch="unknown"
is_critical="false"
if [[ "${match}" == *"arm64-v8a"* ]]; then
arch="arm64-v8a"
is_critical="true"
critical_libs=$((critical_libs + 1))
elif [[ "${match}" == *"x86_64"* ]]; then
arch="x86_64"
is_critical="true"
critical_libs=$((critical_libs + 1))
elif [[ "${match}" == *"armeabi-v7a"* ]]; then
arch="armeabi-v7a"
elif [[ "${match}" == *"x86"* ]]; then
arch="x86"
fi
# Check ELF segment alignment
res="$(objdump -p "${match}" 2>/dev/null | grep LOAD | awk '{ print $NF }' | head -1)"
if [ -z "$res" ]; then
print_table_row "$(basename "${match}")" "$arch" "UNKNOWN" "FAILED" "$is_critical"
continue
fi
# Check if alignment meets 16KB requirement (2**14 or higher)
if [[ $res =~ 2\*\*(1[4-9]|[2-9][0-9]|[1-9][0-9]{2,}) ]]; then
print_table_row "$(basename "${match}")" "$arch" "$res" "ALIGNED" "$is_critical"
aligned_libs+=("${match}")
else
print_table_row "$(basename "${match}")" "$arch" "$res" "UNALIGNED" "$is_critical"
unaligned_libs+=("${match}")
if [[ "$is_critical" == "true" ]]; then
critical_unaligned_libs+=("${match}")
else
non_critical_unaligned_libs+=("${match}")
fi
fi
done
print_table_footer
# Enhanced Summary Section
if [ ${#unaligned_libs[@]} -gt 0 ]; then
# Failure case - unaligned libraries found
summary_lines=(
"${ALERT} ACTION REQUIRED: Unaligned native libraries detected!"
""
"Analysis Results:"
" • Total libraries scanned: $total_libs"
" • Critical architectures (64-bit): $critical_libs"
" • Libraries aligned: ${#aligned_libs[@]}"
" • Libraries UNALIGNED: ${#unaligned_libs[@]}"
)
if [ ${#critical_unaligned_libs[@]} -gt 0 ]; then
summary_lines+=(" • Critical failures: ${#critical_unaligned_libs[@]} (MUST FIX)")
fi
if [ ${#non_critical_unaligned_libs[@]} -gt 0 ]; then
summary_lines+=(" • Non-critical warnings: ${#non_critical_unaligned_libs[@]} (SHOULD FIX)")
fi
summary_lines+=("")
summary_lines+=("Google Play Compliance: FAILED")
summary_lines+=("Deadline: November 1st, 2025")
print_summary_box "COMPATIBILITY CHECK FAILED" "error" "${summary_lines[@]}"
# Detailed library breakdown
if [ ${#critical_unaligned_libs[@]} -gt 0 ]; then
print_subsection "Critical Libraries Requiring Immediate Attention"
for lib in "${critical_unaligned_libs[@]}"; do
arch_type="64-bit"
[[ "${lib}" == *"arm64-v8a"* ]] && arch_type="ARM64"
[[ "${lib}" == *"x86_64"* ]] && arch_type="x86_64"
print_status "error" "$(basename "$lib")" "$arch_type architecture - Required for Google Play"
done
fi
if [ ${#non_critical_unaligned_libs[@]} -gt 0 ]; then
print_subsection "Non-Critical Libraries (Recommended to Fix)"
for lib in "${non_critical_unaligned_libs[@]}"; do
arch_type="32-bit"
[[ "${lib}" == *"armeabi-v7a"* ]] && arch_type="ARMv7"
[[ "${lib}" == *"x86"* ]] && arch_type="x86"
print_status "warning" "$(basename "$lib")" "$arch_type architecture"
done
fi
# Comprehensive fix instructions
print_section "$TOOLS" "How to Fix Unaligned Libraries" "Step-by-step guide to achieve 16KB compatibility"
print_subsection "Step 1: Update Your Build Environment"
print_status "info" "Android Gradle Plugin (AGP)" "Upgrade to version 8.5.1 or higher"
print_status "info" "Android NDK" "Update to NDK r27 or higher (r28+ recommended)"
print_status "info" "Build Tools" "Ensure Android SDK build-tools 35.0.0+ for zipalign"
print_subsection "Step 2: Configure 16KB ELF Alignment"
echo
echo -e "${BOLD}For NDK r28 and newer:${ENDCOLOR}"
print_status "success" "Automatic Support" "16KB alignment enabled by default - no changes needed!"
echo
echo -e "${BOLD}For NDK r27:${ENDCOLOR}"
print_status "info" "Gradle Configuration" "Add to your app/build.gradle:"
echo -e "${DIM} android {"
echo -e " defaultConfig {"
echo -e " externalNativeBuild {"
echo -e " cmake {"
echo -e " arguments '-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON'"
echo -e " }"
echo -e " }"
echo -e " }"
echo -e " }${ENDCOLOR}"
print_status "info" "NDK-Build Configuration" "Add to Application.mk:"
echo -e "${DIM} APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true${ENDCOLOR}"
echo
echo -e "${BOLD}For NDK r26 and older:${ENDCOLOR}"
print_status "info" "Manual Linker Flags" "Add to your native build configuration:"
echo -e "${DIM} # For Android.mk:"
echo -e " LOCAL_LDFLAGS += \"-Wl,-z,max-page-size=16384\""
echo
echo -e " # For CMakeLists.txt:"
echo -e " target_link_options(\${CMAKE_PROJECT_NAME} PRIVATE \"-Wl,-z,max-page-size=16384\")${ENDCOLOR}"
print_subsection "Step 3: Update Library Packaging"
print_status "info" "AGP 8.5.1+" "Uncompressed libraries used by default (no changes needed)"
print_status "info" "Older AGP versions" "Add to app/build.gradle:"
echo -e "${DIM} android {"
echo -e " packagingOptions {"
echo -e " jniLibs {"
echo -e " useLegacyPackaging = true"
echo -e " }"
echo -e " }"
echo -e " }${ENDCOLOR}"
print_subsection "Step 4: Build and Verify"
print_status "processing" "Clean Build" "Run ./gradlew clean"
print_status "processing" "Rebuild Project" "Run ./gradlew assembleRelease"
print_status "processing" "Re-run This Script" "Verify all libraries are now aligned"
print_status "processing" "Test on Device" "Use Android 15 emulator with 16KB system image"
print_section "$BOOKS" "Additional Resources" "Learn more about 16KB page size support"
print_status "info" "Official Guide" "https://developer.android.com/guide/practices/page-sizes"
print_status "info" "NDK Documentation" "https://developer.android.com/ndk/guides/"
print_status "info" "Testing Guide" "Use 'adb shell getconf PAGE_SIZE' (should return 16384)"
print_status "info" "Emulator Setup" "Android 15 system images with 16KB page size"
final_message=""
if [ ${#critical_unaligned_libs[@]} -gt 0 ]; then
final_message="CRITICAL: Fix required for Google Play compliance by November 1st, 2025!"
else
final_message="Warning: Non-critical issues found - recommended to fix for complete compatibility"
fi
print_summary_box "NEXT STEPS" "error" \
"$final_message" \
"" \
"1. Update your build tools and NDK version" \
"2. Apply the configuration changes above" \
"3. Clean and rebuild your project" \
"4. Run this script again to verify fixes" \
"5. Test thoroughly on 16KB devices/emulator"
cleanup_trap 1
else
# Success case - all libraries aligned
summary_lines=(
"${PARTY} All native libraries are properly aligned!"
""
"Analysis Results:"
" • Total libraries scanned: $total_libs"
" • Critical architectures: $critical_libs"
" • All libraries: ${#aligned_libs[@]}/${total_libs} ALIGNED"
""
"Google Play Compliance: PASSED ${CHECK_MARK}"
"Ready for November 1st, 2025 deadline!"
)
print_summary_box "COMPATIBILITY CHECK PASSED" "success" "${summary_lines[@]}"
print_section "$ROCKET" "Next Steps" "Ensure complete 16KB compatibility"
print_status "success" "Library Alignment" "All ELF segments properly aligned for 16KB pages"
print_status "info" "Runtime Testing" "Test on Android 15 emulator with 16KB system image"
print_status "info" "Code Review" "Check for hardcoded PAGE_SIZE dependencies in your code"
print_status "info" "Third-party SDKs" "Verify all dependencies are 16KB compatible"
print_status "info" "Zipalign Verification" "Run: zipalign -c -P 16 -v 4 your-app.apk"
print_subsection "Testing Commands"
echo -e "${DIM} # Verify page size on device/emulator${ENDCOLOR}"
echo -e "${DIM} adb shell getconf PAGE_SIZE # Should return: 16384${ENDCOLOR}"
echo
echo -e "${DIM} # Verify APK alignment${ENDCOLOR}"
echo -e "${DIM} zipalign -c -P 16 -v 4 app-release.apk${ENDCOLOR}"
print_summary_box "CONGRATULATIONS!" "success" \
"${PARTY} Your app is 16KB page size compatible!" \
"" \
"${CHECK_MARK} All native libraries meet Google Play requirements" \
"${CHECK_MARK} Ready for devices with 16KB page sizes" \
"${CHECK_MARK} Compliant with November 1st, 2025 deadline" \
"" \
"Recommendation: Test thoroughly on 16KB environment to ensure" \
"no runtime issues exist in your application code."
fi
echo
print_status "info" "Analysis completed at $(date)"
echo

@ -20,7 +20,5 @@
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0</string>
<key>MinimumOSVersion</key>
<string>12.0</string>
</dict> </dict>
</plist> </plist>

@ -3,7 +3,7 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 54; objectVersion = 60;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
@ -12,6 +12,7 @@
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
76AE113C28E078EA00513112 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 76AE113B28E078EA00513112 /* GoogleService-Info.plist */; }; 76AE113C28E078EA00513112 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 76AE113B28E078EA00513112 /* GoogleService-Info.plist */; };
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
@ -39,6 +40,7 @@
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7627313328E45A2400F354F7 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; }; 7627313328E45A2400F354F7 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
76AE113B28E078EA00513112 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; }; 76AE113B28E078EA00513112 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
@ -57,6 +59,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
1CCF895CFB3C95DEBB84A05F /* Pods_Runner.framework in Frameworks */, 1CCF895CFB3C95DEBB84A05F /* Pods_Runner.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -75,6 +78,7 @@
9740EEB11CF90186004384FC /* Flutter */ = { 9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */, 9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
@ -144,13 +148,16 @@
9705A1C41CF9048500538489 /* Embed Frameworks */, 9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
C4CFBC4C5CAC00182015ACD5 /* [CP] Embed Pods Frameworks */, C4CFBC4C5CAC00182015ACD5 /* [CP] Embed Pods Frameworks */,
EF29CE25BB57F5AA48CE1136 /* [CP] Copy Pods Resources */, D9FFC0F21D4146458615A992 /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
); );
name = Runner; name = Runner;
packageProductDependencies = (
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
);
productName = Runner; productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
@ -179,6 +186,9 @@
Base, Base,
); );
mainGroup = 97C146E51CF9000F007C117D; mainGroup = 97C146E51CF9000F007C117D;
packageReferences = (
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
);
productRefGroup = 97C146EF1CF9000F007C117D /* Products */; productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
@ -242,23 +252,6 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
}; };
6C6E76E4FB19FAF11C24005D /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = { 9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1; alwaysOutOfDate = 1;
@ -291,6 +284,23 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
D9FFC0F21D4146458615A992 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
@ -367,7 +377,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@ -454,7 +464,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -504,7 +514,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@ -600,6 +610,20 @@
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCLocalSwiftPackageReference section */
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
};
/* End XCLocalSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
isa = XCSwiftPackageProductDependency;
productName = FlutterGeneratedPluginSwiftPackage;
};
/* End XCSwiftPackageProductDependency section */
}; };
rootObject = 97C146E61CF9000F007C117D /* Project object */; rootObject = 97C146E61CF9000F007C117D /* Project object */;
} }

@ -5,6 +5,24 @@
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"> buildImplicitDependencies = "YES">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Prepare Flutter Framework Script"
scriptText = "/bin/sh &quot;$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh&quot; prepare&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
<BuildActionEntries> <BuildActionEntries>
<BuildActionEntry <BuildActionEntry
buildForTesting = "YES" buildForTesting = "YES"

@ -3,9 +3,8 @@ import Flutter
import Firebase import Firebase
import flutter_local_notifications import flutter_local_notifications
@main @main
@objc class AppDelegate: FlutterAppDelegate { @objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
override func application( override func application(
_ application: UIApplication, _ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
@ -17,7 +16,11 @@ import flutter_local_notifications
if #available(iOS 10.0, *) { if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
} }
GeneratedPluginRegistrant.register(with: self) // GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions) return super.application(application, didFinishLaunchingWithOptions: launchOptions)
} }
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
}
} }

@ -2,6 +2,27 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneDelegateClassName</key>
<string>FlutterSceneDelegate</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>CADisableMinimumFrameDurationOnPhone</key> <key>CADisableMinimumFrameDurationOnPhone</key>
<true/> <true/>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
@ -57,10 +78,10 @@
<string>This App requires access to your location to mark your attendance.</string> <string>This App requires access to your location to mark your attendance.</string>
<key>NSMicrophoneUsageDescription</key> <key>NSMicrophoneUsageDescription</key>
<string>This app requires microphone access for calls and speech-to-text input.</string> <string>This app requires microphone access for calls and speech-to-text input.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>This app requires speech recognition to convert your voice to text.</string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>
<string>This app requires photo library access to select image as document &amp; upload it.</string> <string>This app requires photo library access to select image as document &amp; upload it.</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>This app requires speech recognition to convert your voice to text.</string>
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
<key>UIBackgroundModes</key> <key>UIBackgroundModes</key>

@ -5,7 +5,6 @@ import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:mobile_device_identifier/mobile_device_identifier.dart';
import 'package:mohem_flutter_app/api/api_client.dart'; import 'package:mohem_flutter_app/api/api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/consts.dart';
@ -254,7 +253,7 @@ class DashboardApiClient {
String QRValue = "", String QRValue = "",
String payrollCode = "", String payrollCode = "",
}) async { }) async {
var _mobileDeviceIdentifierPlugin = MobileDeviceIdentifier();
Codec<String, String> stringToBase64 = utf8.fuse(base64); Codec<String, String> stringToBase64 = utf8.fuse(base64);
String url = "${ApiConsts.swpRest}AuthenticateAndSwipeUserSupportNFC"; String url = "${ApiConsts.swpRest}AuthenticateAndSwipeUserSupportNFC";
var uuid = Uuid(); var uuid = Uuid();
@ -262,7 +261,7 @@ class DashboardApiClient {
// Generate a v4 (random) id // Generate a v4 (random) id
if (Platform.isAndroid) { if (Platform.isAndroid) {
AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo; AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo;
deviceId = await _mobileDeviceIdentifierPlugin.getDeviceId() ?? androidInfo.id; deviceId = await androidInfo.id;
deviceId = stringToBase64.encode(deviceId); deviceId = stringToBase64.encode(deviceId);
deviceId = deviceId.length >= 50 ? deviceId.substring(0, 48) : deviceId; deviceId = deviceId.length >= 50 ? deviceId.substring(0, 48) : deviceId;
} else if (Platform.isIOS) { } else if (Platform.isIOS) {

@ -22,7 +22,7 @@ class DateUtil {
return DateTime.now(); return DateTime.now();
} }
static DateTime convertStringToDate(String date) { static DateTime convertStringToDate(String? date) {
// /Date(1585774800000+0300)/ // /Date(1585774800000+0300)/
if (date != null) { if (date != null) {
const start = "/Date("; const start = "/Date(";
@ -34,8 +34,9 @@ class DateUtil {
date.substring(startIndex + start.length, endIndex), date.substring(startIndex + start.length, endIndex),
), ),
); );
} else } else {
return DateTime.now(); return DateTime.now();
}
} }
static DateTime convertSimpleStringDateToDate(String date, {bool isITG = false}) { static DateTime convertSimpleStringDateToDate(String date, {bool isITG = false}) {

@ -20,7 +20,9 @@ import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart'; import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart';
import 'package:mohem_flutter_app/widgets/loading_dialog.dart'; import 'package:mohem_flutter_app/widgets/loading_dialog.dart';
import 'package:nfc_manager/nfc_manager.dart'; import 'package:nfc_manager/nfc_manager.dart';
import 'package:nfc_manager/platform_tags.dart'; import 'package:nfc_manager/nfc_manager_android.dart';
import 'package:nfc_manager/nfc_manager_ios.dart';
// import 'package:nfc_manager/platform_tags.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
// ignore_for_file: avoid_annotating_with_dynamic // ignore_for_file: avoid_annotating_with_dynamic
@ -349,15 +351,34 @@ class Utils {
NfcManager.instance NfcManager.instance
.startSession( .startSession(
onDiscovered: (NfcTag tag) async { onDiscovered: (NfcTag tag) async {
MifareUltralight f; // MifareUltralight f;
// if (Platform.isAndroid) {
// print(tag);
// f = MifareUltralight(tag: tag, identifier: tag.data["nfca"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22);
// } else {
// f = MifareUltralight(tag: tag, identifier: tag.data["mifare"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22);
// }
Uint8List? id;
if (Platform.isAndroid) { if (Platform.isAndroid) {
print(tag); id = NfcTagAndroid.from(tag)?.id;
f = MifareUltralight(tag: tag, identifier: tag.data["nfca"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22); } else if (Platform.isIOS) {
} else { id = MiFareIos.from(tag)?.identifier;
f = MifareUltralight(tag: tag, identifier: tag.data["mifare"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22); }
if (id == null) {
await NfcManager.instance.stopSession();
return;
} }
String identifier = f.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
NfcManager.instance.stopSession(); String identifier =
id.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
print(identifier);
await NfcManager.instance.stopSession();
// String identifier = f.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
// NfcManager.instance.stopSession();
onRead(identifier); onRead(identifier);
}, },
pollingOptions: {NfcPollingOption.iso14443}, pollingOptions: {NfcPollingOption.iso14443},

@ -8,7 +8,6 @@
import 'package:audio_session/audio_session_web.dart'; import 'package:audio_session/audio_session_web.dart';
import 'package:camera_web/camera_web.dart'; import 'package:camera_web/camera_web.dart';
import 'package:file_picker/_internal/file_picker_web.dart';
import 'package:firebase_core_web/firebase_core_web.dart'; import 'package:firebase_core_web/firebase_core_web.dart';
import 'package:firebase_messaging_web/firebase_messaging_web.dart'; import 'package:firebase_messaging_web/firebase_messaging_web.dart';
import 'package:fluttertoast/fluttertoast_web.dart'; import 'package:fluttertoast/fluttertoast_web.dart';
@ -27,7 +26,6 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart';
void registerPlugins(Registrar registrar) { void registerPlugins(Registrar registrar) {
AudioSessionWeb.registerWith(registrar); AudioSessionWeb.registerWith(registrar);
CameraPlugin.registerWith(registrar); CameraPlugin.registerWith(registrar);
FilePickerWeb.registerWith(registrar);
FirebaseCoreWeb.registerWith(registrar); FirebaseCoreWeb.registerWith(registrar);
FirebaseMessagingWeb.registerWith(registrar); FirebaseMessagingWeb.registerWith(registrar);
FluttertoastWebPlugin.registerWith(registrar); FluttertoastWebPlugin.registerWith(registrar);

@ -18,6 +18,8 @@ import 'package:month_year_picker/month_year_picker.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart'; import 'package:provider/single_child_widget.dart';
import 'package:sizer/sizer.dart'; import 'package:sizer/sizer.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb;
// test uat account // test uat account
// username 199067 // username 199067
@ -75,32 +77,33 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Sizer( return Sizer(
builder: (BuildContext context, Orientation orientation, DeviceType deviceType) { builder: (BuildContext context, Orientation orientation, ScreenType deviceType){
PostParamsModel? obj = AppState().postParamsObject; PostParamsModel? obj = AppState().postParamsObject;
obj?.languageID = EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2; obj?.languageID = EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2;
AppState().setPostParamsModel(obj!); AppState().setPostParamsModel(obj!);
List<LocalizationsDelegate<dynamic>> delegates = context.localizationDelegates; List<LocalizationsDelegate<dynamic>> delegates = context.localizationDelegates;
// delegates.add(GlobalMaterialLocalizations.delegate); // delegates.add(GlobalMaterialLocalizations.delegate);
delegates.add(MonthYearPickerLocalizations.delegate); delegates.add(MonthYearPickerLocalizations.delegate);
return SafeArea( return SafeArea(
top: false, // Set to true if you want to avoid the notch area as well top: false, // Set to true if you want to avoid the notch area as well
bottom: Platform.isIOS ? false : true, bottom: Platform.isIOS ? false : true,
child: MaterialApp( child: MaterialApp(
navigatorKey: AppRoutes.navigatorKey, navigatorKey: AppRoutes.navigatorKey,
builder: (BuildContext context, Widget? child) { builder: (BuildContext context, Widget? child) {
return MediaQuery(data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), child: child!); return MediaQuery(data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), child: child!);
}, },
theme: AppTheme.getTheme(EasyLocalization.of(context)?.locale.languageCode == "ar"), theme: AppTheme.getTheme(EasyLocalization.of(context)?.locale.languageCode == "ar"),
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
localizationsDelegates: delegates, localizationsDelegates: delegates,
supportedLocales: context.supportedLocales, supportedLocales: context.supportedLocales,
locale: context.locale, locale: context.locale,
initialRoute: AppRoutes.initialRoute, initialRoute: AppRoutes.initialRoute,
routes: AppRoutes.routes, routes: AppRoutes.routes,
), ),
); );
}, }
); );
} }
} }

@ -1609,13 +1609,13 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
await File('$dirPath/.nomedia').create(); await File('$dirPath/.nomedia').create();
} }
path = "$dirPath/${AppState().chatDetails!.response!.id}-$receiverID-${DateTime.now().microsecondsSinceEpoch}.aac"; path = "$dirPath/${AppState().chatDetails!.response!.id}-$receiverID-${DateTime.now().microsecondsSinceEpoch}.aac";
recorderController = RecorderController() recorderController = RecorderController();
..androidEncoder = AndroidEncoder.aac // ..androidEncoder = AndroidEncoder.aac
..androidOutputFormat = AndroidOutputFormat.mpeg4 // ..androidOutputFormat = AndroidOutputFormat.mpeg4
..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC // ..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC
..sampleRate = 6000 // ..sampleRate = 6000
..updateFrequency = const Duration(milliseconds: 100) // ..updateFrequency = const Duration(milliseconds: 100)
..bitRate = 18000; // ..bitRate = 18000;
playerController = PlayerController(); playerController = PlayerController();
} }

@ -1,3 +1,4 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/colors.dart';

@ -112,7 +112,7 @@ class _VerifyLastLoginScreenState extends State<VerifyLastLoginScreen> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [LocaleKeys.lastLoginDetails.tr().toText16(), DateUtil.formatDateToDate(DateUtil.convertStringToDate(mobileLoginInfoListModel!.editedOn ?? DateTime.now().toString()), false).toText12()], children: [LocaleKeys.lastLoginDetails.tr().toText16(), DateUtil.formatDateToDate(DateUtil.convertStringToDate(mobileLoginInfoListModel?.editedOn ?? null), false).toText12()],
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -121,7 +121,7 @@ class _VerifyLastLoginScreenState extends State<VerifyLastLoginScreen> {
LocaleKeys.verificationType.tr().toText10(color: MyColors.grey57Color), LocaleKeys.verificationType.tr().toText10(color: MyColors.grey57Color),
getVerificationType(mobileLoginInfoListModel!.loginType!).toText12(), getVerificationType(mobileLoginInfoListModel!.loginType!).toText12(),
const Expanded(child: SizedBox()), const Expanded(child: SizedBox()),
DateUtil.formatDateToTime(DateUtil.convertStringToDate(mobileLoginInfoListModel!.editedOn!)).toText12(), DateUtil.formatDateToTime(DateUtil.convertStringToDate(mobileLoginInfoListModel?.editedOn)).toText12(),
], ],
), ),
], ],
@ -208,7 +208,7 @@ class _VerifyLastLoginScreenState extends State<VerifyLastLoginScreen> {
Future<bool> loginWithFaceIDAndBiometrics() async { Future<bool> loginWithFaceIDAndBiometrics() async {
IOSAuthMessages iosStrings = const IOSAuthMessages(cancelButton: 'cancel', goToSettingsButton: 'settings', goToSettingsDescription: 'Please set up your Touch ID.', lockOut: 'Please reenable your Touch ID'); IOSAuthMessages iosStrings = const IOSAuthMessages(cancelButton: 'cancel');
// IOSAuthMessages iosStrings = const IOSAuthMessages( // IOSAuthMessages iosStrings = const IOSAuthMessages(
// cancelButton: 'cancel', // cancelButton: 'cancel',
@ -219,9 +219,7 @@ class _VerifyLastLoginScreenState extends State<VerifyLastLoginScreen> {
bool authenticated = false; bool authenticated = false;
try { try {
authenticated = await auth.authenticate(localizedReason: 'Scan your fingerprint to authenticate', options: const AuthenticationOptions( authenticated = await auth.authenticate(localizedReason: 'Scan your fingerprint to authenticate',
useErrorDialogs: true, stickyAuth: true, biometricOnly: true,
),
authMessages: [ authMessages: [
iosStrings, iosStrings,
const AndroidAuthMessages(), const AndroidAuthMessages(),

@ -515,11 +515,11 @@ class _VerifyLoginScreenState extends State<VerifyLoginScreen> {
Future<bool> loginWithFaceIDAndBiometrics() async { Future<bool> loginWithFaceIDAndBiometrics() async {
IOSAuthMessages iosStrings = IOSAuthMessages iosStrings =
const IOSAuthMessages(cancelButton: 'cancel', goToSettingsButton: 'settings', goToSettingsDescription: 'Please set up your Touch ID.', lockOut: 'Please reenable your Touch ID'); const IOSAuthMessages(cancelButton: 'cancel',);
bool authenticated = false; bool authenticated = false;
try { try {
authenticated = await auth.authenticate(localizedReason: 'Scan your fingerprint to authenticate', authenticated = await auth.authenticate(localizedReason: 'Scan your fingerprint to authenticate',
options: const AuthenticationOptions(useErrorDialogs: true, stickyAuth: true, biometricOnly: true,), authMessages: [ authMessages: [
iosStrings, iosStrings,
const AndroidAuthMessages(), const AndroidAuthMessages(),
],); ],);

@ -30,18 +30,21 @@ class QuestionCard extends StatelessWidget {
width: double.infinity, width: double.infinity,
child: AppinioSwiper( child: AppinioSwiper(
duration: const Duration(milliseconds: 400), duration: const Duration(milliseconds: 400),
padding: EdgeInsets.zero, swipeOptions: const SwipeOptions.all(),
isDisabled: true, isDisabled: true,
controller: provider.swiperController, controller: provider.swiperController,
unswipe: (int index, AppinioSwiperDirection direction) {}, cardCount: provider.cardContentList.length,
onSwipe: (int index, AppinioSwiperDirection direction) {}, cardBuilder: (BuildContext context, int index) {
cards: provider.cardContentList, return provider.cardContentList[index];
},
), ),
), ),
); );
} }
} }
class CardContent extends StatelessWidget { class CardContent extends StatelessWidget {
const CardContent({Key? key}) : super(key: key); const CardContent({Key? key}) : super(key: key);

@ -97,8 +97,8 @@ class _SubordinateLeaveState extends State<SubordinateLeave> {
separatorBuilder: (BuildContext cxt, int index) => 12.height, separatorBuilder: (BuildContext cxt, int index) => 12.height,
itemCount: getSubordinatesLeavesTotalList.length, itemCount: getSubordinatesLeavesTotalList.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
var diffDays = DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATEEND!) var diffDays = DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATEEND)
.difference(DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATESTART!)) .difference(DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATESTART))
.inDays + 1; .inDays + 1;
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -109,8 +109,8 @@ class _SubordinateLeaveState extends State<SubordinateLeave> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
getSubordinatesLeavesTotalList[index].eMPLOYEENAME!.toText16(), getSubordinatesLeavesTotalList[index].eMPLOYEENAME!.toText16(),
ItemDetailView(LocaleKeys.from.tr(), DateUtil.getFormattedDate(DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATESTART!), "MMM dd yyyy")), ItemDetailView(LocaleKeys.from.tr(), DateUtil.getFormattedDate(DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATESTART), "MMM dd yyyy")),
ItemDetailView(LocaleKeys.to.tr(), DateUtil.getFormattedDate(DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATEEND!), "MMM dd yyyy")), ItemDetailView(LocaleKeys.to.tr(), DateUtil.getFormattedDate(DateUtil.convertStringToDate(getSubordinatesLeavesTotalList[index].dATEEND), "MMM dd yyyy")),
ItemDetailView(LocaleKeys.numberDays.tr(), diffDays.toString()), ItemDetailView(LocaleKeys.numberDays.tr(), diffDays.toString()),
], ],
).expanded ).expanded

@ -2,7 +2,8 @@ import 'dart:convert';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
// import 'package:flutter_html/flutter_html.dart';
import 'package:mohem_flutter_app/api/pending_transactions_api_client.dart'; import 'package:mohem_flutter_app/api/pending_transactions_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/colors.dart';
@ -59,8 +60,8 @@ class _AnnouncementDetailsState extends State<AnnouncementDetails> {
), ),
), ),
), ),
Html( HtmlWidget(
data: AppState().isArabic(context) ? getAnnouncementDetailsObj?.bodyAR : getAnnouncementDetailsObj?.bodyEN, AppState().isArabic(context) ? getAnnouncementDetailsObj?.bodyAR??"" : getAnnouncementDetailsObj?.bodyEN ??"",
).paddingOnly(top: 12), ).paddingOnly(top: 12),
], ],
).objectContainerView().paddingAll(21), ).objectContainerView().paddingAll(21),

@ -190,9 +190,9 @@ class _MowadhafhiHRRequestState extends State<MowadhafhiHRRequest> {
title.toText16().expanded, title.toText16().expanded,
6.width, 6.width,
SimpleButton(LocaleKeys.add.tr(), () async { SimpleButton(LocaleKeys.add.tr(), () async {
FilePickerResult? result = await FilePicker.platform.pickFiles(allowMultiple: true); List<PlatformFile>? result = await FilePicker.pickFiles();
if (result != null) { if (result != null) {
attachmentFiles = attachmentFiles + result.paths.map((path) => File(path!)).toList(); attachmentFiles = attachmentFiles + result.map((path) => File(path.uri.toFilePath())).toList();
attachmentFiles = attachmentFiles.toSet().toList(); attachmentFiles = attachmentFiles.toSet().toList();
setState(() {}); setState(() {});
} }

@ -92,17 +92,22 @@ class _MyDocumentDetailScreenState extends State<MyDocumentDetailScreen> {
), ),
12.height, 12.height,
DottedBorder( DottedBorder(
borderType: BorderType.RRect, options: RoundedRectDottedBorderOptions(
radius: const Radius.circular(10), radius: const Radius.circular(10),
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
dashPattern: const <double>[2, 1], dashPattern: const <double>[2, 1],
color: MyColors.selectedBorderColor, color: MyColors.selectedBorderColor,
),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Transform.rotate( Transform.rotate(
angle: 45, angle: 45,
child: const Icon(Icons.attach_file_rounded, size: 16, color: MyColors.selectedBorderColor), child: const Icon(
Icons.attach_file_rounded,
size: 16,
color: MyColors.selectedBorderColor,
),
), ),
4.width, 4.width,
"Attach Image".toText14(color: MyColors.selectedBorderColor), "Attach Image".toText14(color: MyColors.selectedBorderColor),

@ -67,10 +67,10 @@ class _MyRequestsState extends State<MyRequests> {
isReadOnly: false, isReadOnly: false,
).paddingOnly(bottom: 12), ).paddingOnly(bottom: 12),
itemBuilder: (_) => <PopupMenuItem<int>>[ itemBuilder: (_) => <PopupMenuItem<int>>[
for (int i = 0; i < getConcurrentProgramsList!.length; i++) PopupMenuItem<int>(child: Text(getConcurrentProgramsList![i].uSERCONCURRENTPROGRAMNAME!), value: i), for (int i = 0; i < getConcurrentProgramsList.length; i++) PopupMenuItem<int>(child: Text(getConcurrentProgramsList[i].uSERCONCURRENTPROGRAMNAME!), value: i),
], ],
onSelected: (int popupIndex) { onSelected: (int popupIndex) {
selectedConcurrentProgramList = getConcurrentProgramsList![popupIndex]; selectedConcurrentProgramList = getConcurrentProgramsList[popupIndex];
getCCPTransactions(selectedConcurrentProgramList?.cONCURRENTPROGRAMNAME); getCCPTransactions(selectedConcurrentProgramList?.cONCURRENTPROGRAMNAME);
setState(() {}); setState(() {});
}), }),
@ -105,7 +105,7 @@ class _MyRequestsState extends State<MyRequests> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
("Request ID: " + getCCPTransactionsList[index].rEQUESTID.toString()).toText12(color: MyColors.grey57Color), ("Request ID: " + getCCPTransactionsList[index].rEQUESTID.toString()).toText12(color: MyColors.grey57Color),
DateUtil.formatDateToDate(DateUtil.convertStringToDate(getCCPTransactionsList[index].rEQUESTDATE!), false).toText12(color: MyColors.grey70Color), DateUtil.formatDateToDate(DateUtil.convertStringToDate(getCCPTransactionsList[index].rEQUESTDATE), false).toText12(color: MyColors.grey70Color),
], ],
), ),
Container( Container(

@ -6,8 +6,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart';
@ -16,7 +16,7 @@ import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart'; // import 'package:share_plus/share_plus.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
class OffersAndDiscountsDetails extends StatefulWidget { class OffersAndDiscountsDetails extends StatefulWidget {
@ -68,10 +68,12 @@ class _OffersAndDiscountsDetailsState extends State<OffersAndDiscountsDetails> {
AppState().isArabic(context) AppState().isArabic(context)
? getOffersList[0].titleAr!.toText22(isBold: true, color: const Color(0xff2B353E)).center ? getOffersList[0].titleAr!.toText22(isBold: true, color: const Color(0xff2B353E)).center
: getOffersList[0].titleEn!.toText22(isBold: true, color: const Color(0xff2B353E)).center, : getOffersList[0].titleEn!.toText22(isBold: true, color: const Color(0xff2B353E)).center,
Html( HtmlWidget(
data: AppState().isArabic(context) ? getOffersList[0].descriptionAr! : getOffersList[0].descriptionEn ?? "", AppState().isArabic(context) ? getOffersList[0].descriptionAr! : getOffersList[0].descriptionEn ?? "",
onLinkTap: (String? url, Map<String, String> attributes, _) { onTapUrl: (String? url) {
launchUrl(Uri.parse(url!)); launchUrl(Uri.parse(url!));
return true;
}, },
), ),
checkDate(getOffersList[0].endDate!).paddingOnly(left: 8), checkDate(getOffersList[0].endDate!).paddingOnly(left: 8),
@ -122,7 +124,7 @@ class _OffersAndDiscountsDetailsState extends State<OffersAndDiscountsDetails> {
Directory tempDir = await getTemporaryDirectory(); Directory tempDir = await getTemporaryDirectory();
File file = await File('${tempDir.path}/${DateTime.now().toString()}.png').create(); File file = await File('${tempDir.path}/${DateTime.now().toString()}.png').create();
await file.writeAsBytes(pngBytes); await file.writeAsBytes(pngBytes);
await SharePlus.instance.share(ShareParams(files: [(XFile(file.path))], text: AppState().isArabic(context) ? getOffersList[0].titleAr : getOffersList[0].titleEn)); // await SharePlus.instance.share(ShareParams(files: [(XFile(file.path))], text: AppState().isArabic(context) ? getOffersList[0].titleAr : getOffersList[0].titleEn));
} catch (ex) { } catch (ex) {
debugPrint(ex.toString()); debugPrint(ex.toString());
} }

@ -211,7 +211,7 @@ class _ItemHistoryScreenState extends State<ItemHistoryScreen> {
interval: 1, interval: 1,
getTitlesWidget: (double value, TitleMeta meta) { getTitlesWidget: (double value, TitleMeta meta) {
return SideTitleWidget( return SideTitleWidget(
axisSide: meta.axisSide, meta: meta,
child: Text(reversedList[int.parse(meta.formattedValue)].cREATIONDATE!, style: TextStyle(fontSize: 10)), child: Text(reversedList[int.parse(meta.formattedValue)].cREATIONDATE!, style: TextStyle(fontSize: 10)),
); );
})), })),
@ -222,7 +222,7 @@ class _ItemHistoryScreenState extends State<ItemHistoryScreen> {
interval: 1, interval: 1,
getTitlesWidget: (double value, TitleMeta meta) { getTitlesWidget: (double value, TitleMeta meta) {
return SideTitleWidget( return SideTitleWidget(
axisSide: meta.axisSide, meta: meta,
child: Text(meta.formattedValue, style: TextStyle(fontSize: 10)), child: Text(meta.formattedValue, style: TextStyle(fontSize: 10)),
); );
})), })),
@ -233,7 +233,7 @@ class _ItemHistoryScreenState extends State<ItemHistoryScreen> {
reservedSize: 15, reservedSize: 15,
getTitlesWidget: (double value, TitleMeta meta) { getTitlesWidget: (double value, TitleMeta meta) {
return SideTitleWidget( return SideTitleWidget(
axisSide: meta.axisSide, meta: meta,
child: Text("", style: TextStyle(fontSize: 10)), child: Text("", style: TextStyle(fontSize: 10)),
); );
})), })),

@ -472,7 +472,7 @@ class _WorkListScreenState extends State<WorkListScreen> {
10.height, 10.height,
Row( Row(
children: [ children: [
DateUtil.formatDateToDate(DateUtil.convertStringToDate(requestDetails.modifiedDate!), false).toText10(color: MyColors.lightTextColor).expanded, DateUtil.formatDateToDate(DateUtil.convertStringToDate(requestDetails.modifiedDate), false).toText10(color: MyColors.lightTextColor).expanded,
RotatedBox(quarterTurns: AppState().isArabic(context) ? 2 : 4, child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.darkIconColor)), RotatedBox(quarterTurns: AppState().isArabic(context) ? 2 : 4, child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.darkIconColor)),
], ],
), ),

@ -750,7 +750,7 @@ class InfoFragment extends StatelessWidget {
ItemDetailGrid( ItemDetailGrid(
ItemDetailViewCol( LocaleKeys.requestDate.tr(), ItemDetailViewCol( LocaleKeys.requestDate.tr(),
DateUtil.formatDateToDate( DateUtil.formatDateToDate(
DateUtil.convertStringToDate(data.paymentDetailsList![0].requestDate!), false) ?? ""), DateUtil.convertStringToDate(data.paymentDetailsList![0].requestDate), false) ?? ""),
ItemDetailViewCol( ItemDetailViewCol(
LocaleKeys.requesterEmpName.tr(), data.paymentDetailsList![0].requesterEmployeeName ?? ""), LocaleKeys.requesterEmpName.tr(), data.paymentDetailsList![0].requesterEmployeeName ?? ""),
), ),

@ -47,7 +47,7 @@ class ImageOptions {
} }
}, },
onFilesTap: () async { onFilesTap: () async {
FilePickerResult? result = await FilePicker.platform.pickFiles( List<PlatformFile>? result = await FilePicker.pickFiles(
type: FileType.custom, type: FileType.custom,
allowedExtensions: [ allowedExtensions: [
'jpg', 'jpg',
@ -63,8 +63,8 @@ class ImageOptions {
'zip', 'zip',
], ],
); );
List<File> files = result!.paths.map((path) => File(path!)).toList(); List<File> files = result!.map((path) => File(path.uri.toFilePath())).toList();
image(result.files.first.path.toString(), files.first); image(result.first.path.toString(), files.first);
}, },
), ),
); );

@ -3,7 +3,9 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:nfc_manager/nfc_manager.dart'; import 'package:nfc_manager/nfc_manager.dart';
import 'package:nfc_manager/platform_tags.dart'; import 'package:nfc_manager/nfc_manager_android.dart';
import 'package:nfc_manager/nfc_manager_ios.dart';
import 'dart:typed_data';
void showNfcReader(BuildContext context, {required Function(String? nfcId) onNcfScan}) { void showNfcReader(BuildContext context, {required Function(String? nfcId) onNcfScan}) {
showModalBottomSheet( showModalBottomSheet(
@ -40,14 +42,23 @@ class _NfcLayoutState extends State<NfcLayout> {
void initState() { void initState() {
super.initState(); super.initState();
NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async { NfcManager.instance.startSession( pollingOptions: {NfcPollingOption.iso14443},onDiscovered: (NfcTag tag) async {
var f; Uint8List? id;
if (Platform.isAndroid) { if (Platform.isAndroid) {
f = MifareUltralight(tag: tag, identifier: tag.data["nfca"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22); id = NfcTagAndroid.from(tag)?.id;
} else { } else if (Platform.isIOS) {
f = MifareUltralight(tag: tag, identifier: tag.data["mifare"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22); id = MiFareIos.from(tag)?.identifier;
}
if (id == null) {
await NfcManager.instance.stopSession();
return;
} }
String identifier = f.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
String identifier =
id.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
print(identifier);
nfcId = identifier; nfcId = identifier;
setState(() { setState(() {

@ -38,93 +38,98 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.4 cupertino_icons: ^1.0.4
path_provider: ^2.0.8 path_provider: ^2.0.8
injector: ^2.0.0 injector: ^4.0.0
provider: ^6.0.1 provider: ^6.0.1
easy_localization: ^3.0.0 easy_localization: ^3.0.0
http: ^1.4.0 http: ^1.4.0
permission_handler: ^12.0.1 permission_handler: ^13.0.2
flutter_svg: any flutter_svg: any
sizer: ^2.0.15 sizer: ^3.1.3
local_auth: ^2.3.0 local_auth: ^3.0.2
fluttertoast: ^8.0.8 fluttertoast: ^10.0.0
syncfusion_flutter_calendar: ^29.1.38 syncfusion_flutter_calendar: ^35.1.37
# flutter_calendar_carousel: ^2.1.0 # flutter_calendar_carousel: ^2.1.0
pie_chart: ^5.1.0 pie_chart: ^5.1.0
shared_preferences: ^2.0.12 shared_preferences: ^2.0.12
firebase_messaging: ^15.2.9 firebase_messaging: ^16.7.0
shimmer: ^2.0.0 shimmer: ^4.0.0
logger: ^1.1.0 logger: ^2.8.0
flutter_countdown_timer: ^4.1.0 flutter_countdown_timer: ^4.1.0
# platform_device_id_plus: ^1.0.7 # platform_device_id_plus: ^1.0.7
mobile_device_identifier: ^0.0.3 # mobile_device_identifier: ^0.0.3
device_info_plus: ^11.5.0 device_info_plus: ^13.2.0
image_picker: ^1.1.2 image_picker: ^1.1.2
file_picker: ^8.3.1 file_picker: ^13.1.0
geolocator: ^9.0.2 geolocator: ^14.1.1
month_year_picker: ^0.5.0+1 month_year_picker: ^0.5.0+1
month_picker_dialog: ^6.0.3 month_picker_dialog: ^6.0.3
# open_file: ^3.2.1 # open_file: ^3.2.1
open_filex: ^4.6.0 open_filex: ^4.6.0
wifi_iot: ^0.3.19+1 wifi_iot: ^0.4.0
flutter_html: ^3.0.0-alpha.6 # flutter_html: ^3.0.0-alpha.6
mobile_scanner: ^6.0.10 flutter_widget_from_html: ^0.17.4
just_audio: ^0.10.6
# mobile@1234
mobile_scanner: ^7.4.2
# flutter_barcode_scanner: ^2.0.0 # flutter_barcode_scanner: ^2.0.0
# qr_code_scanner: ^1.0.1 # qr_code_scanner: ^1.0.1
# qr_flutter: ^4.0.0 # qr_flutter: ^4.0.0
url_launcher: ^6.0.15 url_launcher: ^6.0.15
share_plus: ^11.0.0 # share_plus: ^13.3.0
flutter_rating_bar: ^4.0.1 flutter_rating_bar: ^4.0.1
auto_size_text: ^3.0.0 auto_size_text: ^3.0.0
pull_to_refresh: ^2.0.0 pull_to_refresh: ^2.0.0
fl_chart: ^0.66.0 fl_chart: ^1.2.0
# lottie json animations # lottie json animations
lottie: any lottie: any
# Marathon Card Swipe # Marathon Card Swipe
appinio_swiper: ^1.1.1 appinio_swiper: ^2.1.1
expandable: ^5.0.1 expandable: ^5.0.1
safe_device: ^1.1.9 safe_device: ^1.1.9
# networkImage # networkImage
cached_network_image: ^3.2.2 cached_network_image: ^4.0.3
#Chat #Chat
signalr_netcore: ^1.3.3 signalr_netcore: ^1.3.3
logging: ^1.0.1 logging: ^1.0.1
swipe_to: ^1.0.2 swipe_to: ^1.0.2
flutter_webrtc: ^0.12.11 flutter_webrtc: ^1.6.2+hotfix.3
# camera: ^0.10.3 # camera: ^0.10.3
camera: ^0.11.2 camera: ^0.12.1
flutter_local_notifications: ^18.0.1 flutter_local_notifications: ^22.3.1
#firebase_analytics: any #firebase_analytics: any
#Chat Voice Message Recoding & Play #Chat Voice Message Recoding & Play
audio_waveforms: ^1.3.0 audio_waveforms: ^2.0.2
rxdart: ^0.27.7 rxdart: ^0.28.0
#Encryption #Encryption
flutter_des: ^2.1.0 flutter_des: ^3.0.0
video_player: 2.7.0 video_player: ^2.14.0
just_audio: ^0.9.30 # just_audio: ^0.9.30
# safe_device: ^1.1.2 # safe_device: ^1.1.2
flutter_layout_grid: ^2.0.1 flutter_layout_grid: ^2.0.1
#Huawei Dependencies #Huawei Dependencies
# huawei_hmsavailability: ^6.6.0+300 # huawei_hmsavailability: ^6.6.0+300
# huawei_location: 6.0.0+302 # huawei_location: 6.0.0+302
huawei_location: ^6.14.2+301 huawei_location: ^6.17.0+300
# huawei_push: ^6.7.0+300 # huawei_push: ^6.7.0+300
huawei_map: # huawei_map:
git: # git:
url: https://github.com/fleoparra/hms-flutter-plugin.git # url: https://github.com/fleoparra/hms-flutter-plugin.git
path: flutter-hms-map # path: flutter-hms-map
huawei_push: huawei_push: ^6.15.0+300
git: # git:
url: https://github.com/crasowas/hms-flutter-plugin.git # url: https://github.com/crasowas/hms-flutter-plugin.git
path: flutter-hms-push # path: flutter-hms-push
firebase_crashlytics: ^4.3.9 firebase_crashlytics: ^5.4.0
#Items for sale Image Carousel Slider #Items for sale Image Carousel Slider
carousel_slider: ^5.1.1 carousel_slider: ^5.1.1
@ -132,15 +137,15 @@ dependencies:
#Huawei Specified #Huawei Specified
# store_checker: ^1.1.0 # store_checker: ^1.1.0
google_api_availability: ^5.0.1 google_api_availability: ^5.0.1
google_maps_flutter_web: ^0.5.4 google_maps_flutter_web: ^0.6.4+1
in_app_update: 4.1.0 in_app_update: 5.0.0
flutter_inappwebview: ^6.1.5 flutter_inappwebview: ^6.1.5
flutter_blurhash: ^0.8.0 flutter_blurhash: ^0.9.1
#todo its for temporary purpose, later will remove this. #todo its for temporary purpose, later will remove this.
dotted_border: ^2.0.0+3 dotted_border: ^3.1.0
webview_flutter: ^4.13.0 webview_flutter: ^4.13.0
nfc_manager: ^3.2.0 nfc_manager: ^4.2.1
speech_to_text: ^7.3.0 speech_to_text: ^7.3.0
@ -161,7 +166,7 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your # activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint # package. See that file for information about deactivating specific lint
# rules and activating additional ones. # rules and activating additional ones.
flutter_lints: ^1.0.4 flutter_lints: ^6.0.0
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec

Loading…
Cancel
Save