Update: 2026-07-05 00:21:29
This commit is contained in:
@@ -266,8 +266,12 @@ class SiroGeminiService {
|
||||
2. تلخيص أهم هذه النقاشات في تقرير قصير ومفيد (News Report).
|
||||
3. تجاهل المنشورات العشوائية أو الشخصية التي لا تفيد السوق.
|
||||
|
||||
قم بصياغة تقريرك بتنسيق HTML مرتب وجاهز للعرض (بدون علامات ```html)، واستخدم <h3> للعناوين و <ul> للقوائم.
|
||||
إذا لم يكن هناك أي شيء مفيد، اكتب فقط: <p>لا توجد بيانات مفيدة في هذه الدفعة.</p>
|
||||
هام جداً:
|
||||
- يجب أن يكون التقرير باللغة العربية بالكامل.
|
||||
- يجب أن تغلف التقرير بالكامل بوسم <div dir=\"rtl\" align=\"right\"> في البداية و </div> في النهاية لضمان القراءة من اليمين لليسار.
|
||||
- استخدم <h3> للعناوين و <ul> للقوائم داخل التقرير.
|
||||
- لا تقم بتضمين علامات ```html في المخرجات.
|
||||
إذا لم يكن هناك أي شيء مفيد، اكتب فقط: <div dir=\"rtl\" align=\"right\"><p>لا توجد بيانات مفيدة في هذه الدفعة.</p></div>
|
||||
";
|
||||
|
||||
$url = $this->baseUrl . "{$model}:generateContent?key={$this->apiKey}";
|
||||
|
||||
@@ -41,7 +41,17 @@ try {
|
||||
$gemini = new SiroGeminiService();
|
||||
|
||||
// Create a new method in GeminiService or use evaluatePostsForReporting with a specific prompt wrapper
|
||||
$prompt = "You are a Chief Marketing Officer AI. Below are all the daily/individual social media intelligence reports collected over the last 7 days from our scraper bots. Please read through all of them and generate one comprehensive, executive 'Weekly Market Intelligence Summary'. Format it beautifully in HTML. Highlight key market trends, recurrent complaints, competitor activities, and actionable advice for the week.\n\nData:\n" . json_encode($combinedData);
|
||||
$prompt = "أنت خبير واستشاري تسويق الذكاء الاصطناعي (Chief Marketing Officer AI).
|
||||
فيما يلي جميع التقارير اليومية والفردية لاستخبارات وسائل التواصل الاجتماعي التي جمعناها خلال الـ 7 أيام الماضية.
|
||||
يرجى قراءتها بالكامل وتوليد 'ملخص استخبارات السوق الأسبوعي الشامل'.
|
||||
قم بتنسيق التقرير بشكل جميل باستخدام HTML. يجب أن تبرز اتجاهات السوق الرئيسية، الشكاوى المتكررة، نشاطات المنافسين، ونصائح قابلة للتنفيذ لهذا الأسبوع.
|
||||
|
||||
هام جداً:
|
||||
- يجب أن يكون التقرير باللغة العربية بالكامل.
|
||||
- يجب أن تغلف كامل التقرير بـ <div dir=\"rtl\" align=\"right\"> في البداية و </div> في النهاية لضمان المحاذاة.
|
||||
|
||||
البيانات:
|
||||
" . json_encode($combinedData, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
$response = $gemini->callGemini($prompt, 'gemini-1.5-flash');
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ try {
|
||||
switch ($action) {
|
||||
case 'get_task':
|
||||
$stmt = $con->prepare("
|
||||
SELECT id, type
|
||||
SELECT id, type, target_url, prompt_context
|
||||
FROM marketing_tasks
|
||||
WHERE status = 'pending' AND platform = ?
|
||||
ORDER BY id ASC LIMIT 1
|
||||
|
||||
@@ -2,9 +2,17 @@
|
||||
require_once __DIR__ . '/../core/bootstrap.php';
|
||||
$con = Database::get('main');
|
||||
|
||||
$stmt = $con->prepare("INSERT INTO marketing_tasks (platform, type, status) VALUES ('facebook', 'autonomous_scroll_and_reply', 'pending')");
|
||||
$stmt->execute();
|
||||
$platform = $_GET['platform'] ?? 'facebook';
|
||||
$targetUrl = $_GET['target_url'] ?? null;
|
||||
|
||||
if ($targetUrl) {
|
||||
$stmt = $con->prepare("INSERT INTO marketing_tasks (platform, type, status, target_url) VALUES (?, 'autonomous_scroll_and_reply', 'pending', ?)");
|
||||
$stmt->execute([$platform, $targetUrl]);
|
||||
} else {
|
||||
$stmt = $con->prepare("INSERT INTO marketing_tasks (platform, type, status) VALUES (?, 'autonomous_scroll_and_reply', 'pending')");
|
||||
$stmt->execute([$platform]);
|
||||
}
|
||||
|
||||
echo "<h1>Task inserted successfully!</h1>";
|
||||
echo "<p>A new 'autonomous_scroll_and_reply' task is now pending for Facebook.</p>";
|
||||
echo "<p>A new 'autonomous_scroll_and_reply' task is now pending for $platform.</p>";
|
||||
echo "<p>Please restart the Android app now. It will immediately pick up this task and start scrolling!</p>";
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<package android:name="com.instagram.android" />
|
||||
<package android:name="com.twitter.android" />
|
||||
<package android:name="com.zhiliaoapp.musically" />
|
||||
<package android:name="org.telegram.messenger" />
|
||||
</queries>
|
||||
|
||||
<application
|
||||
@@ -63,6 +64,20 @@
|
||||
android:resource="@xml/instagram_accessibility_service_config" />
|
||||
</service>
|
||||
|
||||
<!-- Telegram Accessibility Service -->
|
||||
<service
|
||||
android:name=".social.telegram.TelegramBotService"
|
||||
android:label="Siro Telegram Bot"
|
||||
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.accessibilityservice.AccessibilityService" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.accessibilityservice"
|
||||
android:resource="@xml/telegram_accessibility_service_config" />
|
||||
</service>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -54,9 +54,9 @@ object SocialBotClient {
|
||||
postData("action=complete_task", "task_id=$taskId&result=$result")
|
||||
}
|
||||
|
||||
fun evaluatePosts(posts: List<String>): JSONObject? {
|
||||
fun evaluatePosts(posts: List<String>, platform: String = "facebook"): JSONObject? {
|
||||
try {
|
||||
val urlString = "$BASE_URL?action=evaluate_posts"
|
||||
val urlString = "$BASE_URL?action=evaluate_posts&platform=$platform"
|
||||
val url = URL(urlString)
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "POST"
|
||||
|
||||
+53
@@ -87,6 +87,59 @@ class InstagramBotService : AccessibilityService() {
|
||||
}
|
||||
}
|
||||
}
|
||||
"autonomous_scroll_and_reply" -> {
|
||||
Log.d(TAG, "Starting autonomous mode for Instagram...")
|
||||
if (task.targetUrl != null) {
|
||||
navigator.openUrl(task.targetUrl)
|
||||
} else {
|
||||
navigator.openApp()
|
||||
}
|
||||
delay(3000)
|
||||
|
||||
val allCollectedTexts = mutableSetOf<String>()
|
||||
val totalIterations = 15 // Roughly 3-4 minutes of scrolling
|
||||
var iterations = 0
|
||||
var consecutiveNoNewPosts = 0
|
||||
|
||||
while (iterations < totalIterations) {
|
||||
Log.d(TAG, "Autonomous Iteration: ${iterations + 1} of $totalIterations")
|
||||
val initialSize = allCollectedTexts.size
|
||||
|
||||
// 1. Read feed posts and comments on screen
|
||||
val posts = navigator.extractPostsAndComments()
|
||||
|
||||
// Add to our global collection (Set avoids duplicates)
|
||||
allCollectedTexts.addAll(posts)
|
||||
Log.d(TAG, "Collected ${posts.size} texts in this iteration. Total unique so far: ${allCollectedTexts.size}")
|
||||
|
||||
if (allCollectedTexts.size == initialSize) {
|
||||
consecutiveNoNewPosts++
|
||||
Log.d(TAG, "No new posts found in this iteration. (Streak: $consecutiveNoNewPosts)")
|
||||
if (consecutiveNoNewPosts >= 3) {
|
||||
Log.d(TAG, "No new posts for 3 consecutive iterations. Reached bottom or stuck. Breaking early.")
|
||||
break
|
||||
}
|
||||
} else {
|
||||
consecutiveNoNewPosts = 0
|
||||
}
|
||||
|
||||
Log.d(TAG, "Scrolling down for more posts...")
|
||||
navigator.scrollForward()
|
||||
delay(3000) // wait for new posts to load
|
||||
iterations++
|
||||
}
|
||||
|
||||
// Send everything to Gemini AT ONCE to save API costs
|
||||
if (allCollectedTexts.isNotEmpty()) {
|
||||
Log.d(TAG, "Finished scrolling. Sending ${allCollectedTexts.size} texts to backend (platform=instagram)...")
|
||||
// The endpoint should handle this dynamically if we pass platform to SocialBotClient
|
||||
// Wait, SocialBotClient currently uses a hardcoded url or passes platform?
|
||||
// Let's assume evaluatePosts can take platform or defaults to facebook. We will need to update evaluatePosts in SocialBotClient.
|
||||
SocialBotClient.evaluatePosts(allCollectedTexts.toList(), "instagram")
|
||||
}
|
||||
|
||||
SocialBotClient.completeTask(task.id, "Autonomous session completed. Scraped ${allCollectedTexts.size} items.")
|
||||
}
|
||||
else -> {
|
||||
Log.w(TAG, "Unknown task type: ${task.type}")
|
||||
SocialBotClient.failTask(task.id, "Unknown task type")
|
||||
|
||||
+76
@@ -27,6 +27,17 @@ class InstagramNavigator(private val service: AccessibilityService) {
|
||||
delay(4_000) // Wait for Instagram to load
|
||||
}
|
||||
|
||||
suspend fun openApp() {
|
||||
val intent = service.packageManager.getLaunchIntentForPackage("com.instagram.android")
|
||||
if (intent != null) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
service.startActivity(intent)
|
||||
delay(5_000)
|
||||
} else {
|
||||
// Instagram not installed
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun postComment(commentText: String): Boolean {
|
||||
val root = service.rootInActiveWindow ?: return false
|
||||
|
||||
@@ -56,6 +67,71 @@ class InstagramNavigator(private val service: AccessibilityService) {
|
||||
|
||||
// ─── Utility helpers ───────────────────────────────────────────────────────
|
||||
|
||||
fun scrollForward(): Boolean {
|
||||
val root = service.rootInActiveWindow ?: return false
|
||||
val listNode = findScrollableNode(root)
|
||||
|
||||
if (listNode != null) {
|
||||
val scrolled = listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)
|
||||
if (scrolled) return true
|
||||
}
|
||||
|
||||
// Fallback to gesture swipe
|
||||
val displayMetrics = service.resources.displayMetrics
|
||||
val width = displayMetrics.widthPixels
|
||||
val height = displayMetrics.heightPixels
|
||||
|
||||
val path = android.graphics.Path()
|
||||
path.moveTo(width / 2f, height * 0.8f)
|
||||
path.lineTo(width / 2f, height * 0.2f)
|
||||
|
||||
val gesture = android.accessibilityservice.GestureDescription.Builder()
|
||||
.addStroke(android.accessibilityservice.GestureDescription.StrokeDescription(path, 0, 500))
|
||||
.build()
|
||||
|
||||
return service.dispatchGesture(gesture, null, null)
|
||||
}
|
||||
|
||||
fun extractPostsAndComments(): List<String> {
|
||||
val root = service.rootInActiveWindow ?: return emptyList()
|
||||
val extractedTexts = mutableSetOf<String>()
|
||||
extractTextRecursively(root, extractedTexts)
|
||||
return extractedTexts.toList()
|
||||
}
|
||||
|
||||
private fun extractTextRecursively(node: AccessibilityNodeInfo?, results: MutableSet<String>) {
|
||||
if (node == null) return
|
||||
|
||||
// Only target TextViews
|
||||
if (node.className?.toString() == "android.widget.TextView") {
|
||||
val text = node.text?.toString()?.trim()
|
||||
val contentDesc = node.contentDescription?.toString()?.trim()
|
||||
|
||||
val validText = text ?: contentDesc
|
||||
|
||||
// Filter out short UI elements, timestamps, likes, etc.
|
||||
if (!validText.isNullOrEmpty() && validText.length > 20 && !validText.contains(Regex("^[0-9]+ likes$"))) {
|
||||
results.add(validText)
|
||||
}
|
||||
}
|
||||
|
||||
for (i in 0 until node.childCount) {
|
||||
extractTextRecursively(node.getChild(i), results)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findScrollableNode(node: AccessibilityNodeInfo?): AccessibilityNodeInfo? {
|
||||
if (node == null) return null
|
||||
if (node.isScrollable && (node.className?.toString()?.contains("RecyclerView") == true || node.className?.toString()?.contains("ListView") == true)) {
|
||||
return node
|
||||
}
|
||||
for (i in 0 until node.childCount) {
|
||||
val found = findScrollableNode(node.getChild(i))
|
||||
if (found != null) return found
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun findNodeByHint(node: AccessibilityNodeInfo?, hint: String): AccessibilityNodeInfo? {
|
||||
if (node == null) return null
|
||||
if (node.hintText?.toString()?.contains(hint, ignoreCase = true) == true) return node
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package com.siro.socialmedia_bot.social.telegram
|
||||
|
||||
import android.accessibilityservice.AccessibilityService
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import android.util.Log
|
||||
import com.siro.socialmedia_bot.network.SocialBotClient
|
||||
import com.siro.socialmedia_bot.social.model.SocialTask
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
class TelegramBotService : AccessibilityService() {
|
||||
|
||||
private val TAG = "TelegramBotService"
|
||||
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
private var isBotRunning = false
|
||||
|
||||
private lateinit var navigator: TelegramNavigator
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
Log.d(TAG, "Telegram Accessibility Service Connected")
|
||||
navigator = TelegramNavigator(this)
|
||||
startTaskLoop()
|
||||
}
|
||||
|
||||
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
|
||||
// Observe events when needed for synchronization
|
||||
}
|
||||
|
||||
override fun onInterrupt() {
|
||||
Log.d(TAG, "Service Interrupted")
|
||||
isBotRunning = false
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
private fun startTaskLoop() {
|
||||
if (isBotRunning) return
|
||||
isBotRunning = true
|
||||
|
||||
scope.launch {
|
||||
while (isBotRunning) {
|
||||
try {
|
||||
val taskData = SocialBotClient.getTask("telegram")
|
||||
if (taskData != null) {
|
||||
val task = SocialTask(
|
||||
id = taskData.getInt("id"),
|
||||
type = taskData.getString("type"),
|
||||
targetUrl = if (taskData.isNull("target_url")) null else taskData.getString("target_url"),
|
||||
promptContext = if (taskData.isNull("prompt_context")) null else taskData.getString("prompt_context"),
|
||||
generatedComment = if (taskData.isNull("generated_comment")) null else taskData.getString("generated_comment")
|
||||
)
|
||||
Log.d(TAG, "Received Task: ${task.type}")
|
||||
executeTask(task)
|
||||
} else {
|
||||
Log.d(TAG, "No Telegram tasks. Sleeping...")
|
||||
delay(60_000)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error in task loop", e)
|
||||
delay(30_000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun executeTask(task: SocialTask) {
|
||||
try {
|
||||
when (task.type) {
|
||||
"autonomous_scroll_and_reply" -> {
|
||||
Log.d(TAG, "Starting autonomous mode for Telegram...")
|
||||
if (task.targetUrl != null) {
|
||||
navigator.openChannel(task.targetUrl)
|
||||
} else {
|
||||
navigator.openApp()
|
||||
}
|
||||
delay(3000)
|
||||
|
||||
val allCollectedTexts = mutableSetOf<String>()
|
||||
val totalIterations = 15
|
||||
var iterations = 0
|
||||
var consecutiveNoNewPosts = 0
|
||||
|
||||
while (iterations < totalIterations) {
|
||||
Log.d(TAG, "Autonomous Iteration: ${iterations + 1} of $totalIterations")
|
||||
val initialSize = allCollectedTexts.size
|
||||
|
||||
val posts = navigator.extractPostsAndComments()
|
||||
|
||||
allCollectedTexts.addAll(posts)
|
||||
Log.d(TAG, "Collected ${posts.size} texts in this iteration. Total unique so far: ${allCollectedTexts.size}")
|
||||
|
||||
if (allCollectedTexts.size == initialSize) {
|
||||
consecutiveNoNewPosts++
|
||||
Log.d(TAG, "No new posts found in this iteration. (Streak: $consecutiveNoNewPosts)")
|
||||
if (consecutiveNoNewPosts >= 3) {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
consecutiveNoNewPosts = 0
|
||||
}
|
||||
|
||||
Log.d(TAG, "Scrolling up for more posts...")
|
||||
navigator.scrollForward()
|
||||
delay(2500)
|
||||
iterations++
|
||||
}
|
||||
|
||||
if (allCollectedTexts.isNotEmpty()) {
|
||||
Log.d(TAG, "Finished scrolling. Sending ${allCollectedTexts.size} texts to backend (platform=telegram)...")
|
||||
SocialBotClient.evaluatePosts(allCollectedTexts.toList(), "telegram")
|
||||
}
|
||||
|
||||
SocialBotClient.completeTask(task.id, "Autonomous session completed. Scraped ${allCollectedTexts.size} items.")
|
||||
}
|
||||
else -> {
|
||||
Log.w(TAG, "Unknown task type: ${task.type}")
|
||||
SocialBotClient.failTask(task.id, "Unknown task type")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
SocialBotClient.failTask(task.id, "Exception: ${e.message}")
|
||||
}
|
||||
delay((15_000..30_000).random().toLong())
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package com.siro.socialmedia_bot.social.telegram
|
||||
|
||||
import android.accessibilityservice.AccessibilityService
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.accessibility.AccessibilityNodeInfo
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
/**
|
||||
* Handles navigation and interaction within the Telegram app.
|
||||
* Telegram's package name: org.telegram.messenger
|
||||
*/
|
||||
class TelegramNavigator(private val service: AccessibilityService) {
|
||||
|
||||
suspend fun openApp() {
|
||||
val intent = service.packageManager.getLaunchIntentForPackage("org.telegram.messenger")
|
||||
if (intent != null) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
service.startActivity(intent)
|
||||
delay(5_000)
|
||||
} else {
|
||||
// Telegram not installed
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun openChannel(channelLink: String) {
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(channelLink))
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
intent.setPackage("org.telegram.messenger")
|
||||
try {
|
||||
service.startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
intent.setPackage(null)
|
||||
service.startActivity(intent)
|
||||
}
|
||||
delay(4_000)
|
||||
}
|
||||
|
||||
fun scrollForward(): Boolean {
|
||||
val root = service.rootInActiveWindow ?: return false
|
||||
val listNode = findScrollableNode(root)
|
||||
|
||||
if (listNode != null) {
|
||||
val scrolled = listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD)
|
||||
// Telegram might scroll backward if it's reading top to bottom in chat, but we usually want older posts
|
||||
// so scroll backward is actually ACTION_SCROLL_BACKWARD for loading old messages.
|
||||
if (scrolled) return true
|
||||
}
|
||||
|
||||
val displayMetrics = service.resources.displayMetrics
|
||||
val width = displayMetrics.widthPixels
|
||||
val height = displayMetrics.heightPixels
|
||||
|
||||
val path = android.graphics.Path()
|
||||
path.moveTo(width / 2f, height * 0.2f) // swipe down to go up in chat
|
||||
path.lineTo(width / 2f, height * 0.8f)
|
||||
|
||||
val gesture = android.accessibilityservice.GestureDescription.Builder()
|
||||
.addStroke(android.accessibilityservice.GestureDescription.StrokeDescription(path, 0, 500))
|
||||
.build()
|
||||
|
||||
return service.dispatchGesture(gesture, null, null)
|
||||
}
|
||||
|
||||
fun extractPostsAndComments(): List<String> {
|
||||
val root = service.rootInActiveWindow ?: return emptyList()
|
||||
val extractedTexts = mutableSetOf<String>()
|
||||
extractTextRecursively(root, extractedTexts)
|
||||
return extractedTexts.toList()
|
||||
}
|
||||
|
||||
private fun extractTextRecursively(node: AccessibilityNodeInfo?, results: MutableSet<String>) {
|
||||
if (node == null) return
|
||||
|
||||
// Target TextViews
|
||||
if (node.className?.toString() == "android.widget.TextView") {
|
||||
val text = node.text?.toString()?.trim()
|
||||
val contentDesc = node.contentDescription?.toString()?.trim()
|
||||
|
||||
val validText = text ?: contentDesc
|
||||
|
||||
// Filter short words
|
||||
if (!validText.isNullOrEmpty() && validText.length > 20) {
|
||||
results.add(validText)
|
||||
}
|
||||
}
|
||||
|
||||
for (i in 0 until node.childCount) {
|
||||
extractTextRecursively(node.getChild(i), results)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findScrollableNode(node: AccessibilityNodeInfo?): AccessibilityNodeInfo? {
|
||||
if (node == null) return null
|
||||
if (node.isScrollable && (node.className?.toString()?.contains("RecyclerView") == true || node.className?.toString()?.contains("ListView") == true || node.className?.toString()?.contains("ScrollView") == true)) {
|
||||
return node
|
||||
}
|
||||
for (i in 0 until node.childCount) {
|
||||
val found = findScrollableNode(node.getChild(i))
|
||||
if (found != null) return found
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged"
|
||||
android:accessibilityFeedbackType="feedbackGeneric"
|
||||
android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows|flagIncludeNotImportantViews|flagReportViewIds"
|
||||
android:canRetrieveWindowContent="true"
|
||||
android:canPerformGestures="true"
|
||||
android:description="@string/accessibility_service_description"
|
||||
android:notificationTimeout="100"
|
||||
android:packageNames="org.telegram.messenger" />
|
||||
Reference in New Issue
Block a user