├── 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 |
--------------------------------------------------------------------------------