├── LICENSE ├── README.md ├── android-flet ├── v0.1.1 │ ├── AndroidManifest.xml │ ├── WarpScanner │ │ ├── README.md │ │ ├── pyproject.toml │ │ └── src │ │ │ ├── assets │ │ │ ├── icon.png │ │ │ └── splash_android.png │ │ │ └── main.py │ └── kotlin │ │ ├── MainActivity.kt │ │ ├── PermissionCallback.kt │ │ ├── PermissionHelper.kt │ │ └── PermissionUtils.kt └── v0.1.3 │ ├── AndroidManifest.xml │ ├── WarpScanner │ ├── README.md │ ├── pyproject.toml │ └── src │ │ ├── assets │ │ ├── icon.png │ │ └── splash_android.png │ │ └── main.py │ └── kotlin │ ├── MainActivity.kt │ ├── PermissionCallback.kt │ ├── PermissionHelper.kt │ └── PermissionUtils.kt └── android-toga ├── v0.0.1.py ├── v0.0.2 └── v0.0.2.py ├── v0.0.3 ├── java │ ├── .MainActivity.java.swp │ ├── DrawHandlerView.java │ ├── IDrawHandler.java │ ├── IPythonApp.java │ ├── MainActivity.java │ └── PermissionUtils.java └── v0.0.3.py ├── v0.0.4 ├── java │ ├── .MainActivity.java.swp │ ├── DrawHandlerView.java │ ├── IDrawHandler.java │ ├── IPythonApp.java │ ├── MainActivity.java │ ├── PermissionUtils.java │ ├── VersionChecker.java │ └── tt └── v0.0.4.py ├── v0.0.5 ├── java │ ├── .MainActivity.java.swp │ ├── DrawHandlerView.java │ ├── IDrawHandler.java │ ├── IPythonApp.java │ ├── LTRConfig.java │ ├── LoggingConfig.java │ ├── MainActivity.java │ ├── PermissionUtils.java │ └── VersionChecker.java └── v0.0.5.py ├── v0.0.6 ├── AndroidManifest.xml ├── java │ ├── .MainActivity.java.swp │ ├── DrawHandlerView.java │ ├── IDrawHandler.java │ ├── IPythonApp.java │ ├── LTRConfig.java │ ├── LoggingConfig.java │ ├── MainActivity.java │ ├── PermissionUtils.java │ └── VersionChecker.java └── v0.0.6.py ├── v0.0.7 ├── java │ ├── .MainActivity.java.swp │ ├── DrawHandlerView.java │ ├── IDrawHandler.java │ ├── IPythonApp.java │ ├── LTRConfig.java │ ├── LoggingConfig.java │ ├── MainActivity.java │ ├── PermissionUtils.java │ └── VersionChecker.java └── v0.0.7.py ├── v0.0.8 ├── java │ ├── .MainActivity.java.swp │ ├── DrawHandlerView.java │ ├── IDrawHandler.java │ ├── IPythonApp.java │ ├── LTRConfig.java │ ├── LoggingConfig.java │ ├── MainActivity.java │ ├── PermissionUtils.java │ ├── VersionChecker.java │ └── j └── v0.0.8.py ├── v0.0.9 ├── java │ ├── .MainActivity.java.swp │ ├── DrawHandlerView.java │ ├── IDrawHandler.java │ ├── IPythonApp.java │ ├── LTRConfig.java │ ├── LoggingConfig.java │ ├── MainActivity.java │ ├── PermissionUtils.java │ └── VersionChecker.java └── v0.0.9.py ├── v0.1.0 ├── WarpScanner │ ├── AndroidManifest.xml │ ├── WarpScanner │ │ ├── README.md │ │ ├── pyproject.toml │ │ └── src │ │ │ ├── assets │ │ │ ├── icon.png │ │ │ └── splash_android.png │ │ │ └── main.py │ └── kotlin │ │ ├── MainActivity.kt │ │ ├── PermissionCallback.kt │ │ ├── PermissionHelper.kt │ │ └── PermissionUtils.kt └── n └── version.txt /README.md: -------------------------------------------------------------------------------- 1 | # 🚀 **وارپ اسکنر با صفحه گرافیکی!** 🚀 2 | 3 |

4 | 5 | 🎨 **تلاش کردم این صفحه گرافیکی مثل نسخه ویندوز در بیاد که تقریبا در اومد ولی باگ‌های خودش رو داره.** 6 | 7 |

8 | 9 | 10 | --- 11 | 12 |

13 | 14 | 🛠️ **اگر بعد از بروز رسانی یا به هر دلیلی روی صفحه اول (اسپلش) گیر کردید، پوشه `wwarpscanner` که در پوشه `Download` هست رو پاک کنید و داده‌های اپ رو هم همینطور. اگه مشکل حل نشد، گوشی رو ری‌استارت کنید.** 15 | 16 |

17 | 18 | --- 19 | 20 |

21 | 22 | 💡 **سعی می‌کنم تو هر نسخه جدیدی که میدم باگ‌های قبلی رو رفع کنم یا حداقل ازشون جلوگیری کنم.** 23 |

24 | 25 | --- 26 | 27 |

28 | 29 | ⚡ **اگر باگ جدیدی برخوردید که از قبل پیش‌بینی نشده و من در موردش توضیح نداده بودم، حتما اطلاع بدید.** 30 | 31 |

32 | 33 | 34 | --- 35 | 36 | ## 🎉 **لذت ببرید و به ما بگید که چطور میشه بهترش کرد!** 🎉 37 | -------------------------------------------------------------------------------- /android-flet/v0.1.1/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 25 | 26 | 27 | 28 | 36 | 40 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 53 | 56 | 57 | -------------------------------------------------------------------------------- /android-flet/v0.1.1/WarpScanner/README.md: -------------------------------------------------------------------------------- 1 | # Warpscanner app 2 | 3 | ## Run the app 4 | 5 | ### uv 6 | 7 | Run as a desktop app: 8 | 9 | ``` 10 | uv run flet run 11 | ``` 12 | 13 | Run as a web app: 14 | 15 | ``` 16 | uv run flet run --web 17 | ``` 18 | 19 | ### Poetry 20 | 21 | Install dependencies from `pyproject.toml`: 22 | 23 | ``` 24 | poetry install 25 | ``` 26 | 27 | Run as a desktop app: 28 | 29 | ``` 30 | poetry run flet run 31 | ``` 32 | 33 | Run as a web app: 34 | 35 | ``` 36 | poetry run flet run --web 37 | ``` 38 | 39 | For more details on running the app, refer to the [Getting Started Guide](https://flet.dev/docs/getting-started/). 40 | 41 | ## Build the app 42 | 43 | ### Android 44 | 45 | ``` 46 | flet build apk -v 47 | ``` 48 | 49 | For more details on building and signing `.apk` or `.aab`, refer to the [Android Packaging Guide](https://flet.dev/docs/publish/android/). 50 | 51 | ### iOS 52 | 53 | ``` 54 | flet build ipa -v 55 | ``` 56 | 57 | For more details on building and signing `.ipa`, refer to the [iOS Packaging Guide](https://flet.dev/docs/publish/ios/). 58 | 59 | ### macOS 60 | 61 | ``` 62 | flet build macos -v 63 | ``` 64 | 65 | For more details on building macOS package, refer to the [macOS Packaging Guide](https://flet.dev/docs/publish/macos/). 66 | 67 | ### Linux 68 | 69 | ``` 70 | flet build linux -v 71 | ``` 72 | 73 | For more details on building Linux package, refer to the [Linux Packaging Guide](https://flet.dev/docs/publish/linux/). 74 | 75 | ### Windows 76 | 77 | ``` 78 | flet build windows -v 79 | ``` 80 | 81 | For more details on building Windows package, refer to the [Windows Packaging Guide](https://flet.dev/docs/publish/windows/). -------------------------------------------------------------------------------- /android-flet/v0.1.1/WarpScanner/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "WarpScanner" 3 | version = "0.1.0" 4 | description = "This is a WarpScanner for android" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | authors = [ 8 | { name = "arshiacomplus", email = "arshiacomplus@gmail.com" } 9 | ] 10 | dependencies = [ 11 | "flet==0.27.6", 12 | "icmplib", 13 | "requests", 14 | "retrying" 15 | ] 16 | 17 | [tool.flet] 18 | # org name in reverse domain name notation, e.g. "com.mycompany". 19 | # Combined with project.name to build bundle ID for iOS and Android apps 20 | org = "com.arshiacomplus.warpscanner" 21 | 22 | # project display name that is used as an app title on Android and iOS home screens, 23 | # shown in window titles and about app dialogs on desktop. 24 | product = "warpscanner" 25 | 26 | # company name to display in about app dialogs 27 | company = "" 28 | 29 | # copyright text to display in about app dialogs 30 | copyright = "Copyright (C) 2025 by arshiacomplus" 31 | 32 | [tool.flet.app] 33 | path = "src" 34 | 35 | [tool.uv] 36 | dev-dependencies = [ 37 | "flet[all]==0.27.6", 38 | ] 39 | 40 | [tool.poetry] 41 | package-mode = false 42 | 43 | [tool.poetry.group.dev.dependencies] 44 | flet = {extras = ["all"], version = "0.27.6"} -------------------------------------------------------------------------------- /android-flet/v0.1.1/WarpScanner/src/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/70fdbdc27e57615e2b26fb66f6edb95026e3db8d/android-flet/v0.1.1/WarpScanner/src/assets/icon.png -------------------------------------------------------------------------------- /android-flet/v0.1.1/WarpScanner/src/assets/splash_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/70fdbdc27e57615e2b26fb66f6edb95026e3db8d/android-flet/v0.1.1/WarpScanner/src/assets/splash_android.png -------------------------------------------------------------------------------- /android-flet/v0.1.1/kotlin/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.arshiacomplus.warpscanner.warpscanner // << پکیج نیم شما 2 | 3 | import android.os.Build // <<<< این import برای چک کردن نسخه اندروید لازم است >>>> 4 | import android.os.Bundle 5 | import android.os.Environment // <<<< این import برای چک کردن نتیجه MANAGE_ALL_FILES لازم است >>>> 6 | import android.util.Log 7 | import androidx.annotation.NonNull 8 | import io.flutter.embedding.android.FlutterActivity 9 | // import java.lang.ref.WeakReference // Alternative approach (less needed here) 10 | 11 | class MainActivity: FlutterActivity() { 12 | 13 | companion object { 14 | private const val TAG = "MainActivityKt" 15 | @JvmField 16 | var currentActivityInstance: MainActivity? = null 17 | } 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | currentActivityInstance = this 22 | Log.i(TAG, "MainActivity instance created and static reference stored: $this") 23 | 24 | // --- درخواست مجوز مدیریت تمام فایل‌ها (MANAGE_EXTERNAL_STORAGE) --- 25 | // این کد فقط در اندروید 11 (API 30) و بالاتر اجرا می‌شود. 26 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 27 | // فقط اگر مجوز از قبل داده نشده، کاربر را به تنظیمات بفرست 28 | if (!Environment.isExternalStorageManager()) { 29 | Log.d(TAG, "[Permission Request] Need MANAGE_EXTERNAL_STORAGE. Opening settings...") 30 | try { 31 | // فراخوانی متد استاتیک از object PermissionUtils 32 | // 'this' به عنوان Context و Activity پاس داده می‌شود. 33 | // فرض بر این است که PermissionUtils.kt در همین پکیج وجود دارد 34 | PermissionUtils.requestManageAllFilesPermission(this, this) 35 | // کاربر به تنظیمات هدایت می‌شود. نتیجه مستقیماً برنمی‌گردد. 36 | } catch (e: Exception) { // گرفتن Exception کلی برای اطمینان 37 | Log.e(TAG, "[Permission Request] Error calling requestManageAllFilesPermission", e) 38 | } 39 | } else { 40 | Log.i(TAG, "[Permission Check] MANAGE_EXTERNAL_STORAGE already granted.") 41 | } 42 | } else { 43 | // برای نسخه‌های پایین‌تر، این مجوز لازم نیست یا وجود ندارد. 44 | Log.i(TAG, "[Permission Check] MANAGE_EXTERNAL_STORAGE not applicable below Android 11.") 45 | } 46 | // -------------------------------------------------------------------- 47 | } 48 | 49 | // این متد برای دریافت نتیجه درخواست‌های استاندارد (مثل Storage) است 50 | // و نتیجه MANAGE_EXTERNAL_STORAGE را دریافت *نمی‌کند*. 51 | override fun onRequestPermissionsResult( 52 | requestCode: Int, 53 | @NonNull permissions: Array, 54 | @NonNull grantResults: IntArray 55 | ) { 56 | Log.d(TAG, "onRequestPermissionsResult received for requestCode: $requestCode") 57 | super.onRequestPermissionsResult(requestCode, permissions, grantResults) 58 | 59 | // ارسال نتیجه به PermissionHelper (برای درخواست‌هایی که از پایتون می‌آیند) 60 | try { 61 | PermissionHelper.handleRequestPermissionsResult(requestCode, permissions, grantResults) 62 | Log.d(TAG, "Passed permission result to PermissionHelper.") 63 | } catch (e: ClassNotFoundException) { 64 | Log.e(TAG, "FATAL: PermissionHelper class not found!", e) 65 | } catch (e: NoSuchMethodError) { 66 | Log.e(TAG, "FATAL: handleRequestPermissionsResult method not found or mismatch!", e) 67 | } catch (t: Throwable) { 68 | Log.e(TAG, "Unexpected error calling PermissionHelper.handleRequestPermissionsResult", t) 69 | } 70 | } 71 | 72 | // **مهم:** برای چک کردن نتیجه MANAGE_EXTERNAL_STORAGE 73 | // باید از onResume استفاده کنید. 74 | override fun onResume() { 75 | super.onResume() 76 | Log.d(TAG, "onResume called.") 77 | // چک کردن وضعیت مجوز مدیریت فایل‌ها وقتی کاربر به برنامه برمی‌گردد 78 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 79 | val hasManagePermission = Environment.isExternalStorageManager() 80 | Log.i(TAG, "[Permission Check onResume] MANAGE_EXTERNAL_STORAGE Granted: $hasManagePermission") 81 | // اینجا می‌توانید بر اساس وضعیت مجوز، کاری انجام دهید 82 | // مثلاً یک پیام به کاربر نشان دهید یا قابلیتی را فعال/غیرفعال کنید. 83 | // توجه: ارسال مستقیم نتیجه به پایتون از اینجا کمی پیچیده‌تر است. 84 | } 85 | } 86 | 87 | 88 | override fun onDestroy() { 89 | super.onDestroy() 90 | Log.i(TAG, "MainActivity instance being destroyed: $this") 91 | if (currentActivityInstance == this) { 92 | currentActivityInstance = null 93 | Log.i(TAG, "Static MainActivity instance reference cleared.") 94 | } else { 95 | Log.w(TAG, "onDestroy called, but static instance was already different or null.") 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /android-flet/v0.1.1/kotlin/PermissionCallback.kt: -------------------------------------------------------------------------------- 1 | package com.arshiacomplus.warpscanner.warpscanner // << پکیج نیم شما 2 | 3 | interface PermissionCallback { 4 | fun onPermissionResult(allGranted: Boolean) 5 | } -------------------------------------------------------------------------------- /android-flet/v0.1.1/kotlin/PermissionHelper.kt: -------------------------------------------------------------------------------- 1 | package com.arshiacomplus.warpscanner.warpscanner // << پکیج نیم شما 2 | 3 | import android.app.Activity 4 | import android.content.pm.PackageManager 5 | import android.util.Log 6 | import androidx.core.app.ActivityCompat 7 | import androidx.core.content.ContextCompat 8 | import android.Manifest 9 | 10 | object PermissionHelper { // <<<< باید object یا class باشد، نه چیز دیگر 11 | 12 | private const val TAG = "PermissionHelperKt" 13 | private const val STORAGE_PERMISSION_REQUEST_CODE = 101 14 | 15 | @Volatile 16 | private var permissionCallback: PermissionCallback? = null 17 | 18 | @JvmStatic 19 | fun requestStoragePermission(activity: Activity, callback: PermissionCallback) { 20 | Log.d(TAG, "requestStoragePermission called from Python") 21 | this.permissionCallback = callback 22 | 23 | val permissionsToRequest = arrayOf( 24 | Manifest.permission.READ_EXTERNAL_STORAGE, 25 | Manifest.permission.WRITE_EXTERNAL_STORAGE 26 | ) 27 | 28 | val permissionsNeeded = permissionsToRequest.filter { 29 | ContextCompat.checkSelfPermission(activity, it) != PackageManager.PERMISSION_GRANTED 30 | }.toTypedArray() 31 | 32 | if (permissionsNeeded.isEmpty()) { 33 | Log.d(TAG, "All storage permissions already granted.") 34 | try { 35 | callback.onPermissionResult(true) 36 | } catch (t: Throwable) { 37 | Log.e(TAG, "Error calling callback.onPermissionResult (permissions already granted)", t) 38 | } finally { 39 | this.permissionCallback = null 40 | } 41 | } else { 42 | Log.d(TAG, "Requesting permissions: ${permissionsNeeded.joinToString()}") 43 | ActivityCompat.requestPermissions( 44 | activity, 45 | permissionsNeeded, 46 | STORAGE_PERMISSION_REQUEST_CODE 47 | ) 48 | } 49 | } 50 | 51 | @JvmStatic 52 | fun handleRequestPermissionsResult( 53 | requestCode: Int, 54 | permissions: Array, 55 | grantResults: IntArray 56 | ) { 57 | Log.d(TAG, "handleRequestPermissionsResult called from MainActivity for requestCode: $requestCode") 58 | if (requestCode == STORAGE_PERMISSION_REQUEST_CODE) { 59 | val callback = this.permissionCallback 60 | if (callback != null) { 61 | val allGranted = grantResults.isNotEmpty() && grantResults.all { it == PackageManager.PERMISSION_GRANTED } 62 | Log.d(TAG, "Permission result: allGranted = $allGranted") 63 | try { 64 | callback.onPermissionResult(allGranted) 65 | } catch (t: Throwable) { 66 | Log.e(TAG, "Error calling callback.onPermissionResult (after request)", t) 67 | } finally { 68 | this.permissionCallback = null 69 | Log.d(TAG, "Permission callback cleared.") 70 | } 71 | } else { 72 | Log.w(TAG, "permissionCallback was null when handling result.") 73 | } 74 | } else { 75 | Log.w(TAG, "Received result for unknown requestCode: $requestCode") 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /android-flet/v0.1.1/kotlin/PermissionUtils.kt: -------------------------------------------------------------------------------- 1 | package com.arshiacomplus.warpscanner.warpscanner // << پکیج نیم خودت رو اینجا بذار 2 | 3 | import android.Manifest 4 | import android.app.Activity 5 | import android.content.ActivityNotFoundException 6 | import android.content.Context 7 | import android.content.Intent 8 | import android.content.pm.PackageManager 9 | import android.net.Uri // لازم برای Intent جدیدتر در MANAGE_ALL_FILES 10 | import android.os.Build 11 | import android.os.Environment 12 | import android.provider.Settings 13 | import android.util.Log // برای لاگ‌گیری خطا 14 | import androidx.annotation.RequiresApi // برای نشان دادن نیاز به نسخه API 15 | import androidx.core.app.ActivityCompat 16 | import androidx.core.content.ContextCompat 17 | 18 | /** 19 | * کلاس کمکی برای مدیریت درخواست‌های مجوز در اندروید. 20 | * از object به جای class استفاده شده چون فقط شامل متدهای استاتیک-مانند است. 21 | */ 22 | object PermissionUtils { 23 | 24 | private const val TAG = "PermissionUtilsKt" // تگ برای لاگ 25 | private const val STORAGE_PERMISSION_REQUEST_CODE = 101 // کد دلخواه برای درخواست Storage 26 | 27 | /** 28 | * مجوزهای استاندارد خواندن و نوشتن حافظه خارجی را درخواست می‌کند. 29 | * (READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE) 30 | * این متد برای اندروید 10 به پایین حیاتی است و در نسخه‌های بالاتر هم ممکن است لازم باشد. 31 | * 32 | * @param activity اکتیویتی فعلی که درخواست از آن ارسال می‌شود. 33 | */ 34 | @JvmStatic // این انوتیشن اجازه می‌دهد متد مانند یک متد استاتیک جاوا فراخوانی شود 35 | fun requestStoragePermissions(activity: Activity) { 36 | // بررسی اینکه آیا مجوزها قبلاً داده شده‌اند یا نه 37 | val readPermissionGranted = ContextCompat.checkSelfPermission( 38 | activity, Manifest.permission.READ_EXTERNAL_STORAGE 39 | ) == PackageManager.PERMISSION_GRANTED 40 | 41 | val writePermissionGranted = ContextCompat.checkSelfPermission( 42 | activity, Manifest.permission.WRITE_EXTERNAL_STORAGE 43 | ) == PackageManager.PERMISSION_GRANTED 44 | 45 | // اگر هر کدام از مجوزها داده نشده باشند 46 | if (!readPermissionGranted || !writePermissionGranted) { 47 | Log.d(TAG, "Requesting standard storage permissions (READ/WRITE)...") 48 | // لیست مجوزهایی که باید درخواست شوند 49 | val permissionsToRequest = arrayOf( 50 | Manifest.permission.READ_EXTERNAL_STORAGE, 51 | Manifest.permission.WRITE_EXTERNAL_STORAGE 52 | ) 53 | // نمایش دیالوگ سیستمی درخواست مجوز 54 | ActivityCompat.requestPermissions( 55 | activity, 56 | permissionsToRequest, 57 | STORAGE_PERMISSION_REQUEST_CODE // کد برای شناسایی نتیجه در onRequestPermissionsResult 58 | ) 59 | } else { 60 | Log.d(TAG, "Standard storage permissions (READ/WRITE) already granted.") 61 | // اگر مجوزها از قبل داده شده‌اند، کاری انجام نمی‌دهیم 62 | // (می‌توان یک callback برای اطلاع‌رسانی داشت) 63 | } 64 | } 65 | 66 | /** 67 | * درخواست مجوز MANAGE_EXTERNAL_STORAGE برای اندروید 11 (API 30) و بالاتر. 68 | * این مجوز دسترسی کامل به حافظه خارجی می‌دهد اما نیاز به بررسی دقیق در گوگل پلی دارد. 69 | * این متد کاربر را به صفحه تنظیمات سیستم هدایت می‌کند. 70 | * 71 | * @param context کانتکست برنامه (می‌تواند Activity یا Application Context باشد). 72 | * @param activity (اختیاری) اگر می‌خواهید از startActivityForResult استفاده کنید (در اینجا لازم نیست). 73 | */ 74 | @JvmStatic 75 | @RequiresApi(Build.VERSION_CODES.R) // این متد فقط برای اندروید 11 و بالاتر معنی دارد 76 | fun requestManageAllFilesPermission(context: Context, activity: Activity? = null) { 77 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 78 | // بررسی اینکه آیا برنامه در حال حاضر مجوز MANAGE_EXTERNAL_STORAGE را دارد؟ 79 | if (!Environment.isExternalStorageManager()) { 80 | Log.d(TAG, "Requesting MANAGE_EXTERNAL_STORAGE permission by opening settings...") 81 | try { 82 | // ایجاد Intent برای باز کردن صفحه تنظیمات مدیریت فایل‌ها 83 | val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) 84 | // اضافه کردن پکیج نیم برنامه به URI تا مستقیماً به تنظیمات همین برنامه برود 85 | intent.data = Uri.parse("package:${context.packageName}") 86 | 87 | // اگر activity داریم، از آن برای شروع استفاده می‌کنیم (بهتر است) 88 | if (activity != null) { 89 | activity.startActivity(intent) // می‌توان از startActivityForResult هم استفاده کرد اگر نیاز به نتیجه فوری باشد 90 | } else { 91 | // اگر فقط context داریم (مثلاً از سرویس)، فلگ NEW_TASK لازم است 92 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 93 | context.startActivity(intent) 94 | } 95 | Log.d(TAG, "Settings activity for MANAGE_ALL_FILES opened.") 96 | // توجه: نتیجه این درخواست مستقیماً به برنامه برنمی‌گردد. 97 | // باید دوباره Environment.isExternalStorageManager() را چک کنید 98 | // وقتی کاربر به برنامه برمی‌گردد (مثلاً در onResume اکتیویتی). 99 | 100 | } catch (e: ActivityNotFoundException) { 101 | // اگر صفحه تنظیمات مربوطه در دستگاه وجود نداشت (بسیار نادر) 102 | Log.e(TAG, "Activity not found to handle MANAGE_ALL_FILES_ACCESS_PERMISSION intent.", e) 103 | // می‌توانید یک Intent عمومی‌تر برای تنظیمات برنامه امتحان کنید 104 | // val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) 105 | // intent.data = Uri.parse("package:${context.packageName}") 106 | // context.startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) 107 | } catch(t: Throwable) { 108 | Log.e(TAG, "Error opening MANAGE_ALL_FILES settings", t) 109 | } 110 | } else { 111 | Log.d(TAG, "MANAGE_EXTERNAL_STORAGE permission already granted.") 112 | // اگر مجوز از قبل داده شده، کاری انجام نمی‌دهیم 113 | } 114 | } else { 115 | // برای نسخه‌های پایین‌تر از اندروید 11، این مجوز وجود ندارد 116 | Log.w(TAG, "MANAGE_EXTERNAL_STORAGE permission is not applicable below Android 11 (API 30).") 117 | } 118 | } 119 | 120 | // --- متدهای کمکی دیگر (می‌توانید اضافه کنید) --- 121 | 122 | /** 123 | * بررسی می‌کند آیا مجوزهای استاندارد ذخیره‌سازی داده شده‌اند یا نه. 124 | */ 125 | @JvmStatic 126 | fun hasStoragePermissions(context: Context): Boolean { 127 | val readPermission = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) 128 | val writePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) 129 | return readPermission == PackageManager.PERMISSION_GRANTED && writePermission == PackageManager.PERMISSION_GRANTED 130 | } 131 | 132 | /** 133 | * بررسی می‌کند آیا مجوز مدیریت تمام فایل‌ها داده شده است (فقط اندروید ۱۱ به بالا). 134 | */ 135 | @JvmStatic 136 | @RequiresApi(Build.VERSION_CODES.R) 137 | fun hasManageAllFilesPermission(): Boolean { 138 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 139 | Environment.isExternalStorageManager() 140 | } else { 141 | // در نسخه‌های پایین‌تر، این مجوز وجود ندارد، پس true برمی‌گردانیم؟ 142 | // یا false چون مفهوم ندارد؟ بستگی به منطق برنامه دارد. 143 | // معمولا true مناسب‌تر است چون محدودیت وجود ندارد. 144 | true // یا false بر اساس نیاز 145 | } 146 | } 147 | 148 | } -------------------------------------------------------------------------------- /android-flet/v0.1.3/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 25 | 26 | 27 | 28 | 36 | 40 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 53 | 56 | 57 | -------------------------------------------------------------------------------- /android-flet/v0.1.3/WarpScanner/README.md: -------------------------------------------------------------------------------- 1 | # Warpscanner app 2 | 3 | ## Run the app 4 | 5 | ### uv 6 | 7 | Run as a desktop app: 8 | 9 | ``` 10 | uv run flet run 11 | ``` 12 | 13 | Run as a web app: 14 | 15 | ``` 16 | uv run flet run --web 17 | ``` 18 | 19 | ### Poetry 20 | 21 | Install dependencies from `pyproject.toml`: 22 | 23 | ``` 24 | poetry install 25 | ``` 26 | 27 | Run as a desktop app: 28 | 29 | ``` 30 | poetry run flet run 31 | ``` 32 | 33 | Run as a web app: 34 | 35 | ``` 36 | poetry run flet run --web 37 | ``` 38 | 39 | For more details on running the app, refer to the [Getting Started Guide](https://flet.dev/docs/getting-started/). 40 | 41 | ## Build the app 42 | 43 | ### Android 44 | 45 | ``` 46 | flet build apk -v 47 | ``` 48 | 49 | For more details on building and signing `.apk` or `.aab`, refer to the [Android Packaging Guide](https://flet.dev/docs/publish/android/). 50 | 51 | ### iOS 52 | 53 | ``` 54 | flet build ipa -v 55 | ``` 56 | 57 | For more details on building and signing `.ipa`, refer to the [iOS Packaging Guide](https://flet.dev/docs/publish/ios/). 58 | 59 | ### macOS 60 | 61 | ``` 62 | flet build macos -v 63 | ``` 64 | 65 | For more details on building macOS package, refer to the [macOS Packaging Guide](https://flet.dev/docs/publish/macos/). 66 | 67 | ### Linux 68 | 69 | ``` 70 | flet build linux -v 71 | ``` 72 | 73 | For more details on building Linux package, refer to the [Linux Packaging Guide](https://flet.dev/docs/publish/linux/). 74 | 75 | ### Windows 76 | 77 | ``` 78 | flet build windows -v 79 | ``` 80 | 81 | For more details on building Windows package, refer to the [Windows Packaging Guide](https://flet.dev/docs/publish/windows/). -------------------------------------------------------------------------------- /android-flet/v0.1.3/WarpScanner/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "WarpScanner" 3 | version = "0.1.3" 4 | description = "This is a WarpScanner for android" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | authors = [ 8 | { name = "arshiacomplus", email = "arshiacomplus@gmail.com" } 9 | ] 10 | dependencies = [ 11 | "flet==0.27.6", 12 | "icmplib", 13 | "requests", 14 | "retrying" 15 | ] 16 | 17 | [tool.flet] 18 | # org name in reverse domain name notation, e.g. "com.mycompany". 19 | # Combined with project.name to build bundle ID for iOS and Android apps 20 | org = "com.arshiacomplus.warpscanner" 21 | 22 | # project display name that is used as an app title on Android and iOS home screens, 23 | # shown in window titles and about app dialogs on desktop. 24 | product = "warpscanner" 25 | 26 | # company name to display in about app dialogs 27 | company = "" 28 | 29 | # copyright text to display in about app dialogs 30 | copyright = "Copyright (C) 2025 by arshiacomplus" 31 | 32 | [tool.flet.app] 33 | path = "src" 34 | 35 | [tool.uv] 36 | dev-dependencies = [ 37 | "flet[all]==0.27.6", 38 | ] 39 | 40 | [tool.poetry] 41 | package-mode = false 42 | 43 | [tool.poetry.group.dev.dependencies] 44 | flet = {extras = ["all"], version = "0.27.6"} -------------------------------------------------------------------------------- /android-flet/v0.1.3/WarpScanner/src/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/70fdbdc27e57615e2b26fb66f6edb95026e3db8d/android-flet/v0.1.3/WarpScanner/src/assets/icon.png -------------------------------------------------------------------------------- /android-flet/v0.1.3/WarpScanner/src/assets/splash_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/70fdbdc27e57615e2b26fb66f6edb95026e3db8d/android-flet/v0.1.3/WarpScanner/src/assets/splash_android.png -------------------------------------------------------------------------------- /android-flet/v0.1.3/kotlin/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.arshiacomplus.warpscanner.warpscanner // << پکیج نیم شما 2 | 3 | import android.Manifest 4 | import android.content.pm.PackageManager 5 | import android.os.Build 6 | import android.os.Bundle 7 | import android.os.Environment 8 | import android.util.Log 9 | import androidx.annotation.NonNull 10 | import androidx.core.app.ActivityCompat 11 | import androidx.core.content.ContextCompat 12 | import io.flutter.embedding.android.FlutterActivity 13 | 14 | class MainActivity: FlutterActivity() { 15 | 16 | companion object { 17 | private const val TAG = "MainActivityKt" 18 | // کد درخواست یکسان برای مجوزهای قدیمی در همه نسخه‌ها 19 | private const val LEGACY_STORAGE_REQUEST_CODE = 101 20 | @JvmField 21 | var currentActivityInstance: MainActivity? = null 22 | } 23 | 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | currentActivityInstance = this 27 | Log.i(TAG, "onCreate: MainActivity instance created: $this") 28 | Log.i(TAG, "onCreate: Android SDK Version: ${Build.VERSION.SDK_INT}") 29 | 30 | checkAndRequestPermissions() 31 | } 32 | 33 | private fun checkAndRequestPermissions() { 34 | Log.d(TAG, "checkAndRequestPermissions: Starting permission check...") 35 | // --- بررسی و درخواست مجوز بر اساس نسخه اندروید --- 36 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 37 | // --- Android 11 (API 30) and higher --- 38 | Log.i(TAG, "checkAndRequestPermissions: Running on Android 11+.") 39 | try { 40 | val isManageStorageGranted = Environment.isExternalStorageManager() 41 | Log.d(TAG,"checkAndRequestPermissions: MANAGE_EXTERNAL_STORAGE granted? $isManageStorageGranted") 42 | 43 | if (!isManageStorageGranted) { 44 | Log.w(TAG, "[Permission Request] MANAGE_EXTERNAL_STORAGE not granted.") 45 | // 1. کاربر را به تنظیمات برای مجوز مدیریت فایل بفرست 46 | Log.i(TAG,"[Permission Request] Attempting to open settings for MANAGE_EXTERNAL_STORAGE via PermissionUtils...") 47 | try { 48 | PermissionUtils.requestManageAllFilesPermission(this, this) 49 | Log.i(TAG, "[Permission Request] Call to PermissionUtils.requestManageAllFilesPermission completed.") 50 | } catch (e: Exception) { 51 | Log.e(TAG, "[Permission Request] CRITICAL: Error calling PermissionUtils.requestManageAllFilesPermission", e) 52 | } 53 | 54 | // 2. (طبق درخواست جدید) همچنین مجوزهای قدیمی را هم درخواست بده (داخل try-catch) 55 | Log.i(TAG, "[Permission Request] Also checking and requesting legacy permissions on Android 11+ (as requested)...") 56 | checkAndRequestLegacyPermissions(" (on Android 11+)") // یک پسوند برای لاگ اضافه میکنیم 57 | 58 | } else { 59 | Log.i(TAG, "[Permission Check] MANAGE_EXTERNAL_STORAGE already granted. No further requests needed.") 60 | // وقتی مجوز کامل داریم، نیازی به درخواست قدیمی‌ها نیست. 61 | } 62 | } catch (e: Exception) { 63 | Log.e(TAG, "checkAndRequestPermissions: CRITICAL: Error checking or requesting MANAGE_EXTERNAL_STORAGE", e) 64 | } 65 | 66 | } else { 67 | // --- Android 10 (API 29) and lower --- 68 | Log.i(TAG, "checkAndRequestPermissions: Running on Android < 11.") 69 | // فقط مجوزهای قدیمی را چک و درخواست کن 70 | checkAndRequestLegacyPermissions(" (on Android < 11)") 71 | } 72 | Log.d(TAG, "checkAndRequestPermissions: Finished permission check logic.") 73 | } 74 | 75 | /** 76 | * متد کمکی برای چک و درخواست مجوزهای قدیمی READ/WRITE_EXTERNAL_STORAGE 77 | * @param logSuffix پسوندی برای اضافه کردن به لاگ‌ها جهت تشخیص سناریو 78 | */ 79 | private fun checkAndRequestLegacyPermissions(logSuffix: String = "") { 80 | val hasReadPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED 81 | // WRITE_EXTERNAL_STORAGE در اندروید 11+ رفتار متفاوتی دارد و ممکن است همیشه قابل دریافت نباشد 82 | val hasWritePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED 83 | Log.d(TAG, "checkAndRequestLegacyPermissions$logSuffix: READ granted: $hasReadPermission, WRITE granted: $hasWritePermission") 84 | 85 | val permissionsToRequest = mutableListOf() 86 | if (!hasReadPermission) permissionsToRequest.add(Manifest.permission.READ_EXTERNAL_STORAGE) 87 | if (!hasWritePermission) permissionsToRequest.add(Manifest.permission.WRITE_EXTERNAL_STORAGE) 88 | 89 | if (permissionsToRequest.isNotEmpty()) { 90 | Log.i(TAG, "[Permission Request] Need legacy permissions$logSuffix: ${permissionsToRequest.joinToString()}. Requesting...") 91 | // --- بلوک Try-Catch طبق درخواست --- 92 | try { 93 | ActivityCompat.requestPermissions( 94 | this, 95 | permissionsToRequest.toTypedArray(), // فقط مجوزهای لازم را درخواست بده 96 | LEGACY_STORAGE_REQUEST_CODE // کد برای شناسایی نتیجه 97 | ) 98 | Log.i(TAG, "[Permission Request] ActivityCompat.requestPermissions for legacy permissions called$logSuffix.") 99 | } catch (e: Exception) { 100 | // گرفتن Exception کلی برای اطمینان 101 | Log.e(TAG, "[Permission Request] CRITICAL: Error calling ActivityCompat.requestPermissions for legacy$logSuffix", e) 102 | } 103 | // ------------------------------------ 104 | } else { 105 | Log.i(TAG, "[Permission Check] Legacy storage permissions already granted$logSuffix.") 106 | } 107 | } 108 | 109 | // این متد برای دریافت نتیجه درخواست‌های *استاندارد* (مثل READ/WRITE) است 110 | override fun onRequestPermissionsResult( 111 | requestCode: Int, 112 | @NonNull permissions: Array, 113 | @NonNull grantResults: IntArray 114 | ) { 115 | Log.i(TAG, "onRequestPermissionsResult: Received result for requestCode: $requestCode") 116 | super.onRequestPermissionsResult(requestCode, permissions, grantResults) // فراخوانی والد مهمه 117 | 118 | // پردازش نتیجه برای کد درخواست مجوزهای قدیمی 119 | if (requestCode == LEGACY_STORAGE_REQUEST_CODE) { 120 | val grantedMap = permissions.zip(grantResults.toTypedArray()).toMap() 121 | Log.i(TAG, "onRequestPermissionsResult: Result for LEGACY_STORAGE_REQUEST_CODE. Results: $grantedMap") 122 | // می‌توانید نتایج دقیق‌تر را لاگ کنید 123 | grantedMap.forEach { (permission, grantResult) -> 124 | Log.d(TAG, " Permission: $permission, Granted: ${grantResult == PackageManager.PERMISSION_GRANTED}") 125 | } 126 | } 127 | 128 | // ارسال نتیجه به PermissionHelper (همیشه انجام شود) 129 | try { 130 | PermissionHelper.handleRequestPermissionsResult(requestCode, permissions, grantResults) 131 | Log.d(TAG, "onRequestPermissionsResult: Passed permission result to PermissionHelper.") 132 | } catch (e: Throwable) { 133 | Log.e(TAG, "onRequestPermissionsResult: CRITICAL: Error calling PermissionHelper.handleRequestPermissionsResult", e) 134 | } 135 | } 136 | 137 | // چک کردن نتیجه MANAGE_EXTERNAL_STORAGE در onResume (بدون تغییر) 138 | override fun onResume() { 139 | super.onResume() 140 | Log.i(TAG, "onResume: Activity resumed.") 141 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 142 | try { 143 | val hasManagePermission = Environment.isExternalStorageManager() 144 | Log.i(TAG, "onResume: [Permission Check] MANAGE_EXTERNAL_STORAGE Granted after returning: $hasManagePermission") 145 | } catch (e: Exception) { 146 | Log.e(TAG, "onResume: CRITICAL: Error checking MANAGE_EXTERNAL_STORAGE in onResume", e) 147 | } 148 | } 149 | } 150 | 151 | override fun onDestroy() { 152 | super.onDestroy() 153 | Log.i(TAG, "onDestroy: MainActivity instance being destroyed: $this") 154 | if (currentActivityInstance == this) { 155 | currentActivityInstance = null 156 | Log.i(TAG, "onDestroy: Static MainActivity instance reference cleared.") 157 | } 158 | } 159 | } -------------------------------------------------------------------------------- /android-flet/v0.1.3/kotlin/PermissionCallback.kt: -------------------------------------------------------------------------------- 1 | package com.arshiacomplus.warpscanner.warpscanner // << پکیج نیم شما 2 | 3 | interface PermissionCallback { 4 | fun onPermissionResult(allGranted: Boolean) 5 | } -------------------------------------------------------------------------------- /android-flet/v0.1.3/kotlin/PermissionHelper.kt: -------------------------------------------------------------------------------- 1 | package com.arshiacomplus.warpscanner.warpscanner // << پکیج نیم شما 2 | 3 | import android.app.Activity 4 | import android.content.pm.PackageManager 5 | import android.util.Log 6 | import androidx.core.app.ActivityCompat 7 | import androidx.core.content.ContextCompat 8 | import android.Manifest 9 | 10 | object PermissionHelper { // <<<< باید object یا class باشد، نه چیز دیگر 11 | 12 | private const val TAG = "PermissionHelperKt" 13 | private const val STORAGE_PERMISSION_REQUEST_CODE = 101 14 | 15 | @Volatile 16 | private var permissionCallback: PermissionCallback? = null 17 | 18 | @JvmStatic 19 | fun requestStoragePermission(activity: Activity, callback: PermissionCallback) { 20 | Log.d(TAG, "requestStoragePermission called from Python") 21 | this.permissionCallback = callback 22 | 23 | val permissionsToRequest = arrayOf( 24 | Manifest.permission.READ_EXTERNAL_STORAGE, 25 | Manifest.permission.WRITE_EXTERNAL_STORAGE 26 | ) 27 | 28 | val permissionsNeeded = permissionsToRequest.filter { 29 | ContextCompat.checkSelfPermission(activity, it) != PackageManager.PERMISSION_GRANTED 30 | }.toTypedArray() 31 | 32 | if (permissionsNeeded.isEmpty()) { 33 | Log.d(TAG, "All storage permissions already granted.") 34 | try { 35 | callback.onPermissionResult(true) 36 | } catch (t: Throwable) { 37 | Log.e(TAG, "Error calling callback.onPermissionResult (permissions already granted)", t) 38 | } finally { 39 | this.permissionCallback = null 40 | } 41 | } else { 42 | Log.d(TAG, "Requesting permissions: ${permissionsNeeded.joinToString()}") 43 | ActivityCompat.requestPermissions( 44 | activity, 45 | permissionsNeeded, 46 | STORAGE_PERMISSION_REQUEST_CODE 47 | ) 48 | } 49 | } 50 | 51 | @JvmStatic 52 | fun handleRequestPermissionsResult( 53 | requestCode: Int, 54 | permissions: Array, 55 | grantResults: IntArray 56 | ) { 57 | Log.d(TAG, "handleRequestPermissionsResult called from MainActivity for requestCode: $requestCode") 58 | if (requestCode == STORAGE_PERMISSION_REQUEST_CODE) { 59 | val callback = this.permissionCallback 60 | if (callback != null) { 61 | val allGranted = grantResults.isNotEmpty() && grantResults.all { it == PackageManager.PERMISSION_GRANTED } 62 | Log.d(TAG, "Permission result: allGranted = $allGranted") 63 | try { 64 | callback.onPermissionResult(allGranted) 65 | } catch (t: Throwable) { 66 | Log.e(TAG, "Error calling callback.onPermissionResult (after request)", t) 67 | } finally { 68 | this.permissionCallback = null 69 | Log.d(TAG, "Permission callback cleared.") 70 | } 71 | } else { 72 | Log.w(TAG, "permissionCallback was null when handling result.") 73 | } 74 | } else { 75 | Log.w(TAG, "Received result for unknown requestCode: $requestCode") 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /android-flet/v0.1.3/kotlin/PermissionUtils.kt: -------------------------------------------------------------------------------- 1 | package com.arshiacomplus.warpscanner.warpscanner // << پکیج نیم خودت رو اینجا بذار 2 | 3 | import android.Manifest 4 | import android.app.Activity 5 | import android.content.ActivityNotFoundException 6 | import android.content.Context 7 | import android.content.Intent 8 | import android.content.pm.PackageManager 9 | import android.net.Uri // لازم برای Intent جدیدتر در MANAGE_ALL_FILES 10 | import android.os.Build 11 | import android.os.Environment 12 | import android.provider.Settings 13 | import android.util.Log // برای لاگ‌گیری خطا 14 | import androidx.annotation.RequiresApi // برای نشان دادن نیاز به نسخه API 15 | import androidx.core.app.ActivityCompat 16 | import androidx.core.content.ContextCompat 17 | 18 | /** 19 | * کلاس کمکی برای مدیریت درخواست‌های مجوز در اندروید. 20 | * از object به جای class استفاده شده چون فقط شامل متدهای استاتیک-مانند است. 21 | */ 22 | object PermissionUtils { 23 | 24 | private const val TAG = "PermissionUtilsKt" // تگ برای لاگ 25 | private const val STORAGE_PERMISSION_REQUEST_CODE = 101 // کد دلخواه برای درخواست Storage 26 | 27 | /** 28 | * مجوزهای استاندارد خواندن و نوشتن حافظه خارجی را درخواست می‌کند. 29 | * (READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE) 30 | * این متد برای اندروید 10 به پایین حیاتی است و در نسخه‌های بالاتر هم ممکن است لازم باشد. 31 | * 32 | * @param activity اکتیویتی فعلی که درخواست از آن ارسال می‌شود. 33 | */ 34 | @JvmStatic // این انوتیشن اجازه می‌دهد متد مانند یک متد استاتیک جاوا فراخوانی شود 35 | fun requestStoragePermissions(activity: Activity) { 36 | // بررسی اینکه آیا مجوزها قبلاً داده شده‌اند یا نه 37 | val readPermissionGranted = ContextCompat.checkSelfPermission( 38 | activity, Manifest.permission.READ_EXTERNAL_STORAGE 39 | ) == PackageManager.PERMISSION_GRANTED 40 | 41 | val writePermissionGranted = ContextCompat.checkSelfPermission( 42 | activity, Manifest.permission.WRITE_EXTERNAL_STORAGE 43 | ) == PackageManager.PERMISSION_GRANTED 44 | 45 | // اگر هر کدام از مجوزها داده نشده باشند 46 | if (!readPermissionGranted || !writePermissionGranted) { 47 | Log.d(TAG, "Requesting standard storage permissions (READ/WRITE)...") 48 | // لیست مجوزهایی که باید درخواست شوند 49 | val permissionsToRequest = arrayOf( 50 | Manifest.permission.READ_EXTERNAL_STORAGE, 51 | Manifest.permission.WRITE_EXTERNAL_STORAGE 52 | ) 53 | // نمایش دیالوگ سیستمی درخواست مجوز 54 | ActivityCompat.requestPermissions( 55 | activity, 56 | permissionsToRequest, 57 | STORAGE_PERMISSION_REQUEST_CODE // کد برای شناسایی نتیجه در onRequestPermissionsResult 58 | ) 59 | } else { 60 | Log.d(TAG, "Standard storage permissions (READ/WRITE) already granted.") 61 | // اگر مجوزها از قبل داده شده‌اند، کاری انجام نمی‌دهیم 62 | // (می‌توان یک callback برای اطلاع‌رسانی داشت) 63 | } 64 | } 65 | 66 | /** 67 | * درخواست مجوز MANAGE_EXTERNAL_STORAGE برای اندروید 11 (API 30) و بالاتر. 68 | * این مجوز دسترسی کامل به حافظه خارجی می‌دهد اما نیاز به بررسی دقیق در گوگل پلی دارد. 69 | * این متد کاربر را به صفحه تنظیمات سیستم هدایت می‌کند. 70 | * 71 | * @param context کانتکست برنامه (می‌تواند Activity یا Application Context باشد). 72 | * @param activity (اختیاری) اگر می‌خواهید از startActivityForResult استفاده کنید (در اینجا لازم نیست). 73 | */ 74 | @JvmStatic 75 | @RequiresApi(Build.VERSION_CODES.R) // این متد فقط برای اندروید 11 و بالاتر معنی دارد 76 | fun requestManageAllFilesPermission(context: Context, activity: Activity? = null) { 77 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 78 | // بررسی اینکه آیا برنامه در حال حاضر مجوز MANAGE_EXTERNAL_STORAGE را دارد؟ 79 | if (!Environment.isExternalStorageManager()) { 80 | Log.d(TAG, "Requesting MANAGE_EXTERNAL_STORAGE permission by opening settings...") 81 | try { 82 | // ایجاد Intent برای باز کردن صفحه تنظیمات مدیریت فایل‌ها 83 | val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) 84 | // اضافه کردن پکیج نیم برنامه به URI تا مستقیماً به تنظیمات همین برنامه برود 85 | intent.data = Uri.parse("package:${context.packageName}") 86 | 87 | // اگر activity داریم، از آن برای شروع استفاده می‌کنیم (بهتر است) 88 | if (activity != null) { 89 | activity.startActivity(intent) // می‌توان از startActivityForResult هم استفاده کرد اگر نیاز به نتیجه فوری باشد 90 | } else { 91 | // اگر فقط context داریم (مثلاً از سرویس)، فلگ NEW_TASK لازم است 92 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 93 | context.startActivity(intent) 94 | } 95 | Log.d(TAG, "Settings activity for MANAGE_ALL_FILES opened.") 96 | // توجه: نتیجه این درخواست مستقیماً به برنامه برنمی‌گردد. 97 | // باید دوباره Environment.isExternalStorageManager() را چک کنید 98 | // وقتی کاربر به برنامه برمی‌گردد (مثلاً در onResume اکتیویتی). 99 | 100 | } catch (e: ActivityNotFoundException) { 101 | // اگر صفحه تنظیمات مربوطه در دستگاه وجود نداشت (بسیار نادر) 102 | Log.e(TAG, "Activity not found to handle MANAGE_ALL_FILES_ACCESS_PERMISSION intent.", e) 103 | // می‌توانید یک Intent عمومی‌تر برای تنظیمات برنامه امتحان کنید 104 | // val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) 105 | // intent.data = Uri.parse("package:${context.packageName}") 106 | // context.startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) 107 | } catch(t: Throwable) { 108 | Log.e(TAG, "Error opening MANAGE_ALL_FILES settings", t) 109 | } 110 | } else { 111 | Log.d(TAG, "MANAGE_EXTERNAL_STORAGE permission already granted.") 112 | // اگر مجوز از قبل داده شده، کاری انجام نمی‌دهیم 113 | } 114 | } else { 115 | // برای نسخه‌های پایین‌تر از اندروید 11، این مجوز وجود ندارد 116 | Log.w(TAG, "MANAGE_EXTERNAL_STORAGE permission is not applicable below Android 11 (API 30).") 117 | } 118 | } 119 | 120 | // --- متدهای کمکی دیگر (می‌توانید اضافه کنید) --- 121 | 122 | /** 123 | * بررسی می‌کند آیا مجوزهای استاندارد ذخیره‌سازی داده شده‌اند یا نه. 124 | */ 125 | @JvmStatic 126 | fun hasStoragePermissions(context: Context): Boolean { 127 | val readPermission = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) 128 | val writePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) 129 | return readPermission == PackageManager.PERMISSION_GRANTED && writePermission == PackageManager.PERMISSION_GRANTED 130 | } 131 | 132 | /** 133 | * بررسی می‌کند آیا مجوز مدیریت تمام فایل‌ها داده شده است (فقط اندروید ۱۱ به بالا). 134 | */ 135 | @JvmStatic 136 | @RequiresApi(Build.VERSION_CODES.R) 137 | fun hasManageAllFilesPermission(): Boolean { 138 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 139 | Environment.isExternalStorageManager() 140 | } else { 141 | // در نسخه‌های پایین‌تر، این مجوز وجود ندارد، پس true برمی‌گردانیم؟ 142 | // یا false چون مفهوم ندارد؟ بستگی به منطق برنامه دارد. 143 | // معمولا true مناسب‌تر است چون محدودیت وجود ندارد. 144 | true // یا false بر اساس نیاز 145 | } 146 | } 147 | 148 | } -------------------------------------------------------------------------------- /android-toga/v0.0.3/java/.MainActivity.java.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/70fdbdc27e57615e2b26fb66f6edb95026e3db8d/android-toga/v0.0.3/java/.MainActivity.java.swp -------------------------------------------------------------------------------- /android-toga/v0.0.3/java/DrawHandlerView.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | public class DrawHandlerView extends android.view.View { 4 | private IDrawHandler drawHandler = null; 5 | 6 | public DrawHandlerView(android.content.Context context) { 7 | super(context); 8 | } 9 | 10 | public void setDrawHandler(IDrawHandler drawHandler) { 11 | this.drawHandler = drawHandler; 12 | } 13 | 14 | public void onDraw(android.graphics.Canvas canvas) { 15 | super.onDraw(canvas); 16 | drawHandler.handleDraw(canvas); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android-toga/v0.0.3/java/IDrawHandler.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | public interface IDrawHandler { 4 | public void handleDraw(android.graphics.Canvas canvas); 5 | } 6 | -------------------------------------------------------------------------------- /android-toga/v0.0.3/java/IPythonApp.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | 8 | public interface IPythonApp { 9 | void onCreate(); 10 | void onResume(); 11 | void onStart(); 12 | void onActivityResult(int requestCode, int resultCode, Intent data); 13 | void onConfigurationChanged(Configuration newConfig); 14 | boolean onOptionsItemSelected(MenuItem menuitem); 15 | boolean onPrepareOptionsMenu(Menu menu); 16 | // There's no need to add any more methods to this interface, as the Python 17 | // call mechanism no longer uses it. 18 | } 19 | -------------------------------------------------------------------------------- /android-toga/v0.0.3/java/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | import android.widget.LinearLayout; 10 | 11 | import androidx.appcompat.app.AppCompatActivity; 12 | 13 | import com.chaquo.python.Kwarg; 14 | import com.chaquo.python.PyException; 15 | import com.chaquo.python.PyObject; 16 | import com.chaquo.python.Python; 17 | import com.chaquo.python.android.AndroidPlatform; 18 | 19 | import java.util.List; 20 | 21 | import org.json.JSONArray; 22 | import org.json.JSONException; 23 | 24 | import com.arshiacomplus.warpscanner.warpscanner.R; 25 | 26 | 27 | public class MainActivity extends AppCompatActivity { 28 | 29 | 30 | 31 | // To profile app launch, use `adb -s MainActivity`; look for "onCreate() start" and "onResume() completed". 32 | private String TAG = "MainActivity"; 33 | private static PyObject pythonApp; 34 | 35 | 36 | 37 | /** 38 | * This method is called by `app.__main__` over JNI in Python when the BeeWare 39 | * app launches. 40 | * 41 | * @param app 42 | */ 43 | @SuppressWarnings("unused") 44 | public static void setPythonApp(IPythonApp app) { 45 | pythonApp = PyObject.fromJava(app); 46 | } 47 | 48 | /** 49 | * We store the MainActivity instance on the *class* so that we can easily 50 | * access it from Python. 51 | */ 52 | public static MainActivity singletonThis; 53 | 54 | protected void onCreate(Bundle savedInstanceState) { 55 | Log.d(TAG, "onCreate() start"); 56 | // Change away from the splash screen theme to the app theme. 57 | setTheme(R.style.AppTheme); 58 | super.onCreate(savedInstanceState); 59 | LinearLayout layout = new LinearLayout(this); 60 | this.setContentView(layout); 61 | singletonThis = this; 62 | 63 | 64 | 65 | 66 | // Call PermissionUtils to request permissions 67 | PermissionUtils.requestStoragePermissions(this); 68 | 69 | 70 | Python py; 71 | if (Python.isStarted()) { 72 | Log.d(TAG, "Python already started"); 73 | py = Python.getInstance(); 74 | } else { 75 | Log.d(TAG, "Starting Python"); 76 | AndroidPlatform platform = new AndroidPlatform(this); 77 | platform.redirectStdioToLogcat(); 78 | Python.start(platform); 79 | py = Python.getInstance(); 80 | 81 | String argvStr = getIntent().getStringExtra("org.beeware.ARGV"); 82 | if (argvStr != null) { 83 | try { 84 | JSONArray argvJson = new JSONArray(argvStr); 85 | List sysArgv = py.getModule("sys").get("argv").asList(); 86 | for (int i = 0; i < argvJson.length(); i++) { 87 | sysArgv.add(PyObject.fromJava(argvJson.getString(i))); 88 | } 89 | } catch (JSONException e) { 90 | throw new RuntimeException(e); 91 | } 92 | } 93 | } 94 | 95 | Log.d(TAG, "Running main module " + getString(R.string.main_module)); 96 | py.getModule("runpy").callAttr( 97 | "run_module", 98 | getString(R.string.main_module), 99 | new Kwarg("run_name", "__main__"), 100 | new Kwarg("alter_sys", true) 101 | ); 102 | 103 | userCode("onCreate"); 104 | Log.d(TAG, "onCreate() complete"); 105 | } 106 | 107 | protected void onStart() { 108 | Log.d(TAG, "onStart() start"); 109 | super.onStart(); 110 | userCode("onStart"); 111 | Log.d(TAG, "onStart() complete"); 112 | } 113 | 114 | protected void onResume() { 115 | Log.d(TAG, "onResume() start"); 116 | super.onResume(); 117 | userCode("onResume"); 118 | Log.d(TAG, "onResume() complete"); 119 | } 120 | 121 | protected void onPause() { 122 | Log.d(TAG, "onPause() start"); 123 | super.onPause(); 124 | userCode("onPause"); 125 | Log.d(TAG, "onPause() complete"); 126 | } 127 | 128 | protected void onStop() { 129 | Log.d(TAG, "onStop() start"); 130 | super.onStop(); 131 | userCode("onStop"); 132 | Log.d(TAG, "onStop() complete"); 133 | } 134 | protected void onDestroy() { 135 | Log.d(TAG, "onDestroy() start"); 136 | super.onDestroy(); 137 | userCode("onDestroy"); 138 | Log.d(TAG, "onDestroy() complete"); 139 | } 140 | protected void onRestart() { 141 | Log.d(TAG, "onRestart() start"); 142 | super.onRestart(); 143 | userCode("onRestart"); 144 | Log.d(TAG, "onRestart() complete"); 145 | } 146 | public void onTopResumedActivityChanged (boolean isTopResumedActivity){ 147 | Log.d(TAG, "onTopResumedActivityChanged() start"); 148 | super.onTopResumedActivityChanged(isTopResumedActivity); 149 | userCode("onTopResumedActivityChanged", isTopResumedActivity); 150 | Log.d(TAG, "onTopResumedActivityChanged() complete"); 151 | } 152 | 153 | protected void onActivityResult(int requestCode, int resultCode, Intent data) 154 | { 155 | Log.d(TAG, "onActivityResult() start"); 156 | super.onActivityResult(requestCode, resultCode, data); 157 | userCode("onActivityResult", requestCode, resultCode, data); 158 | Log.d(TAG, "onActivityResult() complete"); 159 | } 160 | 161 | public void onConfigurationChanged(Configuration newConfig) { 162 | Log.d(TAG, "onConfigurationChanged() start"); 163 | super.onConfigurationChanged(newConfig); 164 | userCode("onConfigurationChanged", newConfig); 165 | Log.d(TAG, "onConfigurationChanged() complete"); 166 | } 167 | 168 | public boolean onOptionsItemSelected(MenuItem menuitem) { 169 | Log.d(TAG, "onOptionsItemSelected() start"); 170 | PyObject pyResult = userCode("onOptionsItemSelected", menuitem); 171 | boolean result = (pyResult == null) ? false : pyResult.toBoolean(); 172 | Log.d(TAG, "onOptionsItemSelected() complete"); 173 | return result; 174 | } 175 | 176 | public boolean onPrepareOptionsMenu(Menu menu) { 177 | Log.d(TAG, "onPrepareOptionsMenu() start"); 178 | PyObject pyResult = userCode("onPrepareOptionsMenu", menu); 179 | boolean result = (pyResult == null) ? false : pyResult.toBoolean(); 180 | Log.d(TAG, "onPrepareOptionsMenu() complete"); 181 | return result; 182 | } 183 | 184 | public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 185 | { 186 | Log.d(TAG, "onRequestPermissionsResult() start"); 187 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 188 | userCode("onRequestPermissionsResult", requestCode, permissions, grantResults); 189 | Log.d(TAG, "onRequestPermissionsResult() complete"); 190 | } 191 | 192 | private PyObject userCode(String methodName, Object... args) { 193 | if (pythonApp == null) { 194 | // Could be a non-graphical app such as Python-support-testbed. 195 | return null; 196 | } 197 | try { 198 | if (pythonApp.containsKey(methodName)) { 199 | return pythonApp.callAttr(methodName, args); 200 | } else { 201 | // Handle the case where the method doesn't exist 202 | return null; 203 | } 204 | } catch (PyException e) { 205 | if (e.getMessage().startsWith("NotImplementedError")) { 206 | return null; 207 | } 208 | throw e; 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /android-toga/v0.0.3/java/PermissionUtils.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.pm.PackageManager; 6 | import androidx.core.app.ActivityCompat; 7 | import androidx.core.content.ContextCompat; 8 | 9 | public class PermissionUtils { 10 | public static void requestStoragePermissions(Activity activity) { 11 | if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || 12 | ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 13 | 14 | ActivityCompat.requestPermissions(activity, new String[]{ 15 | Manifest.permission.READ_EXTERNAL_STORAGE, 16 | Manifest.permission.WRITE_EXTERNAL_STORAGE 17 | }, 1); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /android-toga/v0.0.4/java/.MainActivity.java.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/70fdbdc27e57615e2b26fb66f6edb95026e3db8d/android-toga/v0.0.4/java/.MainActivity.java.swp -------------------------------------------------------------------------------- /android-toga/v0.0.4/java/DrawHandlerView.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | public class DrawHandlerView extends android.view.View { 4 | private IDrawHandler drawHandler = null; 5 | 6 | public DrawHandlerView(android.content.Context context) { 7 | super(context); 8 | } 9 | 10 | public void setDrawHandler(IDrawHandler drawHandler) { 11 | this.drawHandler = drawHandler; 12 | } 13 | 14 | public void onDraw(android.graphics.Canvas canvas) { 15 | super.onDraw(canvas); 16 | drawHandler.handleDraw(canvas); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android-toga/v0.0.4/java/IDrawHandler.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | public interface IDrawHandler { 4 | public void handleDraw(android.graphics.Canvas canvas); 5 | } 6 | -------------------------------------------------------------------------------- /android-toga/v0.0.4/java/IPythonApp.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | 8 | public interface IPythonApp { 9 | void onCreate(); 10 | void onResume(); 11 | void onStart(); 12 | void onActivityResult(int requestCode, int resultCode, Intent data); 13 | void onConfigurationChanged(Configuration newConfig); 14 | boolean onOptionsItemSelected(MenuItem menuitem); 15 | boolean onPrepareOptionsMenu(Menu menu); 16 | // There's no need to add any more methods to this interface, as the Python 17 | // call mechanism no longer uses it. 18 | } 19 | -------------------------------------------------------------------------------- /android-toga/v0.0.4/java/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | import android.widget.LinearLayout; 10 | 11 | import androidx.appcompat.app.AppCompatActivity; 12 | 13 | import com.chaquo.python.Kwarg; 14 | import com.chaquo.python.PyException; 15 | import com.chaquo.python.PyObject; 16 | import com.chaquo.python.Python; 17 | import com.chaquo.python.android.AndroidPlatform; 18 | 19 | import java.util.List; 20 | 21 | import org.json.JSONArray; 22 | import org.json.JSONException; 23 | 24 | import com.arshiacomplus.warpscanner.warpscanner.R; 25 | 26 | import android.app.Activity; 27 | 28 | import android.os.StrictMode; 29 | 30 | public class MainActivity extends AppCompatActivity { 31 | 32 | 33 | 34 | // To profile app launch, use `adb -s MainActivity`; look for "onCreate() start" and "onResume() completed". 35 | private String TAG = "MainActivity"; 36 | private static PyObject pythonApp; 37 | 38 | 39 | 40 | /** 41 | * This method is called by `app.__main__` over JNI in Python when the BeeWare 42 | * app launches. 43 | * 44 | * @param app 45 | */ 46 | @SuppressWarnings("unused") 47 | public static void setPythonApp(IPythonApp app) { 48 | pythonApp = PyObject.fromJava(app); 49 | } 50 | 51 | /** 52 | * We store the MainActivity instance on the *class* so that we can easily 53 | * access it from Python. 54 | */ 55 | public static MainActivity singletonThis; 56 | 57 | protected void onCreate(Bundle savedInstanceState) { 58 | Log.d(TAG, "onCreate() start"); 59 | // Change away from the splash screen theme to the app theme. 60 | setTheme(R.style.AppTheme); 61 | super.onCreate(savedInstanceState); 62 | LinearLayout layout = new LinearLayout(this); 63 | this.setContentView(layout); 64 | singletonThis = this; 65 | 66 | 67 | 68 | 69 | // Call PermissionUtils to request permissions 70 | PermissionUtils.requestStoragePermissions(this); 71 | 72 | 73 | StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); 74 | StrictMode.setThreadPolicy(policy); 75 | 76 | // فراخوانی بررسی نسخه 77 | VersionChecker.checkVersion(this); 78 | 79 | Python py; 80 | if (Python.isStarted()) { 81 | Log.d(TAG, "Python already started"); 82 | py = Python.getInstance(); 83 | } else { 84 | Log.d(TAG, "Starting Python"); 85 | AndroidPlatform platform = new AndroidPlatform(this); 86 | platform.redirectStdioToLogcat(); 87 | Python.start(platform); 88 | py = Python.getInstance(); 89 | 90 | String argvStr = getIntent().getStringExtra("org.beeware.ARGV"); 91 | if (argvStr != null) { 92 | try { 93 | JSONArray argvJson = new JSONArray(argvStr); 94 | List sysArgv = py.getModule("sys").get("argv").asList(); 95 | for (int i = 0; i < argvJson.length(); i++) { 96 | sysArgv.add(PyObject.fromJava(argvJson.getString(i))); 97 | } 98 | } catch (JSONException e) { 99 | throw new RuntimeException(e); 100 | } 101 | } 102 | } 103 | 104 | Log.d(TAG, "Running main module " + getString(R.string.main_module)); 105 | py.getModule("runpy").callAttr( 106 | "run_module", 107 | getString(R.string.main_module), 108 | new Kwarg("run_name", "__main__"), 109 | new Kwarg("alter_sys", true) 110 | ); 111 | 112 | userCode("onCreate"); 113 | Log.d(TAG, "onCreate() complete"); 114 | } 115 | 116 | protected void onStart() { 117 | Log.d(TAG, "onStart() start"); 118 | super.onStart(); 119 | userCode("onStart"); 120 | Log.d(TAG, "onStart() complete"); 121 | } 122 | 123 | protected void onResume() { 124 | Log.d(TAG, "onResume() start"); 125 | super.onResume(); 126 | userCode("onResume"); 127 | Log.d(TAG, "onResume() complete"); 128 | } 129 | 130 | protected void onPause() { 131 | Log.d(TAG, "onPause() start"); 132 | super.onPause(); 133 | userCode("onPause"); 134 | Log.d(TAG, "onPause() complete"); 135 | } 136 | 137 | protected void onStop() { 138 | Log.d(TAG, "onStop() start"); 139 | super.onStop(); 140 | userCode("onStop"); 141 | Log.d(TAG, "onStop() complete"); 142 | } 143 | protected void onDestroy() { 144 | Log.d(TAG, "onDestroy() start"); 145 | super.onDestroy(); 146 | userCode("onDestroy"); 147 | Log.d(TAG, "onDestroy() complete"); 148 | } 149 | protected void onRestart() { 150 | Log.d(TAG, "onRestart() start"); 151 | super.onRestart(); 152 | userCode("onRestart"); 153 | Log.d(TAG, "onRestart() complete"); 154 | } 155 | public void onTopResumedActivityChanged (boolean isTopResumedActivity){ 156 | Log.d(TAG, "onTopResumedActivityChanged() start"); 157 | super.onTopResumedActivityChanged(isTopResumedActivity); 158 | userCode("onTopResumedActivityChanged", isTopResumedActivity); 159 | Log.d(TAG, "onTopResumedActivityChanged() complete"); 160 | } 161 | 162 | protected void onActivityResult(int requestCode, int resultCode, Intent data) 163 | { 164 | Log.d(TAG, "onActivityResult() start"); 165 | super.onActivityResult(requestCode, resultCode, data); 166 | userCode("onActivityResult", requestCode, resultCode, data); 167 | Log.d(TAG, "onActivityResult() complete"); 168 | } 169 | 170 | public void onConfigurationChanged(Configuration newConfig) { 171 | Log.d(TAG, "onConfigurationChanged() start"); 172 | super.onConfigurationChanged(newConfig); 173 | userCode("onConfigurationChanged", newConfig); 174 | Log.d(TAG, "onConfigurationChanged() complete"); 175 | } 176 | 177 | public boolean onOptionsItemSelected(MenuItem menuitem) { 178 | Log.d(TAG, "onOptionsItemSelected() start"); 179 | PyObject pyResult = userCode("onOptionsItemSelected", menuitem); 180 | boolean result = (pyResult == null) ? false : pyResult.toBoolean(); 181 | Log.d(TAG, "onOptionsItemSelected() complete"); 182 | return result; 183 | } 184 | 185 | public boolean onPrepareOptionsMenu(Menu menu) { 186 | Log.d(TAG, "onPrepareOptionsMenu() start"); 187 | PyObject pyResult = userCode("onPrepareOptionsMenu", menu); 188 | boolean result = (pyResult == null) ? false : pyResult.toBoolean(); 189 | Log.d(TAG, "onPrepareOptionsMenu() complete"); 190 | return result; 191 | } 192 | 193 | public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 194 | { 195 | Log.d(TAG, "onRequestPermissionsResult() start"); 196 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 197 | userCode("onRequestPermissionsResult", requestCode, permissions, grantResults); 198 | Log.d(TAG, "onRequestPermissionsResult() complete"); 199 | } 200 | 201 | private PyObject userCode(String methodName, Object... args) { 202 | if (pythonApp == null) { 203 | // Could be a non-graphical app such as Python-support-testbed. 204 | return null; 205 | } 206 | try { 207 | if (pythonApp.containsKey(methodName)) { 208 | return pythonApp.callAttr(methodName, args); 209 | } else { 210 | // Handle the case where the method doesn't exist 211 | return null; 212 | } 213 | } catch (PyException e) { 214 | if (e.getMessage().startsWith("NotImplementedError")) { 215 | return null; 216 | } 217 | throw e; 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /android-toga/v0.0.4/java/PermissionUtils.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.pm.PackageManager; 6 | import androidx.core.app.ActivityCompat; 7 | import androidx.core.content.ContextCompat; 8 | 9 | public class PermissionUtils { 10 | public static void requestStoragePermissions(Activity activity) { 11 | if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || 12 | ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 13 | 14 | ActivityCompat.requestPermissions(activity, new String[]{ 15 | Manifest.permission.READ_EXTERNAL_STORAGE, 16 | Manifest.permission.WRITE_EXTERNAL_STORAGE 17 | }, 1); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /android-toga/v0.0.4/java/VersionChecker.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.content.DialogInterface; 6 | import android.content.Intent; 7 | import android.net.Uri; 8 | import android.os.Handler; 9 | import android.os.Looper; 10 | 11 | import java.io.BufferedReader; 12 | import java.io.InputStreamReader; 13 | import java.net.HttpURLConnection; 14 | import java.net.URL; 15 | 16 | public class VersionChecker { 17 | 18 | public static void checkVersion(Activity activity) { 19 | new Thread(new Runnable() { 20 | @Override 21 | public void run() { 22 | String link = "https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/refs/heads/main/android-toga/version.txt"; 23 | String content = getContentFromURL(link); 24 | 25 | if (!"v0.0.4".equals(content.trim())) { 26 | // نمایش دیالوگ در نخ اصلی 27 | new Handler(Looper.getMainLooper()).post(new Runnable() { 28 | @Override 29 | public void run() { 30 | showUpdateDialog(activity); 31 | } 32 | }); 33 | } 34 | } 35 | }).start(); 36 | } 37 | 38 | private static String getContentFromURL(String link) { 39 | StringBuilder content = new StringBuilder(); 40 | try { 41 | URL url = new URL(link); 42 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 43 | connection.setRequestMethod("GET"); 44 | 45 | BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); 46 | String inputLine; 47 | 48 | while ((inputLine = in.readLine()) != null) { 49 | content.append(inputLine); 50 | } 51 | in.close(); 52 | } catch (Exception e) { 53 | e.printStackTrace(); 54 | } 55 | return content.toString(); 56 | } 57 | 58 | private static void showUpdateDialog(Activity activity) { 59 | AlertDialog.Builder builder = new AlertDialog.Builder(activity); 60 | builder.setMessage("بروزرسانی در دسترس است") 61 | .setCancelable(false) 62 | .setPositiveButton("برو", new DialogInterface.OnClickListener() { 63 | public void onClick(DialogInterface dialog, int id) { 64 | Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/arshiacomplus/WarpScanner-android-GUI/")); 65 | activity.startActivity(browserIntent); 66 | } 67 | }) 68 | .setNegativeButton("بستن", new DialogInterface.OnClickListener() { 69 | public void onClick(DialogInterface dialog, int id) { 70 | dialog.dismiss(); 71 | } 72 | }); 73 | AlertDialog alert = builder.create(); 74 | alert.show(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /android-toga/v0.0.4/java/tt: -------------------------------------------------------------------------------- 1 | tt 2 | -------------------------------------------------------------------------------- /android-toga/v0.0.5/java/.MainActivity.java.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/70fdbdc27e57615e2b26fb66f6edb95026e3db8d/android-toga/v0.0.5/java/.MainActivity.java.swp -------------------------------------------------------------------------------- /android-toga/v0.0.5/java/DrawHandlerView.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | public class DrawHandlerView extends android.view.View { 4 | private IDrawHandler drawHandler = null; 5 | 6 | public DrawHandlerView(android.content.Context context) { 7 | super(context); 8 | } 9 | 10 | public void setDrawHandler(IDrawHandler drawHandler) { 11 | this.drawHandler = drawHandler; 12 | } 13 | 14 | public void onDraw(android.graphics.Canvas canvas) { 15 | super.onDraw(canvas); 16 | drawHandler.handleDraw(canvas); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android-toga/v0.0.5/java/IDrawHandler.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | public interface IDrawHandler { 4 | public void handleDraw(android.graphics.Canvas canvas); 5 | } 6 | -------------------------------------------------------------------------------- /android-toga/v0.0.5/java/IPythonApp.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | 8 | public interface IPythonApp { 9 | void onCreate(); 10 | void onResume(); 11 | void onStart(); 12 | void onActivityResult(int requestCode, int resultCode, Intent data); 13 | void onConfigurationChanged(Configuration newConfig); 14 | boolean onOptionsItemSelected(MenuItem menuitem); 15 | boolean onPrepareOptionsMenu(Menu menu); 16 | // There's no need to add any more methods to this interface, as the Python 17 | // call mechanism no longer uses it. 18 | } 19 | -------------------------------------------------------------------------------- /android-toga/v0.0.5/java/LTRConfig.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.app.Activity; 4 | import android.os.Build; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.LinearLayout; 8 | 9 | public class LTRConfig { 10 | 11 | public static void applyLTR(Activity activity) { 12 | ViewGroup rootView = (ViewGroup) activity.findViewById(android.R.id.content); 13 | setLTR(rootView); 14 | } 15 | 16 | private static void setLTR(ViewGroup viewGroup) { 17 | for (int i = 0; i < viewGroup.getChildCount(); i++) { 18 | View view = viewGroup.getChildAt(i); 19 | if (view instanceof ViewGroup) { 20 | setLTR((ViewGroup) view); 21 | } 22 | } 23 | 24 | // اگر نسخه اندروید بالاتر از نوقا است، جهت LTR را تنظیم می‌کنیم 25 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 26 | viewGroup.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android-toga/v0.0.5/java/LoggingConfig.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.util.logging.FileHandler; 8 | import java.util.logging.Handler; 9 | import java.util.logging.Level; 10 | import java.util.logging.Logger; 11 | import java.util.logging.SimpleFormatter; 12 | 13 | public class LoggingConfig { 14 | 15 | public static void setup(Context context) { 16 | String logPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/wwarpscanner/app_java.txt"; 17 | File logFile = new File(logPath); 18 | 19 | if (logFile.exists()) { 20 | logFile.delete(); 21 | } 22 | 23 | try { 24 | if (logFile.createNewFile()) { 25 | Handler fileHandler = new FileHandler(logPath, true); 26 | fileHandler.setFormatter(new SimpleFormatter()); 27 | Logger logger = Logger.getLogger(context.getPackageName()); 28 | logger.addHandler(fileHandler); 29 | logger.setLevel(Level.ALL); 30 | Logger.getLogger("").addHandler(fileHandler); // Add handler to root logger 31 | } 32 | } catch (IOException e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /android-toga/v0.0.5/java/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | import android.widget.LinearLayout; 10 | import android.content.Context; 11 | 12 | import androidx.appcompat.app.AppCompatActivity; 13 | 14 | import com.chaquo.python.Kwarg; 15 | import com.chaquo.python.PyException; 16 | import com.chaquo.python.PyObject; 17 | import com.chaquo.python.Python; 18 | import com.chaquo.python.android.AndroidPlatform; 19 | 20 | import java.util.List; 21 | import java.util.logging.Logger; 22 | 23 | import org.json.JSONArray; 24 | import org.json.JSONException; 25 | 26 | import com.arshiacomplus.warpscanner.warpscanner.R; 27 | 28 | import android.app.Activity; 29 | 30 | import android.os.StrictMode; 31 | 32 | public class MainActivity extends AppCompatActivity { 33 | 34 | 35 | 36 | // To profile app launch, use `adb -s MainActivity`; look for "onCreate() start" and "onResume() completed". 37 | private String TAG = "MainActivity"; 38 | private static PyObject pythonApp; 39 | private static Logger logger; 40 | 41 | 42 | 43 | /** 44 | * This method is called by `app.__main__` over JNI in Python when the BeeWare 45 | * app launches. 46 | * 47 | * @param app 48 | */ 49 | @SuppressWarnings("unused") 50 | public static void setPythonApp(IPythonApp app) { 51 | 52 | pythonApp = PyObject.fromJava(app); 53 | 54 | } 55 | 56 | /** 57 | * We store the MainActivity instance on the *class* so that we can easily 58 | * access it from Python. 59 | */ 60 | public static MainActivity singletonThis; 61 | 62 | protected void onCreate(Bundle savedInstanceState) { 63 | Log.d(TAG, "onCreate() start"); 64 | // Change away from the splash screen theme to the app theme. 65 | setTheme(R.style.AppTheme); 66 | super.onCreate(savedInstanceState); 67 | LinearLayout layout = new LinearLayout(this); 68 | this.setContentView(layout); 69 | singletonThis = this; 70 | 71 | StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); 72 | StrictMode.setThreadPolicy(policy); 73 | 74 | PermissionUtils.requestStoragePermissions(this); 75 | 76 | Context appContext = getApplicationContext(); 77 | LoggingConfig.setup(appContext); 78 | logger = Logger.getLogger(appContext.getPackageName()); 79 | 80 | // ثبت UncaughtExceptionHandler 81 | Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 82 | @Override 83 | public void uncaughtException(Thread t, Throwable e) { 84 | logger.severe("Uncaught exception: " + e.getMessage()); 85 | for (StackTraceElement element : e.getStackTrace()) { 86 | logger.severe(element.toString()); 87 | } 88 | } 89 | }); 90 | 91 | logger.info("Application started"); 92 | 93 | 94 | 95 | 96 | 97 | // Call PermissionUtils to request permissions 98 | 99 | 100 | 101 | LTRConfig.applyLTR(this); 102 | // فراخوانی بررسی نسخه 103 | VersionChecker.checkVersion(this); 104 | 105 | 106 | // اکنون می‌توانید لاگ بگیرید 107 | Logger logger = Logger.getLogger(getPackageName()); 108 | logger.info("Application started"); 109 | 110 | 111 | Python py; 112 | if (Python.isStarted()) { 113 | Log.d(TAG, "Python already started"); 114 | py = Python.getInstance(); 115 | } else { 116 | Log.d(TAG, "Starting Python"); 117 | AndroidPlatform platform = new AndroidPlatform(this); 118 | platform.redirectStdioToLogcat(); 119 | Python.start(platform); 120 | py = Python.getInstance(); 121 | 122 | String argvStr = getIntent().getStringExtra("org.beeware.ARGV"); 123 | if (argvStr != null) { 124 | try { 125 | JSONArray argvJson = new JSONArray(argvStr); 126 | List sysArgv = py.getModule("sys").get("argv").asList(); 127 | for (int i = 0; i < argvJson.length(); i++) { 128 | sysArgv.add(PyObject.fromJava(argvJson.getString(i))); 129 | } 130 | } catch (JSONException e) { 131 | throw new RuntimeException(e); 132 | } 133 | } 134 | } 135 | 136 | Log.d(TAG, "Running main module " + getString(R.string.main_module)); 137 | py.getModule("runpy").callAttr( 138 | "run_module", 139 | getString(R.string.main_module), 140 | new Kwarg("run_name", "__main__"), 141 | new Kwarg("alter_sys", true) 142 | ); 143 | 144 | userCode("onCreate"); 145 | Log.d(TAG, "onCreate() complete"); 146 | } 147 | 148 | protected void onStart() { 149 | Log.d(TAG, "onStart() start"); 150 | super.onStart(); 151 | userCode("onStart"); 152 | Log.d(TAG, "onStart() complete"); 153 | } 154 | 155 | protected void onResume() { 156 | Log.d(TAG, "onResume() start"); 157 | super.onResume(); 158 | userCode("onResume"); 159 | Log.d(TAG, "onResume() complete"); 160 | } 161 | 162 | protected void onPause() { 163 | Log.d(TAG, "onPause() start"); 164 | super.onPause(); 165 | userCode("onPause"); 166 | Log.d(TAG, "onPause() complete"); 167 | } 168 | 169 | protected void onStop() { 170 | Log.d(TAG, "onStop() start"); 171 | super.onStop(); 172 | userCode("onStop"); 173 | Log.d(TAG, "onStop() complete"); 174 | } 175 | protected void onDestroy() { 176 | Log.d(TAG, "onDestroy() start"); 177 | super.onDestroy(); 178 | userCode("onDestroy"); 179 | Log.d(TAG, "onDestroy() complete"); 180 | } 181 | protected void onRestart() { 182 | Log.d(TAG, "onRestart() start"); 183 | super.onRestart(); 184 | userCode("onRestart"); 185 | Log.d(TAG, "onRestart() complete"); 186 | } 187 | public void onTopResumedActivityChanged (boolean isTopResumedActivity){ 188 | Log.d(TAG, "onTopResumedActivityChanged() start"); 189 | super.onTopResumedActivityChanged(isTopResumedActivity); 190 | userCode("onTopResumedActivityChanged", isTopResumedActivity); 191 | Log.d(TAG, "onTopResumedActivityChanged() complete"); 192 | } 193 | 194 | protected void onActivityResult(int requestCode, int resultCode, Intent data) 195 | { 196 | Log.d(TAG, "onActivityResult() start"); 197 | super.onActivityResult(requestCode, resultCode, data); 198 | userCode("onActivityResult", requestCode, resultCode, data); 199 | Log.d(TAG, "onActivityResult() complete"); 200 | } 201 | 202 | public void onConfigurationChanged(Configuration newConfig) { 203 | Log.d(TAG, "onConfigurationChanged() start"); 204 | super.onConfigurationChanged(newConfig); 205 | userCode("onConfigurationChanged", newConfig); 206 | Log.d(TAG, "onConfigurationChanged() complete"); 207 | } 208 | 209 | public boolean onOptionsItemSelected(MenuItem menuitem) { 210 | Log.d(TAG, "onOptionsItemSelected() start"); 211 | PyObject pyResult = userCode("onOptionsItemSelected", menuitem); 212 | boolean result = (pyResult == null) ? false : pyResult.toBoolean(); 213 | Log.d(TAG, "onOptionsItemSelected() complete"); 214 | return result; 215 | } 216 | 217 | public boolean onPrepareOptionsMenu(Menu menu) { 218 | Log.d(TAG, "onPrepareOptionsMenu() start"); 219 | PyObject pyResult = userCode("onPrepareOptionsMenu", menu); 220 | boolean result = (pyResult == null) ? false : pyResult.toBoolean(); 221 | Log.d(TAG, "onPrepareOptionsMenu() complete"); 222 | return result; 223 | } 224 | 225 | public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 226 | { 227 | Log.d(TAG, "onRequestPermissionsResult() start"); 228 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 229 | userCode("onRequestPermissionsResult", requestCode, permissions, grantResults); 230 | Log.d(TAG, "onRequestPermissionsResult() complete"); 231 | } 232 | 233 | private PyObject userCode(String methodName, Object... args) { 234 | if (pythonApp == null) { 235 | // Could be a non-graphical app such as Python-support-testbed. 236 | return null; 237 | } 238 | try { 239 | if (pythonApp.containsKey(methodName)) { 240 | return pythonApp.callAttr(methodName, args); 241 | } else { 242 | // Handle the case where the method doesn't exist 243 | return null; 244 | } 245 | } catch (PyException e) { 246 | if (e.getMessage().startsWith("NotImplementedError")) { 247 | return null; 248 | } 249 | throw e; 250 | } 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /android-toga/v0.0.5/java/PermissionUtils.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.pm.PackageManager; 6 | import androidx.core.app.ActivityCompat; 7 | import androidx.core.content.ContextCompat; 8 | 9 | public class PermissionUtils { 10 | public static void requestStoragePermissions(Activity activity) { 11 | if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || 12 | ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 13 | 14 | ActivityCompat.requestPermissions(activity, new String[]{ 15 | Manifest.permission.READ_EXTERNAL_STORAGE, 16 | Manifest.permission.WRITE_EXTERNAL_STORAGE 17 | }, 1); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /android-toga/v0.0.5/java/VersionChecker.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.content.DialogInterface; 6 | import android.content.Intent; 7 | import android.net.Uri; 8 | import android.os.Handler; 9 | import android.os.Looper; 10 | 11 | import java.io.BufferedReader; 12 | import java.io.InputStreamReader; 13 | import java.net.HttpURLConnection; 14 | import java.net.URL; 15 | 16 | public class VersionChecker { 17 | 18 | public static void checkVersion(Activity activity) { 19 | new Thread(new Runnable() { 20 | @Override 21 | public void run() { 22 | String link = "https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/refs/heads/main/android-toga/version.txt"; 23 | String content = getContentFromURL(link); 24 | 25 | if (!"v0.0.4".equals(content.trim())) { 26 | // نمایش دیالوگ در نخ اصلی 27 | new Handler(Looper.getMainLooper()).post(new Runnable() { 28 | @Override 29 | public void run() { 30 | showUpdateDialog(activity); 31 | } 32 | }); 33 | } 34 | } 35 | }).start(); 36 | } 37 | 38 | private static String getContentFromURL(String link) { 39 | StringBuilder content = new StringBuilder(); 40 | try { 41 | URL url = new URL(link); 42 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 43 | connection.setRequestMethod("GET"); 44 | 45 | BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); 46 | String inputLine; 47 | 48 | while ((inputLine = in.readLine()) != null) { 49 | content.append(inputLine); 50 | } 51 | in.close(); 52 | } catch (Exception e) { 53 | e.printStackTrace(); 54 | } 55 | return content.toString(); 56 | } 57 | 58 | private static void showUpdateDialog(Activity activity) { 59 | AlertDialog.Builder builder = new AlertDialog.Builder(activity); 60 | builder.setMessage("بروزرسانی در دسترس است") 61 | .setCancelable(false) 62 | .setPositiveButton("برو", new DialogInterface.OnClickListener() { 63 | public void onClick(DialogInterface dialog, int id) { 64 | Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/arshiacomplus/WarpScanner-android-GUI/")); 65 | activity.startActivity(browserIntent); 66 | } 67 | }) 68 | .setNegativeButton("بستن", new DialogInterface.OnClickListener() { 69 | public void onClick(DialogInterface dialog, int id) { 70 | dialog.dismiss(); 71 | } 72 | }); 73 | AlertDialog alert = builder.create(); 74 | alert.show(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /android-toga/v0.0.6/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 18 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /android-toga/v0.0.6/java/.MainActivity.java.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/70fdbdc27e57615e2b26fb66f6edb95026e3db8d/android-toga/v0.0.6/java/.MainActivity.java.swp -------------------------------------------------------------------------------- /android-toga/v0.0.6/java/DrawHandlerView.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | public class DrawHandlerView extends android.view.View { 4 | private IDrawHandler drawHandler = null; 5 | 6 | public DrawHandlerView(android.content.Context context) { 7 | super(context); 8 | } 9 | 10 | public void setDrawHandler(IDrawHandler drawHandler) { 11 | this.drawHandler = drawHandler; 12 | } 13 | 14 | public void onDraw(android.graphics.Canvas canvas) { 15 | super.onDraw(canvas); 16 | drawHandler.handleDraw(canvas); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android-toga/v0.0.6/java/IDrawHandler.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | public interface IDrawHandler { 4 | public void handleDraw(android.graphics.Canvas canvas); 5 | } 6 | -------------------------------------------------------------------------------- /android-toga/v0.0.6/java/IPythonApp.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | 8 | public interface IPythonApp { 9 | void onCreate(); 10 | void onResume(); 11 | void onStart(); 12 | void onActivityResult(int requestCode, int resultCode, Intent data); 13 | void onConfigurationChanged(Configuration newConfig); 14 | boolean onOptionsItemSelected(MenuItem menuitem); 15 | boolean onPrepareOptionsMenu(Menu menu); 16 | // There's no need to add any more methods to this interface, as the Python 17 | // call mechanism no longer uses it. 18 | } 19 | -------------------------------------------------------------------------------- /android-toga/v0.0.6/java/LTRConfig.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.app.Activity; 4 | import android.os.Build; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.LinearLayout; 8 | 9 | public class LTRConfig { 10 | 11 | public static void applyLTR(Activity activity) { 12 | ViewGroup rootView = (ViewGroup) activity.findViewById(android.R.id.content); 13 | setLTR(rootView); 14 | } 15 | 16 | private static void setLTR(ViewGroup viewGroup) { 17 | for (int i = 0; i < viewGroup.getChildCount(); i++) { 18 | View view = viewGroup.getChildAt(i); 19 | if (view instanceof ViewGroup) { 20 | setLTR((ViewGroup) view); 21 | } 22 | } 23 | 24 | // اگر نسخه اندروید بالاتر از نوقا است، جهت LTR را تنظیم می‌کنیم 25 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 26 | viewGroup.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android-toga/v0.0.6/java/LoggingConfig.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.util.logging.FileHandler; 8 | import java.util.logging.Handler; 9 | import java.util.logging.Level; 10 | import java.util.logging.Logger; 11 | import java.util.logging.SimpleFormatter; 12 | 13 | public class LoggingConfig { 14 | 15 | public static void setup(Context context) { 16 | String logPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/wwarpscanner/app_java.txt"; 17 | File logFile = new File(logPath); 18 | 19 | if (logFile.exists()) { 20 | logFile.delete(); 21 | } 22 | 23 | try { 24 | if (logFile.createNewFile()) { 25 | Handler fileHandler = new FileHandler(logPath, true); 26 | fileHandler.setFormatter(new SimpleFormatter()); 27 | Logger logger = Logger.getLogger(context.getPackageName()); 28 | logger.addHandler(fileHandler); 29 | logger.setLevel(Level.ALL); 30 | Logger.getLogger("").addHandler(fileHandler); // Add handler to root logger 31 | } 32 | } catch (IOException e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /android-toga/v0.0.6/java/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | import android.widget.LinearLayout; 10 | import android.content.Context; 11 | import android.net.Uri; 12 | import android.os.Handler; 13 | import android.os.Looper; 14 | import android.content.ContentResolver; 15 | import android.content.ContentUris; 16 | import android.database.Cursor; 17 | import android.net.Uri; 18 | import android.provider.MediaStore; 19 | 20 | import androidx.appcompat.app.AppCompatActivity; 21 | 22 | import com.chaquo.python.Kwarg; 23 | import com.chaquo.python.PyException; 24 | import com.chaquo.python.PyObject; 25 | import com.chaquo.python.Python; 26 | import com.chaquo.python.android.AndroidPlatform; 27 | 28 | import java.util.List; 29 | import java.util.logging.Logger; 30 | import java.io.File; 31 | import org.json.JSONArray; 32 | import org.json.JSONException; 33 | 34 | import com.arshiacomplus.warpscanner.warpscanner.R; 35 | 36 | import android.app.AlertDialog; 37 | import android.app.Activity; 38 | import android.os.Environment; 39 | import android.os.StrictMode; 40 | import android.app.AlarmManager; 41 | import android.app.PendingIntent; 42 | import android.content.Context; 43 | import android.content.Intent; 44 | import android.content.DialogInterface; 45 | 46 | public class MainActivity extends AppCompatActivity { 47 | 48 | 49 | 50 | // To profile app launch, use `adb -s MainActivity`; look for "onCreate() start" and "onResume() completed". 51 | private String TAG = "MainActivity"; 52 | private static PyObject pythonApp; 53 | private static Logger logger; 54 | 55 | 56 | 57 | /** 58 | * This method is called by `app.__main__` over JNI in Python when the BeeWare 59 | * app launches. 60 | * 61 | * @param app 62 | */ 63 | @SuppressWarnings("unused") 64 | public static void setPythonApp(IPythonApp app) { 65 | 66 | pythonApp = PyObject.fromJava(app); 67 | 68 | } 69 | 70 | /** 71 | * We store the MainActivity instance on the *class* so that we can easily 72 | * access it from Python. 73 | */ 74 | public static MainActivity singletonThis; 75 | 76 | protected void onCreate(Bundle savedInstanceState) { 77 | Log.d(TAG, "onCreate() start"); 78 | // Change away from the splash screen theme to the app theme. 79 | setTheme(R.style.AppTheme); 80 | super.onCreate(savedInstanceState); 81 | LinearLayout layout = new LinearLayout(this); 82 | this.setContentView(layout); 83 | singletonThis = this; 84 | 85 | 86 | 87 | PermissionUtils.requestStoragePermissions(this); 88 | try { 89 | PermissionUtils.requestManageAllFilesPermission(this); 90 | } catch (Exception e) { 91 | e.printStackTrace(); 92 | } 93 | 94 | 95 | Context appContext = getApplicationContext(); 96 | LoggingConfig.setup(appContext); 97 | logger = Logger.getLogger(appContext.getPackageName()); 98 | String wwpath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/wwarpscanner"; 99 | File warppath = new File(wwpath); 100 | 101 | final Context context = this; 102 | 103 | 104 | Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 105 | @Override 106 | public void uncaughtException(Thread t, Throwable e) { 107 | logger.severe("Uncaught exception: " + e.getMessage()); 108 | for (StackTraceElement element : e.getStackTrace()) { 109 | logger.severe(element.toString()); 110 | } 111 | 112 | } 113 | 114 | }); 115 | 116 | 117 | logger.info("Application started"); 118 | 119 | 120 | 121 | 122 | 123 | // Call PermissionUtils to request permissions 124 | 125 | 126 | 127 | LTRConfig.applyLTR(this); 128 | // فراخوانی بررسی نسخه 129 | VersionChecker.checkVersion(this); 130 | 131 | 132 | // اکنون می‌توانید لاگ بگیرید 133 | Logger logger = Logger.getLogger(getPackageName()); 134 | logger.info("Application started"); 135 | 136 | 137 | Python py; 138 | if (Python.isStarted()) { 139 | Log.d(TAG, "Python already started"); 140 | py = Python.getInstance(); 141 | } else { 142 | Log.d(TAG, "Starting Python"); 143 | AndroidPlatform platform = new AndroidPlatform(this); 144 | platform.redirectStdioToLogcat(); 145 | Python.start(platform); 146 | py = Python.getInstance(); 147 | 148 | String argvStr = getIntent().getStringExtra("org.beeware.ARGV"); 149 | if (argvStr != null) { 150 | try { 151 | JSONArray argvJson = new JSONArray(argvStr); 152 | List sysArgv = py.getModule("sys").get("argv").asList(); 153 | for (int i = 0; i < argvJson.length(); i++) { 154 | sysArgv.add(PyObject.fromJava(argvJson.getString(i))); 155 | } 156 | } catch (JSONException e) { 157 | throw new RuntimeException(e); 158 | } 159 | } 160 | } 161 | 162 | Log.d(TAG, "Running main module " + getString(R.string.main_module)); 163 | py.getModule("runpy").callAttr( 164 | "run_module", 165 | getString(R.string.main_module), 166 | new Kwarg("run_name", "__main__"), 167 | new Kwarg("alter_sys", true) 168 | ); 169 | 170 | userCode("onCreate"); 171 | Log.d(TAG, "onCreate() complete"); 172 | } 173 | 174 | private static void showAlertAndOpenFileManager(Context context, String path) { 175 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 176 | builder.setMessage("لطفاً این فایل را حذف کنید") 177 | .setCancelable(false) 178 | .setPositiveButton("برو", new DialogInterface.OnClickListener() { 179 | public void onClick(DialogInterface dialog, int id) { 180 | Intent intent = new Intent(Intent.ACTION_VIEW); 181 | Uri uri = Uri.parse("file://" + path); 182 | intent.setDataAndType(uri, "resource/folder"); 183 | context.startActivity(Intent.createChooser(intent, "Open folder")); 184 | } 185 | }) 186 | .setNegativeButton("بستن", new DialogInterface.OnClickListener() { 187 | public void onClick(DialogInterface dialog, int id) { 188 | dialog.dismiss(); 189 | } 190 | }); 191 | AlertDialog alert = builder.create(); 192 | alert.show(); 193 | } 194 | 195 | 196 | private static void forceDeleteWarpscannerFolder(File folder) { 197 | if (folder.isDirectory()) { 198 | File[] children = folder.listFiles(); 199 | if (children != null) { 200 | for (File child : children) { 201 | forceDeleteWarpscannerFolder(child); 202 | } 203 | } 204 | } 205 | if (!folder.delete()) { 206 | throw new RuntimeException("Failed to delete folder: " + folder.getAbsolutePath()); 207 | } else { 208 | System.out.println("Folder " + folder.getAbsolutePath() + " deleted successfully."); 209 | } 210 | } 211 | 212 | 213 | protected void onStart() { 214 | Log.d(TAG, "onStart() start"); 215 | super.onStart(); 216 | userCode("onStart"); 217 | Log.d(TAG, "onStart() complete"); 218 | } 219 | 220 | protected void onResume() { 221 | Log.d(TAG, "onResume() start"); 222 | super.onResume(); 223 | userCode("onResume"); 224 | Log.d(TAG, "onResume() complete"); 225 | } 226 | 227 | protected void onPause() { 228 | Log.d(TAG, "onPause() start"); 229 | super.onPause(); 230 | userCode("onPause"); 231 | Log.d(TAG, "onPause() complete"); 232 | } 233 | 234 | protected void onStop() { 235 | Log.d(TAG, "onStop() start"); 236 | super.onStop(); 237 | userCode("onStop"); 238 | Log.d(TAG, "onStop() complete"); 239 | } 240 | protected void onDestroy() { 241 | Log.d(TAG, "onDestroy() start"); 242 | super.onDestroy(); 243 | userCode("onDestroy"); 244 | Log.d(TAG, "onDestroy() complete"); 245 | } 246 | protected void onRestart() { 247 | Log.d(TAG, "onRestart() start"); 248 | super.onRestart(); 249 | userCode("onRestart"); 250 | Log.d(TAG, "onRestart() complete"); 251 | } 252 | public void onTopResumedActivityChanged (boolean isTopResumedActivity){ 253 | Log.d(TAG, "onTopResumedActivityChanged() start"); 254 | super.onTopResumedActivityChanged(isTopResumedActivity); 255 | userCode("onTopResumedActivityChanged", isTopResumedActivity); 256 | Log.d(TAG, "onTopResumedActivityChanged() complete"); 257 | } 258 | 259 | protected void onActivityResult(int requestCode, int resultCode, Intent data) 260 | { 261 | Log.d(TAG, "onActivityResult() start"); 262 | super.onActivityResult(requestCode, resultCode, data); 263 | userCode("onActivityResult", requestCode, resultCode, data); 264 | Log.d(TAG, "onActivityResult() complete"); 265 | } 266 | 267 | public void onConfigurationChanged(Configuration newConfig) { 268 | Log.d(TAG, "onConfigurationChanged() start"); 269 | super.onConfigurationChanged(newConfig); 270 | userCode("onConfigurationChanged", newConfig); 271 | Log.d(TAG, "onConfigurationChanged() complete"); 272 | } 273 | 274 | public boolean onOptionsItemSelected(MenuItem menuitem) { 275 | Log.d(TAG, "onOptionsItemSelected() start"); 276 | PyObject pyResult = userCode("onOptionsItemSelected", menuitem); 277 | boolean result = (pyResult == null) ? false : pyResult.toBoolean(); 278 | Log.d(TAG, "onOptionsItemSelected() complete"); 279 | return result; 280 | } 281 | 282 | public boolean onPrepareOptionsMenu(Menu menu) { 283 | Log.d(TAG, "onPrepareOptionsMenu() start"); 284 | PyObject pyResult = userCode("onPrepareOptionsMenu", menu); 285 | boolean result = (pyResult == null) ? false : pyResult.toBoolean(); 286 | Log.d(TAG, "onPrepareOptionsMenu() complete"); 287 | return result; 288 | } 289 | 290 | public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 291 | { 292 | Log.d(TAG, "onRequestPermissionsResult() start"); 293 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 294 | userCode("onRequestPermissionsResult", requestCode, permissions, grantResults); 295 | Log.d(TAG, "onRequestPermissionsResult() complete"); 296 | } 297 | 298 | private PyObject userCode(String methodName, Object... args) { 299 | if (pythonApp == null) { 300 | // Could be a non-graphical app such as Python-support-testbed. 301 | return null; 302 | } 303 | try { 304 | if (pythonApp.containsKey(methodName)) { 305 | return pythonApp.callAttr(methodName, args); 306 | } else { 307 | // Handle the case where the method doesn't exist 308 | return null; 309 | } 310 | } catch (PyException e) { 311 | if (e.getMessage().startsWith("NotImplementedError")) { 312 | return null; 313 | } 314 | throw e; 315 | } 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /android-toga/v0.0.6/java/PermissionUtils.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.pm.PackageManager; 6 | import androidx.core.app.ActivityCompat; 7 | import androidx.core.content.ContextCompat; 8 | import android.app.AlertDialog; 9 | import android.app.Activity; 10 | import android.os.Environment; 11 | import android.os.StrictMode; 12 | import android.app.AlarmManager; 13 | import android.app.PendingIntent; 14 | import android.content.Context; 15 | import android.content.Intent; 16 | import android.content.DialogInterface; 17 | import android.content.Intent; 18 | import android.os.Build; 19 | import android.provider.Settings; 20 | 21 | 22 | public class PermissionUtils { 23 | public static void requestStoragePermissions(Activity activity) { 24 | if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || 25 | ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 26 | 27 | ActivityCompat.requestPermissions(activity, new String[]{ 28 | Manifest.permission.READ_EXTERNAL_STORAGE, 29 | Manifest.permission.WRITE_EXTERNAL_STORAGE 30 | }, 1); 31 | } 32 | 33 | } 34 | public static void requestManageAllFilesPermission(Context context) { 35 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 36 | if (!Environment.isExternalStorageManager()) { 37 | Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION); 38 | context.startActivity(intent); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /android-toga/v0.0.6/java/VersionChecker.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.content.Context; 6 | import android.content.DialogInterface; 7 | import android.content.Intent; 8 | import android.net.ConnectivityManager; 9 | import android.net.NetworkInfo; 10 | import android.net.Uri; 11 | import android.os.Handler; 12 | import android.os.Looper; 13 | 14 | import java.io.BufferedReader; 15 | import java.io.InputStreamReader; 16 | import java.net.HttpURLConnection; 17 | import java.net.URL; 18 | 19 | public class VersionChecker { 20 | public static void checkVersion(Activity activity) { 21 | if (!isInternetAvailable(activity)) { 22 | showNoInternetDialog(activity); 23 | return; 24 | } 25 | 26 | new Thread(new Runnable() { 27 | @Override 28 | public void run() { 29 | String link = "https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/refs/heads/main/android-toga/version.txt"; 30 | String content = getContentFromURL(link); 31 | if (!"v0.0.6".equals(content.trim())) { 32 | // نمایش دیالوگ در نخ اصلی 33 | new Handler(Looper.getMainLooper()).post(new Runnable() { 34 | @Override 35 | public void run() { 36 | showUpdateDialog(activity); 37 | } 38 | }); 39 | } 40 | } 41 | }).start(); 42 | } 43 | 44 | private static boolean isInternetAvailable(Context context) { 45 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 46 | NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); 47 | return activeNetwork != null && activeNetwork.isConnectedOrConnecting(); 48 | } 49 | 50 | private static String getContentFromURL(String link) { 51 | StringBuilder content = new StringBuilder(); 52 | try { 53 | URL url = new URL(link); 54 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 55 | connection.setRequestMethod("GET"); 56 | BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); 57 | String inputLine; 58 | while ((inputLine = in.readLine()) != null) { 59 | content.append(inputLine); 60 | } 61 | in.close(); 62 | } catch (Exception e) { 63 | e.printStackTrace(); 64 | } 65 | return content.toString(); 66 | } 67 | 68 | private static void showUpdateDialog(Activity activity) { 69 | AlertDialog.Builder builder = new AlertDialog.Builder(activity); 70 | builder.setMessage("بروزرسانی در دسترس است") 71 | .setCancelable(false) 72 | .setPositiveButton("برو", new DialogInterface.OnClickListener() { 73 | public void onClick(DialogInterface dialog, int id) { 74 | Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/arshiacomplus/WarpScanner-android-GUI/")); 75 | activity.startActivity(browserIntent); 76 | } 77 | }) 78 | .setNegativeButton("بستن", new DialogInterface.OnClickListener() { 79 | public void onClick(DialogInterface dialog, int id) { 80 | dialog.dismiss(); 81 | } 82 | }); 83 | AlertDialog alert = builder.create(); 84 | alert.show(); 85 | } 86 | 87 | private static void showNoInternetDialog(Activity activity) { 88 | AlertDialog.Builder builder = new AlertDialog.Builder(activity); 89 | builder.setMessage("اتصال به اینترنت برقرار نیست. لطفاً اتصال خود را بررسی کنید.") 90 | .setCancelable(false) 91 | .setPositiveButton("باشه", new DialogInterface.OnClickListener() { 92 | public void onClick(DialogInterface dialog, int id) { 93 | dialog.dismiss(); 94 | } 95 | }); 96 | AlertDialog alert = builder.create(); 97 | alert.show(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /android-toga/v0.0.7/java/.MainActivity.java.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/70fdbdc27e57615e2b26fb66f6edb95026e3db8d/android-toga/v0.0.7/java/.MainActivity.java.swp -------------------------------------------------------------------------------- /android-toga/v0.0.7/java/DrawHandlerView.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | public class DrawHandlerView extends android.view.View { 4 | private IDrawHandler drawHandler = null; 5 | 6 | public DrawHandlerView(android.content.Context context) { 7 | super(context); 8 | } 9 | 10 | public void setDrawHandler(IDrawHandler drawHandler) { 11 | this.drawHandler = drawHandler; 12 | } 13 | 14 | public void onDraw(android.graphics.Canvas canvas) { 15 | super.onDraw(canvas); 16 | drawHandler.handleDraw(canvas); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android-toga/v0.0.7/java/IDrawHandler.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | public interface IDrawHandler { 4 | public void handleDraw(android.graphics.Canvas canvas); 5 | } 6 | -------------------------------------------------------------------------------- /android-toga/v0.0.7/java/IPythonApp.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | 8 | public interface IPythonApp { 9 | void onCreate(); 10 | void onResume(); 11 | void onStart(); 12 | void onActivityResult(int requestCode, int resultCode, Intent data); 13 | void onConfigurationChanged(Configuration newConfig); 14 | boolean onOptionsItemSelected(MenuItem menuitem); 15 | boolean onPrepareOptionsMenu(Menu menu); 16 | // There's no need to add any more methods to this interface, as the Python 17 | // call mechanism no longer uses it. 18 | } 19 | -------------------------------------------------------------------------------- /android-toga/v0.0.7/java/LTRConfig.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.app.Activity; 4 | import android.os.Build; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.LinearLayout; 8 | 9 | public class LTRConfig { 10 | 11 | public static void applyLTR(Activity activity) { 12 | ViewGroup rootView = (ViewGroup) activity.findViewById(android.R.id.content); 13 | setLTR(rootView); 14 | } 15 | 16 | private static void setLTR(ViewGroup viewGroup) { 17 | for (int i = 0; i < viewGroup.getChildCount(); i++) { 18 | View view = viewGroup.getChildAt(i); 19 | if (view instanceof ViewGroup) { 20 | setLTR((ViewGroup) view); 21 | } 22 | } 23 | 24 | // اگر نسخه اندروید بالاتر از نوقا است، جهت LTR را تنظیم می‌کنیم 25 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 26 | viewGroup.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android-toga/v0.0.7/java/LoggingConfig.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.util.logging.FileHandler; 8 | import java.util.logging.Handler; 9 | import java.util.logging.Level; 10 | import java.util.logging.Logger; 11 | import java.util.logging.SimpleFormatter; 12 | 13 | public class LoggingConfig { 14 | 15 | public static void setup(Context context) { 16 | String logPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/wwarpscanner/app_java.txt"; 17 | File logFile = new File(logPath); 18 | 19 | if (logFile.exists()) { 20 | logFile.delete(); 21 | } 22 | 23 | try { 24 | if (logFile.createNewFile()) { 25 | Handler fileHandler = new FileHandler(logPath, true); 26 | fileHandler.setFormatter(new SimpleFormatter()); 27 | Logger logger = Logger.getLogger(context.getPackageName()); 28 | logger.addHandler(fileHandler); 29 | logger.setLevel(Level.ALL); 30 | Logger.getLogger("").addHandler(fileHandler); // Add handler to root logger 31 | } 32 | } catch (IOException e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /android-toga/v0.0.7/java/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | import android.widget.LinearLayout; 10 | import android.content.Context; 11 | import android.net.Uri; 12 | import android.os.Handler; 13 | import android.os.Looper; 14 | import android.content.ContentResolver; 15 | import android.content.ContentUris; 16 | import android.database.Cursor; 17 | import android.net.Uri; 18 | import android.provider.MediaStore; 19 | 20 | import androidx.appcompat.app.AppCompatActivity; 21 | 22 | import com.chaquo.python.Kwarg; 23 | import com.chaquo.python.PyException; 24 | import com.chaquo.python.PyObject; 25 | import com.chaquo.python.Python; 26 | import com.chaquo.python.android.AndroidPlatform; 27 | 28 | import java.util.List; 29 | import java.util.logging.Logger; 30 | import java.io.File; 31 | import org.json.JSONArray; 32 | import org.json.JSONException; 33 | 34 | import com.arshiacomplus.warpscanner.warpscanner.R; 35 | 36 | import androidx.appcompat.app.AppCompatActivity; 37 | import com.google.android.material.button.MaterialButton; 38 | import android.app.AlertDialog; 39 | import android.app.Activity; 40 | import android.os.Environment; 41 | import android.os.StrictMode; 42 | import android.app.AlarmManager; 43 | import android.app.PendingIntent; 44 | import android.content.Context; 45 | import android.content.Intent; 46 | import android.content.DialogInterface; 47 | 48 | public class MainActivity extends AppCompatActivity { 49 | 50 | 51 | 52 | // To profile app launch, use `adb -s MainActivity`; look for "onCreate() start" and "onResume() completed". 53 | private String TAG = "MainActivity"; 54 | private static PyObject pythonApp; 55 | private static Logger logger; 56 | 57 | 58 | 59 | /** 60 | * This method is called by `app.__main__` over JNI in Python when the BeeWare 61 | * app launches. 62 | * 63 | * @param app 64 | */ 65 | @SuppressWarnings("unused") 66 | public static void setPythonApp(IPythonApp app) { 67 | 68 | pythonApp = PyObject.fromJava(app); 69 | 70 | } 71 | 72 | /** 73 | * We store the MainActivity instance on the *class* so that we can easily 74 | * access it from Python. 75 | */ 76 | public static MainActivity singletonThis; 77 | 78 | protected void onCreate(Bundle savedInstanceState) { 79 | Log.d(TAG, "onCreate() start"); 80 | // Change away from the splash screen theme to the app theme. 81 | setTheme(R.style.AppTheme); 82 | super.onCreate(savedInstanceState); 83 | LinearLayout layout = new LinearLayout(this); 84 | this.setContentView(layout); 85 | singletonThis = this; 86 | 87 | 88 | 89 | PermissionUtils.requestStoragePermissions(this); 90 | try { 91 | PermissionUtils.requestManageAllFilesPermission(this); 92 | } catch (Exception e) { 93 | e.printStackTrace(); 94 | } 95 | 96 | 97 | Context appContext = getApplicationContext(); 98 | LoggingConfig.setup(appContext); 99 | logger = Logger.getLogger(appContext.getPackageName()); 100 | String wwpath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/wwarpscanner"; 101 | File warppath = new File(wwpath); 102 | 103 | final Context context = this; 104 | 105 | 106 | Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 107 | @Override 108 | public void uncaughtException(Thread t, Throwable e) { 109 | logger.severe("Uncaught exception: " + e.getMessage()); 110 | for (StackTraceElement element : e.getStackTrace()) { 111 | logger.severe(element.toString()); 112 | } 113 | 114 | } 115 | 116 | }); 117 | 118 | 119 | logger.info("Application started"); 120 | 121 | 122 | 123 | 124 | 125 | // Call PermissionUtils to request permissions 126 | 127 | 128 | 129 | LTRConfig.applyLTR(this); 130 | 131 | // فراخوانی بررسی نسخه 132 | VersionChecker.checkVersion(this); 133 | 134 | 135 | // اکنون می‌توانید لاگ بگیرید 136 | Logger logger = Logger.getLogger(getPackageName()); 137 | logger.info("Application started"); 138 | 139 | 140 | Python py; 141 | if (Python.isStarted()) { 142 | Log.d(TAG, "Python already started"); 143 | py = Python.getInstance(); 144 | } else { 145 | Log.d(TAG, "Starting Python"); 146 | AndroidPlatform platform = new AndroidPlatform(this); 147 | platform.redirectStdioToLogcat(); 148 | Python.start(platform); 149 | py = Python.getInstance(); 150 | 151 | String argvStr = getIntent().getStringExtra("org.beeware.ARGV"); 152 | if (argvStr != null) { 153 | try { 154 | JSONArray argvJson = new JSONArray(argvStr); 155 | List sysArgv = py.getModule("sys").get("argv").asList(); 156 | for (int i = 0; i < argvJson.length(); i++) { 157 | sysArgv.add(PyObject.fromJava(argvJson.getString(i))); 158 | } 159 | } catch (JSONException e) { 160 | throw new RuntimeException(e); 161 | } 162 | } 163 | } 164 | 165 | Log.d(TAG, "Running main module " + getString(R.string.main_module)); 166 | py.getModule("runpy").callAttr( 167 | "run_module", 168 | getString(R.string.main_module), 169 | new Kwarg("run_name", "__main__"), 170 | new Kwarg("alter_sys", true) 171 | ); 172 | 173 | userCode("onCreate"); 174 | Log.d(TAG, "onCreate() complete"); 175 | } 176 | 177 | private static void showAlertAndOpenFileManager(Context context, String path) { 178 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 179 | builder.setMessage("لطفاً این فایل را حذف کنید") 180 | .setCancelable(false) 181 | .setPositiveButton("برو", new DialogInterface.OnClickListener() { 182 | public void onClick(DialogInterface dialog, int id) { 183 | Intent intent = new Intent(Intent.ACTION_VIEW); 184 | Uri uri = Uri.parse("file://" + path); 185 | intent.setDataAndType(uri, "resource/folder"); 186 | context.startActivity(Intent.createChooser(intent, "Open folder")); 187 | } 188 | }) 189 | .setNegativeButton("بستن", new DialogInterface.OnClickListener() { 190 | public void onClick(DialogInterface dialog, int id) { 191 | dialog.dismiss(); 192 | } 193 | }); 194 | AlertDialog alert = builder.create(); 195 | alert.show(); 196 | } 197 | 198 | 199 | private static void forceDeleteWarpscannerFolder(File folder) { 200 | if (folder.isDirectory()) { 201 | File[] children = folder.listFiles(); 202 | if (children != null) { 203 | for (File child : children) { 204 | forceDeleteWarpscannerFolder(child); 205 | } 206 | } 207 | } 208 | if (!folder.delete()) { 209 | throw new RuntimeException("Failed to delete folder: " + folder.getAbsolutePath()); 210 | } else { 211 | System.out.println("Folder " + folder.getAbsolutePath() + " deleted successfully."); 212 | } 213 | } 214 | 215 | 216 | protected void onStart() { 217 | Log.d(TAG, "onStart() start"); 218 | super.onStart(); 219 | userCode("onStart"); 220 | Log.d(TAG, "onStart() complete"); 221 | } 222 | 223 | protected void onResume() { 224 | Log.d(TAG, "onResume() start"); 225 | super.onResume(); 226 | userCode("onResume"); 227 | Log.d(TAG, "onResume() complete"); 228 | } 229 | 230 | protected void onPause() { 231 | Log.d(TAG, "onPause() start"); 232 | super.onPause(); 233 | userCode("onPause"); 234 | Log.d(TAG, "onPause() complete"); 235 | } 236 | 237 | protected void onStop() { 238 | Log.d(TAG, "onStop() start"); 239 | super.onStop(); 240 | userCode("onStop"); 241 | Log.d(TAG, "onStop() complete"); 242 | } 243 | protected void onDestroy() { 244 | Log.d(TAG, "onDestroy() start"); 245 | super.onDestroy(); 246 | userCode("onDestroy"); 247 | Log.d(TAG, "onDestroy() complete"); 248 | } 249 | protected void onRestart() { 250 | Log.d(TAG, "onRestart() start"); 251 | super.onRestart(); 252 | userCode("onRestart"); 253 | Log.d(TAG, "onRestart() complete"); 254 | } 255 | public void onTopResumedActivityChanged (boolean isTopResumedActivity){ 256 | Log.d(TAG, "onTopResumedActivityChanged() start"); 257 | super.onTopResumedActivityChanged(isTopResumedActivity); 258 | userCode("onTopResumedActivityChanged", isTopResumedActivity); 259 | Log.d(TAG, "onTopResumedActivityChanged() complete"); 260 | } 261 | 262 | protected void onActivityResult(int requestCode, int resultCode, Intent data) 263 | { 264 | Log.d(TAG, "onActivityResult() start"); 265 | super.onActivityResult(requestCode, resultCode, data); 266 | userCode("onActivityResult", requestCode, resultCode, data); 267 | Log.d(TAG, "onActivityResult() complete"); 268 | } 269 | 270 | public void onConfigurationChanged(Configuration newConfig) { 271 | Log.d(TAG, "onConfigurationChanged() start"); 272 | super.onConfigurationChanged(newConfig); 273 | userCode("onConfigurationChanged", newConfig); 274 | Log.d(TAG, "onConfigurationChanged() complete"); 275 | } 276 | 277 | public boolean onOptionsItemSelected(MenuItem menuitem) { 278 | Log.d(TAG, "onOptionsItemSelected() start"); 279 | PyObject pyResult = userCode("onOptionsItemSelected", menuitem); 280 | boolean result = (pyResult == null) ? false : pyResult.toBoolean(); 281 | Log.d(TAG, "onOptionsItemSelected() complete"); 282 | return result; 283 | } 284 | 285 | public boolean onPrepareOptionsMenu(Menu menu) { 286 | Log.d(TAG, "onPrepareOptionsMenu() start"); 287 | PyObject pyResult = userCode("onPrepareOptionsMenu", menu); 288 | boolean result = (pyResult == null) ? false : pyResult.toBoolean(); 289 | Log.d(TAG, "onPrepareOptionsMenu() complete"); 290 | return result; 291 | } 292 | 293 | public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 294 | { 295 | Log.d(TAG, "onRequestPermissionsResult() start"); 296 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 297 | userCode("onRequestPermissionsResult", requestCode, permissions, grantResults); 298 | Log.d(TAG, "onRequestPermissionsResult() complete"); 299 | } 300 | 301 | private PyObject userCode(String methodName, Object... args) { 302 | if (pythonApp == null) { 303 | // Could be a non-graphical app such as Python-support-testbed. 304 | return null; 305 | } 306 | try { 307 | if (pythonApp.containsKey(methodName)) { 308 | return pythonApp.callAttr(methodName, args); 309 | } else { 310 | // Handle the case where the method doesn't exist 311 | return null; 312 | } 313 | } catch (PyException e) { 314 | if (e.getMessage().startsWith("NotImplementedError")) { 315 | return null; 316 | } 317 | throw e; 318 | } 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /android-toga/v0.0.7/java/PermissionUtils.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.pm.PackageManager; 6 | import androidx.core.app.ActivityCompat; 7 | import androidx.core.content.ContextCompat; 8 | import android.app.AlertDialog; 9 | import android.app.Activity; 10 | import android.os.Environment; 11 | import android.os.StrictMode; 12 | import android.app.AlarmManager; 13 | import android.app.PendingIntent; 14 | import android.content.Context; 15 | import android.content.Intent; 16 | import android.content.DialogInterface; 17 | import android.content.Intent; 18 | import android.os.Build; 19 | import android.provider.Settings; 20 | 21 | 22 | public class PermissionUtils { 23 | public static void requestStoragePermissions(Activity activity) { 24 | if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || 25 | ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 26 | 27 | ActivityCompat.requestPermissions(activity, new String[]{ 28 | Manifest.permission.READ_EXTERNAL_STORAGE, 29 | Manifest.permission.WRITE_EXTERNAL_STORAGE 30 | }, 1); 31 | } 32 | 33 | } 34 | public static void requestManageAllFilesPermission(Context context) { 35 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 36 | if (!Environment.isExternalStorageManager()) { 37 | Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION); 38 | context.startActivity(intent); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /android-toga/v0.0.7/java/VersionChecker.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.content.Context; 6 | import android.content.DialogInterface; 7 | import android.content.Intent; 8 | import android.net.ConnectivityManager; 9 | import android.net.NetworkInfo; 10 | import android.net.Uri; 11 | import android.os.Handler; 12 | import android.os.Looper; 13 | 14 | import java.io.BufferedReader; 15 | import java.io.InputStreamReader; 16 | import java.net.HttpURLConnection; 17 | import java.net.URL; 18 | 19 | public class VersionChecker { 20 | public static void checkVersion(Activity activity) { 21 | if (!isInternetAvailable(activity)) { 22 | showNoInternetDialog(activity); 23 | return; 24 | } 25 | 26 | new Thread(new Runnable() { 27 | @Override 28 | public void run() { 29 | String link = "https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/refs/heads/main/android-toga/version.txt"; 30 | String content = getContentFromURL(link); 31 | if (!"v0.0.7".equals(content.trim())) { 32 | // نمایش دیالوگ در نخ اصلی 33 | new Handler(Looper.getMainLooper()).post(new Runnable() { 34 | @Override 35 | public void run() { 36 | showUpdateDialog(activity); 37 | } 38 | }); 39 | } 40 | } 41 | }).start(); 42 | } 43 | 44 | private static boolean isInternetAvailable(Context context) { 45 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 46 | NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); 47 | return activeNetwork != null && activeNetwork.isConnectedOrConnecting(); 48 | } 49 | 50 | private static String getContentFromURL(String link) { 51 | StringBuilder content = new StringBuilder(); 52 | try { 53 | URL url = new URL(link); 54 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 55 | connection.setRequestMethod("GET"); 56 | BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); 57 | String inputLine; 58 | while ((inputLine = in.readLine()) != null) { 59 | content.append(inputLine); 60 | } 61 | in.close(); 62 | } catch (Exception e) { 63 | e.printStackTrace(); 64 | } 65 | return content.toString(); 66 | } 67 | 68 | private static void showUpdateDialog(Activity activity) { 69 | AlertDialog.Builder builder = new AlertDialog.Builder(activity); 70 | builder.setMessage("بروزرسانی در دسترس است") 71 | .setCancelable(false) 72 | .setPositiveButton("برو", new DialogInterface.OnClickListener() { 73 | public void onClick(DialogInterface dialog, int id) { 74 | Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/arshiacomplus/WarpScanner-android-GUI/")); 75 | activity.startActivity(browserIntent); 76 | } 77 | }) 78 | .setNegativeButton("بستن", new DialogInterface.OnClickListener() { 79 | public void onClick(DialogInterface dialog, int id) { 80 | dialog.dismiss(); 81 | } 82 | }); 83 | AlertDialog alert = builder.create(); 84 | alert.show(); 85 | } 86 | 87 | private static void showNoInternetDialog(Activity activity) { 88 | AlertDialog.Builder builder = new AlertDialog.Builder(activity); 89 | builder.setMessage("اتصال به اینترنت برقرار نیست. لطفاً اتصال خود را بررسی کنید.") 90 | .setCancelable(false) 91 | .setPositiveButton("باشه", new DialogInterface.OnClickListener() { 92 | public void onClick(DialogInterface dialog, int id) { 93 | dialog.dismiss(); 94 | } 95 | }); 96 | AlertDialog alert = builder.create(); 97 | alert.show(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /android-toga/v0.0.8/java/.MainActivity.java.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/70fdbdc27e57615e2b26fb66f6edb95026e3db8d/android-toga/v0.0.8/java/.MainActivity.java.swp -------------------------------------------------------------------------------- /android-toga/v0.0.8/java/DrawHandlerView.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | public class DrawHandlerView extends android.view.View { 4 | private IDrawHandler drawHandler = null; 5 | 6 | public DrawHandlerView(android.content.Context context) { 7 | super(context); 8 | } 9 | 10 | public void setDrawHandler(IDrawHandler drawHandler) { 11 | this.drawHandler = drawHandler; 12 | } 13 | 14 | public void onDraw(android.graphics.Canvas canvas) { 15 | super.onDraw(canvas); 16 | drawHandler.handleDraw(canvas); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android-toga/v0.0.8/java/IDrawHandler.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | public interface IDrawHandler { 4 | public void handleDraw(android.graphics.Canvas canvas); 5 | } 6 | -------------------------------------------------------------------------------- /android-toga/v0.0.8/java/IPythonApp.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | 8 | public interface IPythonApp { 9 | void onCreate(); 10 | void onResume(); 11 | void onStart(); 12 | void onActivityResult(int requestCode, int resultCode, Intent data); 13 | void onConfigurationChanged(Configuration newConfig); 14 | boolean onOptionsItemSelected(MenuItem menuitem); 15 | boolean onPrepareOptionsMenu(Menu menu); 16 | // There's no need to add any more methods to this interface, as the Python 17 | // call mechanism no longer uses it. 18 | } 19 | -------------------------------------------------------------------------------- /android-toga/v0.0.8/java/LTRConfig.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.app.Activity; 4 | import android.os.Build; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.LinearLayout; 8 | 9 | public class LTRConfig { 10 | 11 | public static void applyLTR(Activity activity) { 12 | ViewGroup rootView = (ViewGroup) activity.findViewById(android.R.id.content); 13 | setLTR(rootView); 14 | } 15 | 16 | private static void setLTR(ViewGroup viewGroup) { 17 | for (int i = 0; i < viewGroup.getChildCount(); i++) { 18 | View view = viewGroup.getChildAt(i); 19 | if (view instanceof ViewGroup) { 20 | setLTR((ViewGroup) view); 21 | } 22 | } 23 | 24 | // اگر نسخه اندروید بالاتر از نوقا است، جهت LTR را تنظیم می‌کنیم 25 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 26 | viewGroup.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android-toga/v0.0.8/java/LoggingConfig.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.util.logging.FileHandler; 8 | import java.util.logging.Handler; 9 | import java.util.logging.Level; 10 | import java.util.logging.Logger; 11 | import java.util.logging.SimpleFormatter; 12 | 13 | public class LoggingConfig { 14 | 15 | public static void setup(Context context) { 16 | String logPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/wwarpscanner/app_java.txt"; 17 | File logFile = new File(logPath); 18 | 19 | if (logFile.exists()) { 20 | logFile.delete(); 21 | } 22 | 23 | try { 24 | if (logFile.createNewFile()) { 25 | Handler fileHandler = new FileHandler(logPath, true); 26 | fileHandler.setFormatter(new SimpleFormatter()); 27 | Logger logger = Logger.getLogger(context.getPackageName()); 28 | logger.addHandler(fileHandler); 29 | logger.setLevel(Level.ALL); 30 | Logger.getLogger("").addHandler(fileHandler); // Add handler to root logger 31 | } 32 | } catch (IOException e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /android-toga/v0.0.8/java/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | import android.widget.LinearLayout; 10 | import android.content.Context; 11 | import android.net.Uri; 12 | import android.os.Handler; 13 | import android.os.Looper; 14 | import android.content.ContentResolver; 15 | import android.content.ContentUris; 16 | import android.database.Cursor; 17 | import android.net.Uri; 18 | import android.provider.MediaStore; 19 | 20 | import androidx.appcompat.app.AppCompatActivity; 21 | 22 | import com.chaquo.python.Kwarg; 23 | import com.chaquo.python.PyException; 24 | import com.chaquo.python.PyObject; 25 | import com.chaquo.python.Python; 26 | import com.chaquo.python.android.AndroidPlatform; 27 | 28 | import java.util.List; 29 | import java.util.logging.Logger; 30 | import java.io.File; 31 | import org.json.JSONArray; 32 | import org.json.JSONException; 33 | 34 | import com.arshiacomplus.warpscanner.warpscanner.R; 35 | 36 | import androidx.appcompat.app.AppCompatActivity; 37 | import com.google.android.material.button.MaterialButton; 38 | import android.app.AlertDialog; 39 | import android.app.Activity; 40 | import android.os.Environment; 41 | import android.os.StrictMode; 42 | import android.app.AlarmManager; 43 | import android.app.PendingIntent; 44 | import android.content.Context; 45 | import android.content.Intent; 46 | import android.content.DialogInterface; 47 | 48 | public class MainActivity extends AppCompatActivity { 49 | 50 | 51 | 52 | // To profile app launch, use `adb -s MainActivity`; look for "onCreate() start" and "onResume() completed". 53 | private String TAG = "MainActivity"; 54 | private static PyObject pythonApp; 55 | private static Logger logger; 56 | 57 | 58 | 59 | /** 60 | * This method is called by `app.__main__` over JNI in Python when the BeeWare 61 | * app launches. 62 | * 63 | * @param app 64 | */ 65 | @SuppressWarnings("unused") 66 | public static void setPythonApp(IPythonApp app) { 67 | 68 | pythonApp = PyObject.fromJava(app); 69 | 70 | } 71 | 72 | /** 73 | * We store the MainActivity instance on the *class* so that we can easily 74 | * access it from Python. 75 | */ 76 | public static MainActivity singletonThis; 77 | 78 | protected void onCreate(Bundle savedInstanceState) { 79 | Log.d(TAG, "onCreate() start"); 80 | // Change away from the splash screen theme to the app theme. 81 | setTheme(R.style.AppTheme); 82 | super.onCreate(savedInstanceState); 83 | LinearLayout layout = new LinearLayout(this); 84 | this.setContentView(layout); 85 | singletonThis = this; 86 | 87 | 88 | 89 | PermissionUtils.requestStoragePermissions(this); 90 | try { 91 | PermissionUtils.requestManageAllFilesPermission(this); 92 | } catch (Exception e) { 93 | e.printStackTrace(); 94 | } 95 | 96 | 97 | Context appContext = getApplicationContext(); 98 | LoggingConfig.setup(appContext); 99 | logger = Logger.getLogger(appContext.getPackageName()); 100 | String wwpath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/wwarpscanner"; 101 | File warppath = new File(wwpath); 102 | 103 | final Context context = this; 104 | 105 | 106 | Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 107 | @Override 108 | public void uncaughtException(Thread t, Throwable e) { 109 | logger.severe("Uncaught exception: " + e.getMessage()); 110 | for (StackTraceElement element : e.getStackTrace()) { 111 | logger.severe(element.toString()); 112 | } 113 | 114 | } 115 | 116 | }); 117 | 118 | 119 | logger.info("Application started"); 120 | 121 | 122 | 123 | 124 | 125 | // Call PermissionUtils to request permissions 126 | 127 | 128 | 129 | LTRConfig.applyLTR(this); 130 | 131 | // فراخوانی بررسی نسخه 132 | VersionChecker.checkVersion(this); 133 | 134 | 135 | // اکنون می‌توانید لاگ بگیرید 136 | Logger logger = Logger.getLogger(getPackageName()); 137 | logger.info("Application started"); 138 | 139 | 140 | Python py; 141 | if (Python.isStarted()) { 142 | Log.d(TAG, "Python already started"); 143 | py = Python.getInstance(); 144 | } else { 145 | Log.d(TAG, "Starting Python"); 146 | AndroidPlatform platform = new AndroidPlatform(this); 147 | platform.redirectStdioToLogcat(); 148 | Python.start(platform); 149 | py = Python.getInstance(); 150 | 151 | String argvStr = getIntent().getStringExtra("org.beeware.ARGV"); 152 | if (argvStr != null) { 153 | try { 154 | JSONArray argvJson = new JSONArray(argvStr); 155 | List sysArgv = py.getModule("sys").get("argv").asList(); 156 | for (int i = 0; i < argvJson.length(); i++) { 157 | sysArgv.add(PyObject.fromJava(argvJson.getString(i))); 158 | } 159 | } catch (JSONException e) { 160 | throw new RuntimeException(e); 161 | } 162 | } 163 | } 164 | 165 | Log.d(TAG, "Running main module " + getString(R.string.main_module)); 166 | py.getModule("runpy").callAttr( 167 | "run_module", 168 | getString(R.string.main_module), 169 | new Kwarg("run_name", "__main__"), 170 | new Kwarg("alter_sys", true) 171 | ); 172 | 173 | userCode("onCreate"); 174 | Log.d(TAG, "onCreate() complete"); 175 | } 176 | 177 | private static void showAlertAndOpenFileManager(Context context, String path) { 178 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 179 | builder.setMessage("لطفاً این فایل را حذف کنید") 180 | .setCancelable(false) 181 | .setPositiveButton("برو", new DialogInterface.OnClickListener() { 182 | public void onClick(DialogInterface dialog, int id) { 183 | Intent intent = new Intent(Intent.ACTION_VIEW); 184 | Uri uri = Uri.parse("file://" + path); 185 | intent.setDataAndType(uri, "resource/folder"); 186 | context.startActivity(Intent.createChooser(intent, "Open folder")); 187 | } 188 | }) 189 | .setNegativeButton("بستن", new DialogInterface.OnClickListener() { 190 | public void onClick(DialogInterface dialog, int id) { 191 | dialog.dismiss(); 192 | } 193 | }); 194 | AlertDialog alert = builder.create(); 195 | alert.show(); 196 | } 197 | 198 | 199 | private static void forceDeleteWarpscannerFolder(File folder) { 200 | if (folder.isDirectory()) { 201 | File[] children = folder.listFiles(); 202 | if (children != null) { 203 | for (File child : children) { 204 | forceDeleteWarpscannerFolder(child); 205 | } 206 | } 207 | } 208 | if (!folder.delete()) { 209 | throw new RuntimeException("Failed to delete folder: " + folder.getAbsolutePath()); 210 | } else { 211 | System.out.println("Folder " + folder.getAbsolutePath() + " deleted successfully."); 212 | } 213 | } 214 | 215 | 216 | protected void onStart() { 217 | Log.d(TAG, "onStart() start"); 218 | super.onStart(); 219 | userCode("onStart"); 220 | Log.d(TAG, "onStart() complete"); 221 | } 222 | 223 | protected void onResume() { 224 | Log.d(TAG, "onResume() start"); 225 | super.onResume(); 226 | userCode("onResume"); 227 | Log.d(TAG, "onResume() complete"); 228 | } 229 | 230 | protected void onPause() { 231 | Log.d(TAG, "onPause() start"); 232 | super.onPause(); 233 | userCode("onPause"); 234 | Log.d(TAG, "onPause() complete"); 235 | } 236 | 237 | protected void onStop() { 238 | Log.d(TAG, "onStop() start"); 239 | super.onStop(); 240 | userCode("onStop"); 241 | Log.d(TAG, "onStop() complete"); 242 | } 243 | protected void onDestroy() { 244 | Log.d(TAG, "onDestroy() start"); 245 | super.onDestroy(); 246 | userCode("onDestroy"); 247 | Log.d(TAG, "onDestroy() complete"); 248 | } 249 | protected void onRestart() { 250 | Log.d(TAG, "onRestart() start"); 251 | super.onRestart(); 252 | userCode("onRestart"); 253 | Log.d(TAG, "onRestart() complete"); 254 | } 255 | public void onTopResumedActivityChanged (boolean isTopResumedActivity){ 256 | Log.d(TAG, "onTopResumedActivityChanged() start"); 257 | super.onTopResumedActivityChanged(isTopResumedActivity); 258 | userCode("onTopResumedActivityChanged", isTopResumedActivity); 259 | Log.d(TAG, "onTopResumedActivityChanged() complete"); 260 | } 261 | 262 | protected void onActivityResult(int requestCode, int resultCode, Intent data) 263 | { 264 | Log.d(TAG, "onActivityResult() start"); 265 | super.onActivityResult(requestCode, resultCode, data); 266 | userCode("onActivityResult", requestCode, resultCode, data); 267 | Log.d(TAG, "onActivityResult() complete"); 268 | } 269 | 270 | public void onConfigurationChanged(Configuration newConfig) { 271 | Log.d(TAG, "onConfigurationChanged() start"); 272 | super.onConfigurationChanged(newConfig); 273 | userCode("onConfigurationChanged", newConfig); 274 | Log.d(TAG, "onConfigurationChanged() complete"); 275 | } 276 | 277 | public boolean onOptionsItemSelected(MenuItem menuitem) { 278 | Log.d(TAG, "onOptionsItemSelected() start"); 279 | PyObject pyResult = userCode("onOptionsItemSelected", menuitem); 280 | boolean result = (pyResult == null) ? false : pyResult.toBoolean(); 281 | Log.d(TAG, "onOptionsItemSelected() complete"); 282 | return result; 283 | } 284 | 285 | public boolean onPrepareOptionsMenu(Menu menu) { 286 | Log.d(TAG, "onPrepareOptionsMenu() start"); 287 | PyObject pyResult = userCode("onPrepareOptionsMenu", menu); 288 | boolean result = (pyResult == null) ? false : pyResult.toBoolean(); 289 | Log.d(TAG, "onPrepareOptionsMenu() complete"); 290 | return result; 291 | } 292 | 293 | public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 294 | { 295 | Log.d(TAG, "onRequestPermissionsResult() start"); 296 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 297 | userCode("onRequestPermissionsResult", requestCode, permissions, grantResults); 298 | Log.d(TAG, "onRequestPermissionsResult() complete"); 299 | } 300 | 301 | private PyObject userCode(String methodName, Object... args) { 302 | if (pythonApp == null) { 303 | // Could be a non-graphical app such as Python-support-testbed. 304 | return null; 305 | } 306 | try { 307 | if (pythonApp.containsKey(methodName)) { 308 | return pythonApp.callAttr(methodName, args); 309 | } else { 310 | // Handle the case where the method doesn't exist 311 | return null; 312 | } 313 | } catch (PyException e) { 314 | if (e.getMessage().startsWith("NotImplementedError")) { 315 | return null; 316 | } 317 | throw e; 318 | } 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /android-toga/v0.0.8/java/PermissionUtils.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.pm.PackageManager; 6 | import androidx.core.app.ActivityCompat; 7 | import androidx.core.content.ContextCompat; 8 | import android.app.AlertDialog; 9 | import android.app.Activity; 10 | import android.os.Environment; 11 | import android.os.StrictMode; 12 | import android.app.AlarmManager; 13 | import android.app.PendingIntent; 14 | import android.content.Context; 15 | import android.content.Intent; 16 | import android.content.DialogInterface; 17 | import android.content.Intent; 18 | import android.os.Build; 19 | import android.provider.Settings; 20 | 21 | 22 | public class PermissionUtils { 23 | public static void requestStoragePermissions(Activity activity) { 24 | if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || 25 | ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 26 | 27 | ActivityCompat.requestPermissions(activity, new String[]{ 28 | Manifest.permission.READ_EXTERNAL_STORAGE, 29 | Manifest.permission.WRITE_EXTERNAL_STORAGE 30 | }, 1); 31 | } 32 | 33 | } 34 | public static void requestManageAllFilesPermission(Context context) { 35 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 36 | if (!Environment.isExternalStorageManager()) { 37 | Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION); 38 | context.startActivity(intent); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /android-toga/v0.0.8/java/VersionChecker.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.content.Context; 6 | import android.content.DialogInterface; 7 | import android.content.Intent; 8 | import android.net.ConnectivityManager; 9 | import android.net.NetworkInfo; 10 | import android.net.Uri; 11 | import android.os.Handler; 12 | import android.os.Looper; 13 | 14 | import java.io.BufferedReader; 15 | import java.io.InputStreamReader; 16 | import java.net.HttpURLConnection; 17 | import java.net.URL; 18 | 19 | public class VersionChecker { 20 | public static void checkVersion(Activity activity) { 21 | if (!isInternetAvailable(activity)) { 22 | showNoInternetDialog(activity); 23 | return; 24 | } 25 | 26 | new Thread(new Runnable() { 27 | @Override 28 | public void run() { 29 | String link = "https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/refs/heads/main/android-toga/version.txt"; 30 | String content = getContentFromURL(link); 31 | if (!"v0.0.8".equals(content.trim())) { 32 | // نمایش دیالوگ در نخ اصلی 33 | new Handler(Looper.getMainLooper()).post(new Runnable() { 34 | @Override 35 | public void run() { 36 | showUpdateDialog(activity); 37 | } 38 | }); 39 | } 40 | } 41 | }).start(); 42 | } 43 | 44 | private static boolean isInternetAvailable(Context context) { 45 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 46 | NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); 47 | return activeNetwork != null && activeNetwork.isConnectedOrConnecting(); 48 | } 49 | 50 | private static String getContentFromURL(String link) { 51 | StringBuilder content = new StringBuilder(); 52 | try { 53 | URL url = new URL(link); 54 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 55 | connection.setRequestMethod("GET"); 56 | BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); 57 | String inputLine; 58 | while ((inputLine = in.readLine()) != null) { 59 | content.append(inputLine); 60 | } 61 | in.close(); 62 | } catch (Exception e) { 63 | e.printStackTrace(); 64 | } 65 | return content.toString(); 66 | } 67 | 68 | private static void showUpdateDialog(Activity activity) { 69 | AlertDialog.Builder builder = new AlertDialog.Builder(activity); 70 | builder.setMessage("بروزرسانی در دسترس است") 71 | .setCancelable(false) 72 | .setPositiveButton("برو", new DialogInterface.OnClickListener() { 73 | public void onClick(DialogInterface dialog, int id) { 74 | Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/arshiacomplus/WarpScanner-android-GUI/")); 75 | activity.startActivity(browserIntent); 76 | } 77 | }) 78 | .setNegativeButton("بستن", new DialogInterface.OnClickListener() { 79 | public void onClick(DialogInterface dialog, int id) { 80 | dialog.dismiss(); 81 | } 82 | }); 83 | AlertDialog alert = builder.create(); 84 | alert.show(); 85 | } 86 | 87 | private static void showNoInternetDialog(Activity activity) { 88 | AlertDialog.Builder builder = new AlertDialog.Builder(activity); 89 | builder.setMessage("اتصال به اینترنت برقرار نیست. لطفاً اتصال خود را بررسی کنید.") 90 | .setCancelable(false) 91 | .setPositiveButton("باشه", new DialogInterface.OnClickListener() { 92 | public void onClick(DialogInterface dialog, int id) { 93 | dialog.dismiss(); 94 | } 95 | }); 96 | AlertDialog alert = builder.create(); 97 | alert.show(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /android-toga/v0.0.8/java/j: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android-toga/v0.0.9/java/.MainActivity.java.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/70fdbdc27e57615e2b26fb66f6edb95026e3db8d/android-toga/v0.0.9/java/.MainActivity.java.swp -------------------------------------------------------------------------------- /android-toga/v0.0.9/java/DrawHandlerView.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | public class DrawHandlerView extends android.view.View { 4 | private IDrawHandler drawHandler = null; 5 | 6 | public DrawHandlerView(android.content.Context context) { 7 | super(context); 8 | } 9 | 10 | public void setDrawHandler(IDrawHandler drawHandler) { 11 | this.drawHandler = drawHandler; 12 | } 13 | 14 | public void onDraw(android.graphics.Canvas canvas) { 15 | super.onDraw(canvas); 16 | drawHandler.handleDraw(canvas); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android-toga/v0.0.9/java/IDrawHandler.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | public interface IDrawHandler { 4 | public void handleDraw(android.graphics.Canvas canvas); 5 | } 6 | -------------------------------------------------------------------------------- /android-toga/v0.0.9/java/IPythonApp.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | 8 | public interface IPythonApp { 9 | void onCreate(); 10 | void onResume(); 11 | void onStart(); 12 | void onActivityResult(int requestCode, int resultCode, Intent data); 13 | void onConfigurationChanged(Configuration newConfig); 14 | boolean onOptionsItemSelected(MenuItem menuitem); 15 | boolean onPrepareOptionsMenu(Menu menu); 16 | // There's no need to add any more methods to this interface, as the Python 17 | // call mechanism no longer uses it. 18 | } 19 | -------------------------------------------------------------------------------- /android-toga/v0.0.9/java/LTRConfig.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.app.Activity; 4 | import android.os.Build; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.LinearLayout; 8 | 9 | public class LTRConfig { 10 | 11 | public static void applyLTR(Activity activity) { 12 | ViewGroup rootView = (ViewGroup) activity.findViewById(android.R.id.content); 13 | setLTR(rootView); 14 | } 15 | 16 | private static void setLTR(ViewGroup viewGroup) { 17 | for (int i = 0; i < viewGroup.getChildCount(); i++) { 18 | View view = viewGroup.getChildAt(i); 19 | if (view instanceof ViewGroup) { 20 | setLTR((ViewGroup) view); 21 | } 22 | } 23 | 24 | // اگر نسخه اندروید بالاتر از نوقا است، جهت LTR را تنظیم می‌کنیم 25 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 26 | viewGroup.setLayoutDirection(View.LAYOUT_DIRECTION_LTR); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android-toga/v0.0.9/java/LoggingConfig.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.util.logging.FileHandler; 8 | import java.util.logging.Handler; 9 | import java.util.logging.Level; 10 | import java.util.logging.Logger; 11 | import java.util.logging.SimpleFormatter; 12 | 13 | public class LoggingConfig { 14 | 15 | public static void setup(Context context) { 16 | String logPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/wwarpscanner/app_java.txt"; 17 | File logFile = new File(logPath); 18 | 19 | if (logFile.exists()) { 20 | logFile.delete(); 21 | } 22 | 23 | try { 24 | if (logFile.createNewFile()) { 25 | Handler fileHandler = new FileHandler(logPath, true); 26 | fileHandler.setFormatter(new SimpleFormatter()); 27 | Logger logger = Logger.getLogger(context.getPackageName()); 28 | logger.addHandler(fileHandler); 29 | logger.setLevel(Level.ALL); 30 | Logger.getLogger("").addHandler(fileHandler); // Add handler to root logger 31 | } 32 | } catch (IOException e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /android-toga/v0.0.9/java/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.content.Intent; 4 | import android.content.res.Configuration; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | import android.widget.LinearLayout; 10 | import android.content.Context; 11 | import android.net.Uri; 12 | import android.os.Handler; 13 | import android.os.Looper; 14 | import android.content.ContentResolver; 15 | import android.content.ContentUris; 16 | import android.database.Cursor; 17 | import android.net.Uri; 18 | import android.provider.MediaStore; 19 | 20 | import androidx.appcompat.app.AppCompatActivity; 21 | 22 | import com.chaquo.python.Kwarg; 23 | import com.chaquo.python.PyException; 24 | import com.chaquo.python.PyObject; 25 | import com.chaquo.python.Python; 26 | import com.chaquo.python.android.AndroidPlatform; 27 | 28 | import java.util.List; 29 | import java.util.logging.Logger; 30 | import java.io.File; 31 | import org.json.JSONArray; 32 | import org.json.JSONException; 33 | 34 | import com.arshiacomplus.warpscanner.warpscanner.R; 35 | 36 | import androidx.appcompat.app.AppCompatActivity; 37 | import com.google.android.material.button.MaterialButton; 38 | import android.app.AlertDialog; 39 | import android.app.Activity; 40 | import android.os.Environment; 41 | import android.os.StrictMode; 42 | import android.app.AlarmManager; 43 | import android.app.PendingIntent; 44 | import android.content.Context; 45 | import android.content.Intent; 46 | import android.content.DialogInterface; 47 | 48 | public class MainActivity extends AppCompatActivity { 49 | 50 | 51 | 52 | // To profile app launch, use `adb -s MainActivity`; look for "onCreate() start" and "onResume() completed". 53 | private String TAG = "MainActivity"; 54 | private static PyObject pythonApp; 55 | private static Logger logger; 56 | 57 | 58 | 59 | /** 60 | * This method is called by `app.__main__` over JNI in Python when the BeeWare 61 | * app launches. 62 | * 63 | * @param app 64 | */ 65 | @SuppressWarnings("unused") 66 | public static void setPythonApp(IPythonApp app) { 67 | 68 | pythonApp = PyObject.fromJava(app); 69 | 70 | } 71 | 72 | /** 73 | * We store the MainActivity instance on the *class* so that we can easily 74 | * access it from Python. 75 | */ 76 | public static MainActivity singletonThis; 77 | 78 | protected void onCreate(Bundle savedInstanceState) { 79 | Log.d(TAG, "onCreate() start"); 80 | // Change away from the splash screen theme to the app theme. 81 | setTheme(R.style.AppTheme); 82 | super.onCreate(savedInstanceState); 83 | LinearLayout layout = new LinearLayout(this); 84 | this.setContentView(layout); 85 | singletonThis = this; 86 | 87 | 88 | 89 | PermissionUtils.requestStoragePermissions(this); 90 | try { 91 | PermissionUtils.requestManageAllFilesPermission(this); 92 | } catch (Exception e) { 93 | e.printStackTrace(); 94 | } 95 | 96 | 97 | Context appContext = getApplicationContext(); 98 | LoggingConfig.setup(appContext); 99 | logger = Logger.getLogger(appContext.getPackageName()); 100 | String wwpath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/wwarpscanner"; 101 | File warppath = new File(wwpath); 102 | 103 | final Context context = this; 104 | 105 | 106 | Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 107 | @Override 108 | public void uncaughtException(Thread t, Throwable e) { 109 | logger.severe("Uncaught exception: " + e.getMessage()); 110 | for (StackTraceElement element : e.getStackTrace()) { 111 | logger.severe(element.toString()); 112 | } 113 | 114 | } 115 | 116 | }); 117 | 118 | 119 | logger.info("Application started"); 120 | 121 | 122 | 123 | 124 | 125 | // Call PermissionUtils to request permissions 126 | 127 | 128 | 129 | LTRConfig.applyLTR(this); 130 | 131 | // فراخوانی بررسی نسخه 132 | VersionChecker.checkVersion(this); 133 | 134 | 135 | // اکنون می‌توانید لاگ بگیرید 136 | Logger logger = Logger.getLogger(getPackageName()); 137 | logger.info("Application started"); 138 | 139 | 140 | Python py; 141 | if (Python.isStarted()) { 142 | Log.d(TAG, "Python already started"); 143 | py = Python.getInstance(); 144 | } else { 145 | Log.d(TAG, "Starting Python"); 146 | AndroidPlatform platform = new AndroidPlatform(this); 147 | platform.redirectStdioToLogcat(); 148 | Python.start(platform); 149 | py = Python.getInstance(); 150 | 151 | String argvStr = getIntent().getStringExtra("org.beeware.ARGV"); 152 | if (argvStr != null) { 153 | try { 154 | JSONArray argvJson = new JSONArray(argvStr); 155 | List sysArgv = py.getModule("sys").get("argv").asList(); 156 | for (int i = 0; i < argvJson.length(); i++) { 157 | sysArgv.add(PyObject.fromJava(argvJson.getString(i))); 158 | } 159 | } catch (JSONException e) { 160 | throw new RuntimeException(e); 161 | } 162 | } 163 | } 164 | 165 | Log.d(TAG, "Running main module " + getString(R.string.main_module)); 166 | py.getModule("runpy").callAttr( 167 | "run_module", 168 | getString(R.string.main_module), 169 | new Kwarg("run_name", "__main__"), 170 | new Kwarg("alter_sys", true) 171 | ); 172 | 173 | userCode("onCreate"); 174 | Log.d(TAG, "onCreate() complete"); 175 | } 176 | 177 | private static void showAlertAndOpenFileManager(Context context, String path) { 178 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 179 | builder.setMessage("لطفاً این فایل را حذف کنید") 180 | .setCancelable(false) 181 | .setPositiveButton("برو", new DialogInterface.OnClickListener() { 182 | public void onClick(DialogInterface dialog, int id) { 183 | Intent intent = new Intent(Intent.ACTION_VIEW); 184 | Uri uri = Uri.parse("file://" + path); 185 | intent.setDataAndType(uri, "resource/folder"); 186 | context.startActivity(Intent.createChooser(intent, "Open folder")); 187 | } 188 | }) 189 | .setNegativeButton("بستن", new DialogInterface.OnClickListener() { 190 | public void onClick(DialogInterface dialog, int id) { 191 | dialog.dismiss(); 192 | } 193 | }); 194 | AlertDialog alert = builder.create(); 195 | alert.show(); 196 | } 197 | 198 | 199 | private static void forceDeleteWarpscannerFolder(File folder) { 200 | if (folder.isDirectory()) { 201 | File[] children = folder.listFiles(); 202 | if (children != null) { 203 | for (File child : children) { 204 | forceDeleteWarpscannerFolder(child); 205 | } 206 | } 207 | } 208 | if (!folder.delete()) { 209 | throw new RuntimeException("Failed to delete folder: " + folder.getAbsolutePath()); 210 | } else { 211 | System.out.println("Folder " + folder.getAbsolutePath() + " deleted successfully."); 212 | } 213 | } 214 | 215 | 216 | protected void onStart() { 217 | Log.d(TAG, "onStart() start"); 218 | super.onStart(); 219 | userCode("onStart"); 220 | Log.d(TAG, "onStart() complete"); 221 | } 222 | 223 | protected void onResume() { 224 | Log.d(TAG, "onResume() start"); 225 | super.onResume(); 226 | userCode("onResume"); 227 | Log.d(TAG, "onResume() complete"); 228 | } 229 | 230 | protected void onPause() { 231 | Log.d(TAG, "onPause() start"); 232 | super.onPause(); 233 | userCode("onPause"); 234 | Log.d(TAG, "onPause() complete"); 235 | } 236 | 237 | protected void onStop() { 238 | Log.d(TAG, "onStop() start"); 239 | super.onStop(); 240 | userCode("onStop"); 241 | Log.d(TAG, "onStop() complete"); 242 | } 243 | protected void onDestroy() { 244 | Log.d(TAG, "onDestroy() start"); 245 | super.onDestroy(); 246 | userCode("onDestroy"); 247 | Log.d(TAG, "onDestroy() complete"); 248 | } 249 | protected void onRestart() { 250 | Log.d(TAG, "onRestart() start"); 251 | super.onRestart(); 252 | userCode("onRestart"); 253 | Log.d(TAG, "onRestart() complete"); 254 | } 255 | public void onTopResumedActivityChanged (boolean isTopResumedActivity){ 256 | Log.d(TAG, "onTopResumedActivityChanged() start"); 257 | super.onTopResumedActivityChanged(isTopResumedActivity); 258 | userCode("onTopResumedActivityChanged", isTopResumedActivity); 259 | Log.d(TAG, "onTopResumedActivityChanged() complete"); 260 | } 261 | 262 | protected void onActivityResult(int requestCode, int resultCode, Intent data) 263 | { 264 | Log.d(TAG, "onActivityResult() start"); 265 | super.onActivityResult(requestCode, resultCode, data); 266 | userCode("onActivityResult", requestCode, resultCode, data); 267 | Log.d(TAG, "onActivityResult() complete"); 268 | } 269 | 270 | public void onConfigurationChanged(Configuration newConfig) { 271 | Log.d(TAG, "onConfigurationChanged() start"); 272 | super.onConfigurationChanged(newConfig); 273 | userCode("onConfigurationChanged", newConfig); 274 | Log.d(TAG, "onConfigurationChanged() complete"); 275 | } 276 | 277 | public boolean onOptionsItemSelected(MenuItem menuitem) { 278 | Log.d(TAG, "onOptionsItemSelected() start"); 279 | PyObject pyResult = userCode("onOptionsItemSelected", menuitem); 280 | boolean result = (pyResult == null) ? false : pyResult.toBoolean(); 281 | Log.d(TAG, "onOptionsItemSelected() complete"); 282 | return result; 283 | } 284 | 285 | public boolean onPrepareOptionsMenu(Menu menu) { 286 | Log.d(TAG, "onPrepareOptionsMenu() start"); 287 | PyObject pyResult = userCode("onPrepareOptionsMenu", menu); 288 | boolean result = (pyResult == null) ? false : pyResult.toBoolean(); 289 | Log.d(TAG, "onPrepareOptionsMenu() complete"); 290 | return result; 291 | } 292 | 293 | public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 294 | { 295 | Log.d(TAG, "onRequestPermissionsResult() start"); 296 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 297 | userCode("onRequestPermissionsResult", requestCode, permissions, grantResults); 298 | Log.d(TAG, "onRequestPermissionsResult() complete"); 299 | } 300 | 301 | private PyObject userCode(String methodName, Object... args) { 302 | if (pythonApp == null) { 303 | // Could be a non-graphical app such as Python-support-testbed. 304 | return null; 305 | } 306 | try { 307 | if (pythonApp.containsKey(methodName)) { 308 | return pythonApp.callAttr(methodName, args); 309 | } else { 310 | // Handle the case where the method doesn't exist 311 | return null; 312 | } 313 | } catch (PyException e) { 314 | if (e.getMessage().startsWith("NotImplementedError")) { 315 | return null; 316 | } 317 | throw e; 318 | } 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /android-toga/v0.0.9/java/PermissionUtils.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.pm.PackageManager; 6 | import androidx.core.app.ActivityCompat; 7 | import androidx.core.content.ContextCompat; 8 | import android.app.AlertDialog; 9 | import android.app.Activity; 10 | import android.os.Environment; 11 | import android.os.StrictMode; 12 | import android.app.AlarmManager; 13 | import android.app.PendingIntent; 14 | import android.content.Context; 15 | import android.content.Intent; 16 | import android.content.DialogInterface; 17 | import android.content.Intent; 18 | import android.os.Build; 19 | import android.provider.Settings; 20 | 21 | 22 | public class PermissionUtils { 23 | public static void requestStoragePermissions(Activity activity) { 24 | if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || 25 | ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 26 | 27 | ActivityCompat.requestPermissions(activity, new String[]{ 28 | Manifest.permission.READ_EXTERNAL_STORAGE, 29 | Manifest.permission.WRITE_EXTERNAL_STORAGE 30 | }, 1); 31 | } 32 | 33 | } 34 | public static void requestManageAllFilesPermission(Context context) { 35 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 36 | if (!Environment.isExternalStorageManager()) { 37 | Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION); 38 | context.startActivity(intent); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /android-toga/v0.0.9/java/VersionChecker.java: -------------------------------------------------------------------------------- 1 | package org.beeware.android; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.content.Context; 6 | import android.content.DialogInterface; 7 | import android.content.Intent; 8 | import android.net.ConnectivityManager; 9 | import android.net.NetworkInfo; 10 | import android.net.Uri; 11 | import android.os.Handler; 12 | import android.os.Looper; 13 | 14 | import java.io.BufferedReader; 15 | import java.io.InputStreamReader; 16 | import java.net.HttpURLConnection; 17 | import java.net.URL; 18 | 19 | public class VersionChecker { 20 | public static void checkVersion(Activity activity) { 21 | if (!isInternetAvailable(activity)) { 22 | showNoInternetDialog(activity); 23 | return; 24 | } 25 | 26 | new Thread(new Runnable() { 27 | @Override 28 | public void run() { 29 | String link = "https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/refs/heads/main/android-toga/version.txt"; 30 | String content = getContentFromURL(link); 31 | if (!"v0.0.9".equals(content.trim())) { 32 | // نمایش دیالوگ در نخ اصلی 33 | new Handler(Looper.getMainLooper()).post(new Runnable() { 34 | @Override 35 | public void run() { 36 | showUpdateDialog(activity); 37 | } 38 | }); 39 | } 40 | } 41 | }).start(); 42 | } 43 | 44 | private static boolean isInternetAvailable(Context context) { 45 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 46 | NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); 47 | return activeNetwork != null && activeNetwork.isConnectedOrConnecting(); 48 | } 49 | 50 | private static String getContentFromURL(String link) { 51 | StringBuilder content = new StringBuilder(); 52 | try { 53 | URL url = new URL(link); 54 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 55 | connection.setRequestMethod("GET"); 56 | BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); 57 | String inputLine; 58 | while ((inputLine = in.readLine()) != null) { 59 | content.append(inputLine); 60 | } 61 | in.close(); 62 | } catch (Exception e) { 63 | e.printStackTrace(); 64 | } 65 | return content.toString(); 66 | } 67 | 68 | private static void showUpdateDialog(Activity activity) { 69 | AlertDialog.Builder builder = new AlertDialog.Builder(activity); 70 | builder.setMessage("بروزرسانی در دسترس است") 71 | .setCancelable(false) 72 | .setPositiveButton("برو", new DialogInterface.OnClickListener() { 73 | public void onClick(DialogInterface dialog, int id) { 74 | Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/arshiacomplus/WarpScanner-android-GUI/")); 75 | activity.startActivity(browserIntent); 76 | } 77 | }) 78 | .setNegativeButton("بستن", new DialogInterface.OnClickListener() { 79 | public void onClick(DialogInterface dialog, int id) { 80 | dialog.dismiss(); 81 | } 82 | }); 83 | AlertDialog alert = builder.create(); 84 | alert.show(); 85 | } 86 | 87 | private static void showNoInternetDialog(Activity activity) { 88 | AlertDialog.Builder builder = new AlertDialog.Builder(activity); 89 | builder.setMessage("اتصال به اینترنت برقرار نیست. لطفاً اتصال خود را بررسی کنید.") 90 | .setCancelable(false) 91 | .setPositiveButton("باشه", new DialogInterface.OnClickListener() { 92 | public void onClick(DialogInterface dialog, int id) { 93 | dialog.dismiss(); 94 | } 95 | }); 96 | AlertDialog alert = builder.create(); 97 | alert.show(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /android-toga/v0.1.0/WarpScanner/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 25 | 26 | 27 | 28 | 36 | 40 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 53 | 56 | 57 | -------------------------------------------------------------------------------- /android-toga/v0.1.0/WarpScanner/WarpScanner/README.md: -------------------------------------------------------------------------------- 1 | # Warpscanner app 2 | 3 | ## Run the app 4 | 5 | ### uv 6 | 7 | Run as a desktop app: 8 | 9 | ``` 10 | uv run flet run 11 | ``` 12 | 13 | Run as a web app: 14 | 15 | ``` 16 | uv run flet run --web 17 | ``` 18 | 19 | ### Poetry 20 | 21 | Install dependencies from `pyproject.toml`: 22 | 23 | ``` 24 | poetry install 25 | ``` 26 | 27 | Run as a desktop app: 28 | 29 | ``` 30 | poetry run flet run 31 | ``` 32 | 33 | Run as a web app: 34 | 35 | ``` 36 | poetry run flet run --web 37 | ``` 38 | 39 | For more details on running the app, refer to the [Getting Started Guide](https://flet.dev/docs/getting-started/). 40 | 41 | ## Build the app 42 | 43 | ### Android 44 | 45 | ``` 46 | flet build apk -v 47 | ``` 48 | 49 | For more details on building and signing `.apk` or `.aab`, refer to the [Android Packaging Guide](https://flet.dev/docs/publish/android/). 50 | 51 | ### iOS 52 | 53 | ``` 54 | flet build ipa -v 55 | ``` 56 | 57 | For more details on building and signing `.ipa`, refer to the [iOS Packaging Guide](https://flet.dev/docs/publish/ios/). 58 | 59 | ### macOS 60 | 61 | ``` 62 | flet build macos -v 63 | ``` 64 | 65 | For more details on building macOS package, refer to the [macOS Packaging Guide](https://flet.dev/docs/publish/macos/). 66 | 67 | ### Linux 68 | 69 | ``` 70 | flet build linux -v 71 | ``` 72 | 73 | For more details on building Linux package, refer to the [Linux Packaging Guide](https://flet.dev/docs/publish/linux/). 74 | 75 | ### Windows 76 | 77 | ``` 78 | flet build windows -v 79 | ``` 80 | 81 | For more details on building Windows package, refer to the [Windows Packaging Guide](https://flet.dev/docs/publish/windows/). -------------------------------------------------------------------------------- /android-toga/v0.1.0/WarpScanner/WarpScanner/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "WarpScanner" 3 | version = "0.1.0" 4 | description = "This is a WarpScanner for android" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | authors = [ 8 | { name = "arshiacomplus", email = "arshiacomplus@gmail.com" } 9 | ] 10 | dependencies = [ 11 | "flet==0.27.6", 12 | "icmplib", 13 | "requests", 14 | "retrying" 15 | ] 16 | 17 | [tool.flet] 18 | # org name in reverse domain name notation, e.g. "com.mycompany". 19 | # Combined with project.name to build bundle ID for iOS and Android apps 20 | org = "com.arshiacomplus.warpscanner" 21 | 22 | # project display name that is used as an app title on Android and iOS home screens, 23 | # shown in window titles and about app dialogs on desktop. 24 | product = "warpscanner" 25 | 26 | # company name to display in about app dialogs 27 | company = "" 28 | 29 | # copyright text to display in about app dialogs 30 | copyright = "Copyright (C) 2025 by arshiacomplus" 31 | 32 | [tool.flet.app] 33 | path = "src" 34 | 35 | [tool.uv] 36 | dev-dependencies = [ 37 | "flet[all]==0.27.6", 38 | ] 39 | 40 | [tool.poetry] 41 | package-mode = false 42 | 43 | [tool.poetry.group.dev.dependencies] 44 | flet = {extras = ["all"], version = "0.27.6"} -------------------------------------------------------------------------------- /android-toga/v0.1.0/WarpScanner/WarpScanner/src/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/70fdbdc27e57615e2b26fb66f6edb95026e3db8d/android-toga/v0.1.0/WarpScanner/WarpScanner/src/assets/icon.png -------------------------------------------------------------------------------- /android-toga/v0.1.0/WarpScanner/WarpScanner/src/assets/splash_android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arshiacomplus/WarpScanner-android-GUI/70fdbdc27e57615e2b26fb66f6edb95026e3db8d/android-toga/v0.1.0/WarpScanner/WarpScanner/src/assets/splash_android.png -------------------------------------------------------------------------------- /android-toga/v0.1.0/WarpScanner/kotlin/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.arshiacomplus.warpscanner.warpscanner // << پکیج نیم شما 2 | 3 | import android.os.Build // <<<< این import برای چک کردن نسخه اندروید لازم است >>>> 4 | import android.os.Bundle 5 | import android.os.Environment // <<<< این import برای چک کردن نتیجه MANAGE_ALL_FILES لازم است >>>> 6 | import android.util.Log 7 | import androidx.annotation.NonNull 8 | import io.flutter.embedding.android.FlutterActivity 9 | // import java.lang.ref.WeakReference // Alternative approach (less needed here) 10 | 11 | class MainActivity: FlutterActivity() { 12 | 13 | companion object { 14 | private const val TAG = "MainActivityKt" 15 | @JvmField 16 | var currentActivityInstance: MainActivity? = null 17 | } 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | currentActivityInstance = this 22 | Log.i(TAG, "MainActivity instance created and static reference stored: $this") 23 | 24 | // --- درخواست مجوز مدیریت تمام فایل‌ها (MANAGE_EXTERNAL_STORAGE) --- 25 | // این کد فقط در اندروید 11 (API 30) و بالاتر اجرا می‌شود. 26 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 27 | // فقط اگر مجوز از قبل داده نشده، کاربر را به تنظیمات بفرست 28 | if (!Environment.isExternalStorageManager()) { 29 | Log.d(TAG, "[Permission Request] Need MANAGE_EXTERNAL_STORAGE. Opening settings...") 30 | try { 31 | // فراخوانی متد استاتیک از object PermissionUtils 32 | // 'this' به عنوان Context و Activity پاس داده می‌شود. 33 | // فرض بر این است که PermissionUtils.kt در همین پکیج وجود دارد 34 | PermissionUtils.requestManageAllFilesPermission(this, this) 35 | // کاربر به تنظیمات هدایت می‌شود. نتیجه مستقیماً برنمی‌گردد. 36 | } catch (e: Exception) { // گرفتن Exception کلی برای اطمینان 37 | Log.e(TAG, "[Permission Request] Error calling requestManageAllFilesPermission", e) 38 | } 39 | } else { 40 | Log.i(TAG, "[Permission Check] MANAGE_EXTERNAL_STORAGE already granted.") 41 | } 42 | } else { 43 | // برای نسخه‌های پایین‌تر، این مجوز لازم نیست یا وجود ندارد. 44 | Log.i(TAG, "[Permission Check] MANAGE_EXTERNAL_STORAGE not applicable below Android 11.") 45 | } 46 | // -------------------------------------------------------------------- 47 | } 48 | 49 | // این متد برای دریافت نتیجه درخواست‌های استاندارد (مثل Storage) است 50 | // و نتیجه MANAGE_EXTERNAL_STORAGE را دریافت *نمی‌کند*. 51 | override fun onRequestPermissionsResult( 52 | requestCode: Int, 53 | @NonNull permissions: Array, 54 | @NonNull grantResults: IntArray 55 | ) { 56 | Log.d(TAG, "onRequestPermissionsResult received for requestCode: $requestCode") 57 | super.onRequestPermissionsResult(requestCode, permissions, grantResults) 58 | 59 | // ارسال نتیجه به PermissionHelper (برای درخواست‌هایی که از پایتون می‌آیند) 60 | try { 61 | PermissionHelper.handleRequestPermissionsResult(requestCode, permissions, grantResults) 62 | Log.d(TAG, "Passed permission result to PermissionHelper.") 63 | } catch (e: ClassNotFoundException) { 64 | Log.e(TAG, "FATAL: PermissionHelper class not found!", e) 65 | } catch (e: NoSuchMethodError) { 66 | Log.e(TAG, "FATAL: handleRequestPermissionsResult method not found or mismatch!", e) 67 | } catch (t: Throwable) { 68 | Log.e(TAG, "Unexpected error calling PermissionHelper.handleRequestPermissionsResult", t) 69 | } 70 | } 71 | 72 | // **مهم:** برای چک کردن نتیجه MANAGE_EXTERNAL_STORAGE 73 | // باید از onResume استفاده کنید. 74 | override fun onResume() { 75 | super.onResume() 76 | Log.d(TAG, "onResume called.") 77 | // چک کردن وضعیت مجوز مدیریت فایل‌ها وقتی کاربر به برنامه برمی‌گردد 78 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 79 | val hasManagePermission = Environment.isExternalStorageManager() 80 | Log.i(TAG, "[Permission Check onResume] MANAGE_EXTERNAL_STORAGE Granted: $hasManagePermission") 81 | // اینجا می‌توانید بر اساس وضعیت مجوز، کاری انجام دهید 82 | // مثلاً یک پیام به کاربر نشان دهید یا قابلیتی را فعال/غیرفعال کنید. 83 | // توجه: ارسال مستقیم نتیجه به پایتون از اینجا کمی پیچیده‌تر است. 84 | } 85 | } 86 | 87 | 88 | override fun onDestroy() { 89 | super.onDestroy() 90 | Log.i(TAG, "MainActivity instance being destroyed: $this") 91 | if (currentActivityInstance == this) { 92 | currentActivityInstance = null 93 | Log.i(TAG, "Static MainActivity instance reference cleared.") 94 | } else { 95 | Log.w(TAG, "onDestroy called, but static instance was already different or null.") 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /android-toga/v0.1.0/WarpScanner/kotlin/PermissionCallback.kt: -------------------------------------------------------------------------------- 1 | package com.arshiacomplus.warpscanner.warpscanner // << پکیج نیم شما 2 | 3 | interface PermissionCallback { 4 | fun onPermissionResult(allGranted: Boolean) 5 | } -------------------------------------------------------------------------------- /android-toga/v0.1.0/WarpScanner/kotlin/PermissionHelper.kt: -------------------------------------------------------------------------------- 1 | package com.arshiacomplus.warpscanner.warpscanner // << پکیج نیم شما 2 | 3 | import android.app.Activity 4 | import android.content.pm.PackageManager 5 | import android.util.Log 6 | import androidx.core.app.ActivityCompat 7 | import androidx.core.content.ContextCompat 8 | import android.Manifest 9 | 10 | object PermissionHelper { // <<<< باید object یا class باشد، نه چیز دیگر 11 | 12 | private const val TAG = "PermissionHelperKt" 13 | private const val STORAGE_PERMISSION_REQUEST_CODE = 101 14 | 15 | @Volatile 16 | private var permissionCallback: PermissionCallback? = null 17 | 18 | @JvmStatic 19 | fun requestStoragePermission(activity: Activity, callback: PermissionCallback) { 20 | Log.d(TAG, "requestStoragePermission called from Python") 21 | this.permissionCallback = callback 22 | 23 | val permissionsToRequest = arrayOf( 24 | Manifest.permission.READ_EXTERNAL_STORAGE, 25 | Manifest.permission.WRITE_EXTERNAL_STORAGE 26 | ) 27 | 28 | val permissionsNeeded = permissionsToRequest.filter { 29 | ContextCompat.checkSelfPermission(activity, it) != PackageManager.PERMISSION_GRANTED 30 | }.toTypedArray() 31 | 32 | if (permissionsNeeded.isEmpty()) { 33 | Log.d(TAG, "All storage permissions already granted.") 34 | try { 35 | callback.onPermissionResult(true) 36 | } catch (t: Throwable) { 37 | Log.e(TAG, "Error calling callback.onPermissionResult (permissions already granted)", t) 38 | } finally { 39 | this.permissionCallback = null 40 | } 41 | } else { 42 | Log.d(TAG, "Requesting permissions: ${permissionsNeeded.joinToString()}") 43 | ActivityCompat.requestPermissions( 44 | activity, 45 | permissionsNeeded, 46 | STORAGE_PERMISSION_REQUEST_CODE 47 | ) 48 | } 49 | } 50 | 51 | @JvmStatic 52 | fun handleRequestPermissionsResult( 53 | requestCode: Int, 54 | permissions: Array, 55 | grantResults: IntArray 56 | ) { 57 | Log.d(TAG, "handleRequestPermissionsResult called from MainActivity for requestCode: $requestCode") 58 | if (requestCode == STORAGE_PERMISSION_REQUEST_CODE) { 59 | val callback = this.permissionCallback 60 | if (callback != null) { 61 | val allGranted = grantResults.isNotEmpty() && grantResults.all { it == PackageManager.PERMISSION_GRANTED } 62 | Log.d(TAG, "Permission result: allGranted = $allGranted") 63 | try { 64 | callback.onPermissionResult(allGranted) 65 | } catch (t: Throwable) { 66 | Log.e(TAG, "Error calling callback.onPermissionResult (after request)", t) 67 | } finally { 68 | this.permissionCallback = null 69 | Log.d(TAG, "Permission callback cleared.") 70 | } 71 | } else { 72 | Log.w(TAG, "permissionCallback was null when handling result.") 73 | } 74 | } else { 75 | Log.w(TAG, "Received result for unknown requestCode: $requestCode") 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /android-toga/v0.1.0/WarpScanner/kotlin/PermissionUtils.kt: -------------------------------------------------------------------------------- 1 | package com.arshiacomplus.warpscanner.warpscanner // << پکیج نیم خودت رو اینجا بذار 2 | 3 | import android.Manifest 4 | import android.app.Activity 5 | import android.content.ActivityNotFoundException 6 | import android.content.Context 7 | import android.content.Intent 8 | import android.content.pm.PackageManager 9 | import android.net.Uri // لازم برای Intent جدیدتر در MANAGE_ALL_FILES 10 | import android.os.Build 11 | import android.os.Environment 12 | import android.provider.Settings 13 | import android.util.Log // برای لاگ‌گیری خطا 14 | import androidx.annotation.RequiresApi // برای نشان دادن نیاز به نسخه API 15 | import androidx.core.app.ActivityCompat 16 | import androidx.core.content.ContextCompat 17 | 18 | /** 19 | * کلاس کمکی برای مدیریت درخواست‌های مجوز در اندروید. 20 | * از object به جای class استفاده شده چون فقط شامل متدهای استاتیک-مانند است. 21 | */ 22 | object PermissionUtils { 23 | 24 | private const val TAG = "PermissionUtilsKt" // تگ برای لاگ 25 | private const val STORAGE_PERMISSION_REQUEST_CODE = 101 // کد دلخواه برای درخواست Storage 26 | 27 | /** 28 | * مجوزهای استاندارد خواندن و نوشتن حافظه خارجی را درخواست می‌کند. 29 | * (READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE) 30 | * این متد برای اندروید 10 به پایین حیاتی است و در نسخه‌های بالاتر هم ممکن است لازم باشد. 31 | * 32 | * @param activity اکتیویتی فعلی که درخواست از آن ارسال می‌شود. 33 | */ 34 | @JvmStatic // این انوتیشن اجازه می‌دهد متد مانند یک متد استاتیک جاوا فراخوانی شود 35 | fun requestStoragePermissions(activity: Activity) { 36 | // بررسی اینکه آیا مجوزها قبلاً داده شده‌اند یا نه 37 | val readPermissionGranted = ContextCompat.checkSelfPermission( 38 | activity, Manifest.permission.READ_EXTERNAL_STORAGE 39 | ) == PackageManager.PERMISSION_GRANTED 40 | 41 | val writePermissionGranted = ContextCompat.checkSelfPermission( 42 | activity, Manifest.permission.WRITE_EXTERNAL_STORAGE 43 | ) == PackageManager.PERMISSION_GRANTED 44 | 45 | // اگر هر کدام از مجوزها داده نشده باشند 46 | if (!readPermissionGranted || !writePermissionGranted) { 47 | Log.d(TAG, "Requesting standard storage permissions (READ/WRITE)...") 48 | // لیست مجوزهایی که باید درخواست شوند 49 | val permissionsToRequest = arrayOf( 50 | Manifest.permission.READ_EXTERNAL_STORAGE, 51 | Manifest.permission.WRITE_EXTERNAL_STORAGE 52 | ) 53 | // نمایش دیالوگ سیستمی درخواست مجوز 54 | ActivityCompat.requestPermissions( 55 | activity, 56 | permissionsToRequest, 57 | STORAGE_PERMISSION_REQUEST_CODE // کد برای شناسایی نتیجه در onRequestPermissionsResult 58 | ) 59 | } else { 60 | Log.d(TAG, "Standard storage permissions (READ/WRITE) already granted.") 61 | // اگر مجوزها از قبل داده شده‌اند، کاری انجام نمی‌دهیم 62 | // (می‌توان یک callback برای اطلاع‌رسانی داشت) 63 | } 64 | } 65 | 66 | /** 67 | * درخواست مجوز MANAGE_EXTERNAL_STORAGE برای اندروید 11 (API 30) و بالاتر. 68 | * این مجوز دسترسی کامل به حافظه خارجی می‌دهد اما نیاز به بررسی دقیق در گوگل پلی دارد. 69 | * این متد کاربر را به صفحه تنظیمات سیستم هدایت می‌کند. 70 | * 71 | * @param context کانتکست برنامه (می‌تواند Activity یا Application Context باشد). 72 | * @param activity (اختیاری) اگر می‌خواهید از startActivityForResult استفاده کنید (در اینجا لازم نیست). 73 | */ 74 | @JvmStatic 75 | @RequiresApi(Build.VERSION_CODES.R) // این متد فقط برای اندروید 11 و بالاتر معنی دارد 76 | fun requestManageAllFilesPermission(context: Context, activity: Activity? = null) { 77 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 78 | // بررسی اینکه آیا برنامه در حال حاضر مجوز MANAGE_EXTERNAL_STORAGE را دارد؟ 79 | if (!Environment.isExternalStorageManager()) { 80 | Log.d(TAG, "Requesting MANAGE_EXTERNAL_STORAGE permission by opening settings...") 81 | try { 82 | // ایجاد Intent برای باز کردن صفحه تنظیمات مدیریت فایل‌ها 83 | val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) 84 | // اضافه کردن پکیج نیم برنامه به URI تا مستقیماً به تنظیمات همین برنامه برود 85 | intent.data = Uri.parse("package:${context.packageName}") 86 | 87 | // اگر activity داریم، از آن برای شروع استفاده می‌کنیم (بهتر است) 88 | if (activity != null) { 89 | activity.startActivity(intent) // می‌توان از startActivityForResult هم استفاده کرد اگر نیاز به نتیجه فوری باشد 90 | } else { 91 | // اگر فقط context داریم (مثلاً از سرویس)، فلگ NEW_TASK لازم است 92 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 93 | context.startActivity(intent) 94 | } 95 | Log.d(TAG, "Settings activity for MANAGE_ALL_FILES opened.") 96 | // توجه: نتیجه این درخواست مستقیماً به برنامه برنمی‌گردد. 97 | // باید دوباره Environment.isExternalStorageManager() را چک کنید 98 | // وقتی کاربر به برنامه برمی‌گردد (مثلاً در onResume اکتیویتی). 99 | 100 | } catch (e: ActivityNotFoundException) { 101 | // اگر صفحه تنظیمات مربوطه در دستگاه وجود نداشت (بسیار نادر) 102 | Log.e(TAG, "Activity not found to handle MANAGE_ALL_FILES_ACCESS_PERMISSION intent.", e) 103 | // می‌توانید یک Intent عمومی‌تر برای تنظیمات برنامه امتحان کنید 104 | // val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) 105 | // intent.data = Uri.parse("package:${context.packageName}") 106 | // context.startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) 107 | } catch(t: Throwable) { 108 | Log.e(TAG, "Error opening MANAGE_ALL_FILES settings", t) 109 | } 110 | } else { 111 | Log.d(TAG, "MANAGE_EXTERNAL_STORAGE permission already granted.") 112 | // اگر مجوز از قبل داده شده، کاری انجام نمی‌دهیم 113 | } 114 | } else { 115 | // برای نسخه‌های پایین‌تر از اندروید 11، این مجوز وجود ندارد 116 | Log.w(TAG, "MANAGE_EXTERNAL_STORAGE permission is not applicable below Android 11 (API 30).") 117 | } 118 | } 119 | 120 | // --- متدهای کمکی دیگر (می‌توانید اضافه کنید) --- 121 | 122 | /** 123 | * بررسی می‌کند آیا مجوزهای استاندارد ذخیره‌سازی داده شده‌اند یا نه. 124 | */ 125 | @JvmStatic 126 | fun hasStoragePermissions(context: Context): Boolean { 127 | val readPermission = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) 128 | val writePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) 129 | return readPermission == PackageManager.PERMISSION_GRANTED && writePermission == PackageManager.PERMISSION_GRANTED 130 | } 131 | 132 | /** 133 | * بررسی می‌کند آیا مجوز مدیریت تمام فایل‌ها داده شده است (فقط اندروید ۱۱ به بالا). 134 | */ 135 | @JvmStatic 136 | @RequiresApi(Build.VERSION_CODES.R) 137 | fun hasManageAllFilesPermission(): Boolean { 138 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 139 | Environment.isExternalStorageManager() 140 | } else { 141 | // در نسخه‌های پایین‌تر، این مجوز وجود ندارد، پس true برمی‌گردانیم؟ 142 | // یا false چون مفهوم ندارد؟ بستگی به منطق برنامه دارد. 143 | // معمولا true مناسب‌تر است چون محدودیت وجود ندارد. 144 | true // یا false بر اساس نیاز 145 | } 146 | } 147 | 148 | } -------------------------------------------------------------------------------- /android-toga/v0.1.0/n: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android-toga/version.txt: -------------------------------------------------------------------------------- 1 | v0.1.0 2 | --------------------------------------------------------------------------------