我的 Query_all_packages 权限请求在 Play 管理中心被拒绝。还有其他解决方案吗?我希望在按下“打开应用程序”按钮后打开应用程序进行确认,但该代码适用于较旧的手机,但不适用于运行 Android 11 及更高版本的设备。
我已经尝试了大部分代码,但无法解决与权限相关的问题。我已经为此苦苦挣扎了3天了
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.testing.brctestingcommunity">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.GET_TASKS" />
<application
android:allowBackup="false"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@drawable/brctesting"
android:label="@string/app_name"
android:roundIcon="@drawable/brctesting"
android:supportsRtl="true"
android:theme="@style/Theme.BrcTestingCommunity"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.BrcTestingCommunity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".LoginRegisterActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.BrcTestingCommunity" />
<activity
android:name=".AddAppActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.BrcTestingCommunity" />
<activity
android:name=".AppDetailActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.BrcTestingCommunity" />
<activity
android:name=".WebLinkActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.BrcTestingCommunity" />
<!-- UserAppsActivity Eklendi -->
<activity
android:name=".UserAppsActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.BrcTestingCommunity" />
<activity android:name=".PoliciesActivity" />
<activity android:name=".RefundPolicyActivity" />
<activity android:name=".ShippingPolicyActivity" />
<activity android:name=".TermsConditionsActivity" />
<activity android:name=".PrivacyPolicyActivity" />
<activity android:name=".StepsActivity" />
<activity android:name=".VideoActivity" />
<activity android:name=".GuideActivity" />
</application>
</manifest>
package com.testing.brctestingcommunity
import android.app.Dialog
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.os.CountDownTimer
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import com.google.android.material.imageview.ShapeableImageView
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.FieldValue
import com.google.firebase.firestore.FirebaseFirestore
import java.util.concurrent.TimeUnit
class AppDetailActivity : AppCompatActivity() {
private lateinit var webLinkLauncher: ActivityResultLauncher<Intent>
private lateinit var feedbackLauncher: ActivityResultLauncher<Intent>
private lateinit var webLinkButton: Button
private lateinit var androidLinkButton: Button
private lateinit var countdownTextView: TextView
private val userId = FirebaseAuth.getInstance().currentUser?.uid
private val db = FirebaseFirestore.getInstance()
private var shouldShowFeedbackPopup: Boolean = false
private var shouldShowOpenAppPopup: Boolean = false
private var feedbackGiven: Boolean = false
private var pointsAwardedForFeedback: Boolean = false // Kullanıcının geri bildirim puanı alıp almadığını kontrol eder
private val feedbackPoints = 5
private val webLinkPoints = 5 // WebLink için verilecek puan
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_app_detail)
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.title = getString(R.string.app_details)
toolbar.setNavigationOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
loadDataFromIntent(intent)
webLinkButton = findViewById(R.id.web_link_button)
androidLinkButton = findViewById(R.id.android_link_button)
countdownTextView = findViewById(R.id.countdown_text_view) // Geri sayım için TextView
webLinkLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
addPointsToUser(webLinkPoints)
Toast.makeText(this, "You earned $webLinkPoints points!", Toast.LENGTH_LONG).show()
webLinkButton.visibility = View.GONE // WebLink butonunu gizle
saveWebLinkStatusToFirestore()
}
}
feedbackLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { _ ->
if (feedbackGiven && !pointsAwardedForFeedback) {
addPointsToUser(feedbackPoints)
Toast.makeText(this, "Thank you for your feedback! You earned $feedbackPoints points.", Toast.LENGTH_LONG).show()
pointsAwardedForFeedback = true
saveFeedbackGivenStatusToFirestore()
saveLastInteractionTime() // Zaman damgasını kaydet
startCountdown(24 * 60 * 60 * 1000) // 24 saatlik geri sayım başlat
androidLinkButton.visibility = View.GONE // Butonu gizle
countdownTextView.visibility = View.VISIBLE // Geri sayım göster
}
}
webLinkButton.setOnClickListener {
val webLink = intent.getStringExtra("webLink")
if (!webLink.isNullOrEmpty()) {
openLinkInWebView(webLink)
} else {
Toast.makeText(this, getString(R.string.no_web_link_provided), Toast.LENGTH_SHORT).show()
}
}
androidLinkButton.setOnClickListener {
val androidLink = intent.getStringExtra("androidLink")
if (!androidLink.isNullOrEmpty()) {
shouldShowOpenAppPopup = true
shouldShowFeedbackPopup = true
openPlayStoreLink(androidLink)
} else {
Toast.makeText(this, getString(R.string.no_android_link_provided), Toast.LENGTH_SHORT).show()
}
}
// Buton durumlarını kontrol et
checkButtonVisibility()
checkAndInitializeUser()
}
override fun onResume() {
super.onResume()
if (shouldShowOpenAppPopup) {
showOpenAppPopup()
shouldShowOpenAppPopup = false
} else if (shouldShowFeedbackPopup) {
showFeedbackPopup()
shouldShowFeedbackPopup = false
}
// Butonların görünürlüğünü her geri dönüşte kontrol edin
checkButtonVisibility()
}
private fun loadDataFromIntent(intent: Intent) {
val appLogo: ShapeableImageView = findViewById(R.id.app_logo)
val appName: TextView = findViewById(R.id.app_name)
val developerName: TextView = findViewById(R.id.developer_name)
val postDate: TextView = findViewById(R.id.post_date)
val credits: TextView = findViewById(R.id.credits)
val appDescription: TextView = findViewById(R.id.app_description)
val logoUrl = intent.getStringExtra("logoUrl")
val name = intent.getStringExtra("name")
val developer = intent.getStringExtra("developer")
val date = intent.getStringExtra("date")
val creditsValue = intent.getIntExtra("credits", 0)
val description = intent.getStringExtra("description")
Glide.with(this)
.load(logoUrl)
.apply(RequestOptions().transform(CenterCrop(), RoundedCorners(16)))
.into(appLogo)
appName.text = name
developerName.text = developer
postDate.text = getString(R.string.posted_on, date)
credits.text = getString(R.string.credits, creditsValue)
appDescription.text = description
}
private fun openLinkInWebView(url: String) {
val intent = Intent(this, WebLinkActivity::class.java).apply {
putExtra("url", url)
putExtra("logoUrl", [email protected]("logoUrl"))
putExtra("name", [email protected]("name"))
putExtra("developer", [email protected]("developer"))
putExtra("date", [email protected]("date"))
putExtra("credits", [email protected]("credits", 0))
putExtra("description", [email protected]("description"))
putExtra("webLink", [email protected]("webLink"))
putExtra("androidLink", [email protected]("androidLink"))
}
webLinkLauncher.launch(intent)
}
private fun openPlayStoreLink(url: String) {
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
startActivity(intent)
Log.d("AppDetailActivity", "Play Store link opened: $url")
} catch (e: Exception) {
Log.e("AppDetailActivity", "Play Store link could not be opened: $url", e)
Toast.makeText(this, "Failed to open the Play Store link.", Toast.LENGTH_LONG).show()
}
}
private fun checkButtonVisibility() {
val webLink = intent.getStringExtra("webLink")
val androidLink = intent.getStringExtra("androidLink")
if (userId != null) {
val userRef = db.collection("users").document(userId)
userRef.get().addOnSuccessListener { document ->
if (document.exists()) {
val earnedUrls = document.get("earnedUrls") as? List<*>
if (earnedUrls != null && earnedUrls.contains(webLink)) {
webLinkButton.visibility = View.GONE
} else {
webLinkButton.visibility = View.VISIBLE
}
val feedbackGivenForUrls = document.get("feedbackGivenForUrls") as? List<*>
val lastInteractionTime = document.getLong("lastInteractionTime") ?: 0L
val currentTime = System.currentTimeMillis()
val elapsedTime = currentTime - lastInteractionTime
if (feedbackGivenForUrls != null && feedbackGivenForUrls.contains(androidLink)) {
if (elapsedTime < 24 * 60 * 60 * 1000) {
androidLinkButton.visibility = View.GONE
countdownTextView.visibility = View.VISIBLE
startCountdown(24 * 60 * 60 * 1000 - elapsedTime)
} else {
androidLinkButton.visibility = View.VISIBLE
countdownTextView.visibility = View.GONE
}
} else {
androidLinkButton.visibility = View.VISIBLE
countdownTextView.visibility = View.GONE
}
}
}.addOnFailureListener { e ->
Log.e("AppDetailActivity", "Failed to check button visibility: $e")
}
}
}
private fun extractPackageNameFromUrl(url: String): String? {
return try {
val uri = Uri.parse(url)
uri.getQueryParameter("id") ?: uri.lastPathSegment.also {
Log.d("AppDetailActivity", "Extracted package name: $it")
}
} catch (e: Exception) {
Log.e("AppDetailActivity", "Failed to extract package name from URL: $url", e)
null
}
}
private fun openInstalledApp(packageName: String) {
Log.d("AppDetailActivity", "Trying to open the app: $packageName")
try {
val intent = packageManager.getLaunchIntentForPackage(packageName)?.apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
if (intent != null) {
startActivity(intent)
Log.d("AppDetailActivity", "App opened: $packageName")
} else {
Log.d("AppDetailActivity", "App not found: $packageName")
Toast.makeText(
this,
"App is not installed or you are not part of the test group. Make sure you are signed in with the correct Google account.",
Toast.LENGTH_LONG
).show()
}
} catch (e: Exception) {
Log.e("AppDetailActivity", "Error while trying to open the app: $packageName", e)
Toast.makeText(this, "Failed to open the app due to an error.", Toast.LENGTH_LONG).show()
}
}
private fun showOpenAppPopup() {
val dialog = Dialog(this)
dialog.setContentView(R.layout.popup_layout) // Burada doğru layout dosyasını kullanın
val openAppButton = dialog.findViewById<Button>(R.id.openAppButton)
openAppButton.setOnClickListener {
val androidLink = intent.getStringExtra("androidLink")
if (!androidLink.isNullOrEmpty()) {
val appPackageName = extractPackageNameFromUrl(androidLink)
if (!appPackageName.isNullOrEmpty() && isAppInstalled(appPackageName)) {
openInstalledApp(appPackageName)
shouldShowFeedbackPopup = true
} else {
Toast.makeText(this, getString(R.string.app_not_installed), Toast.LENGTH_SHORT).show()
}
}
dialog.dismiss()
}
dialog.show()
}
private fun showFeedbackPopup() {
val dialog = Dialog(this)
dialog.setContentView(R.layout.popup_feedback_layout)
val feedbackButton = dialog.findViewById<Button>(R.id.feedbackButton)
feedbackButton.setOnClickListener {
val androidLink = intent.getStringExtra("androidLink")
if (!androidLink.isNullOrEmpty()) {
val appPackageName = extractPackageNameFromUrl(androidLink)
if (!appPackageName.isNullOrEmpty()) {
feedbackGiven = true // Mark as feedback given before redirecting
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$appPackageName"))
feedbackLauncher.launch(intent)
} else {
Toast.makeText(this, "Invalid app link", Toast.LENGTH_SHORT).show()
}
} else {
Toast.makeText(this, "No app link provided", Toast.LENGTH_SHORT).show()
}
dialog.dismiss()
}
dialog.show()
}
private fun isAppInstalled(packageName: String): Boolean {
return try {
packageManager.getPackageInfo(packageName, 0)
Log.d("AppDetailActivity", "App is installed: $packageName")
true
} catch (e: PackageManager.NameNotFoundException) {
Log.d("AppDetailActivity", "App is not installed: $packageName")
false
}
}
private fun addPointsToUser(points: Int) {
userId?.let { uid ->
val userRef = db.collection("users").document(uid)
userRef.update("points", FieldValue.increment(points.toLong()))
.addOnSuccessListener {
Log.d("AppDetailActivity", "$points points added to user.")
}
.addOnFailureListener { e ->
Log.e("AppDetailActivity", "Error adding points: ", e)
Toast.makeText(this, "Failed to add points", Toast.LENGTH_LONG).show()
}
}
}
private fun saveFeedbackGivenStatusToFirestore() {
userId?.let { uid ->
val androidLink = intent.getStringExtra("androidLink")
if (androidLink != null) {
val userRef = db.collection("users").document(uid)
userRef.update("feedbackGivenForUrls", FieldValue.arrayUnion(androidLink))
.addOnSuccessListener {
Log.d("AppDetailActivity", "Feedback given status saved for $androidLink.")
}
.addOnFailureListener { e ->
Log.e("AppDetailActivity", "Error saving feedback given status: $androidLink", e)
}
}
}
}
private fun saveWebLinkStatusToFirestore() {
userId?.let { uid ->
val webLink = intent.getStringExtra("webLink")
if (webLink != null) {
val userRef = db.collection("users").document(uid)
userRef.update("earnedUrls", FieldValue.arrayUnion(webLink))
.addOnSuccessListener {
Log.d("AppDetailActivity", "WebLink status saved for $webLink.")
}
.addOnFailureListener { e ->
Log.e("AppDetailActivity", "Error saving WebLink status: $webLink", e)
}
}
}
}
private fun saveLastInteractionTime() {
userId?.let { uid ->
val userRef = db.collection("users").document(uid)
userRef.update("lastInteractionTime", System.currentTimeMillis())
.addOnSuccessListener {
Log.d("AppDetailActivity", "Last interaction time saved.")
}
.addOnFailureListener { e ->
Log.e("AppDetailActivity", "Error saving last interaction time: ", e)
}
}
}
private fun startCountdown(millisUntilFinished: Long) {
val countdownTimer = object : CountDownTimer(millisUntilFinished, 1000) {
override fun onTick(millisUntilFinished: Long) {
val hours = TimeUnit.MILLISECONDS.toHours(millisUntilFinished)
val minutes = TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished) % 60
val seconds = TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) % 60
val timeFormatted = String.format("%02d:%02d:%02d", hours, minutes, seconds)
countdownTextView.text = "Button will be active in: $timeFormatted"
}
override fun onFinish() {
countdownTextView.visibility = View.GONE
androidLinkButton.visibility = View.VISIBLE
}
}
countdownTimer.start()
}
private fun checkAndInitializeUser() {
val userId = FirebaseAuth.getInstance().currentUser?.uid
if (userId != null) {
val userRef = FirebaseFirestore.getInstance().collection("users").document(userId)
userRef.get().addOnSuccessListener { document ->
if (!document.exists()) {
initializeUserInFirestore(userId) // Eğer kullanıcı Firestore'da yoksa başlat
}
}.addOnFailureListener { e ->
Log.e("AppDetailActivity", "Error checking user in Firestore: $userId", e)
}
}
}
private fun initializeUserInFirestore(userId: String) {
val userRef = FirebaseFirestore.getInstance().collection("users").document(userId)
val userData = hashMapOf(
"points" to 0,
"earnedUrls" to emptyList<String>(),
"feedbackGivenForUrls" to emptyList<String>(),
"lastInteractionTime" to 0L
)
userRef.set(userData)
.addOnSuccessListener {
Log.d("AppDetailActivity", "User initialized in Firestore: $userId")
}
.addOnFailureListener { e ->
Log.e("AppDetailActivity", "Failed to initialize user in Firestore: $userId", e)
}
}
}
Google 已经明确表示,
QUERY_ALL_PACKAGES
是一种特殊情况,只有当您的应用程序属于某种类型(例如应用程序启动器)时才会被授予。
我在一家大量使用包管理的公司工作,为了支持 Android 11,我们必须接受一些更改并改变我们的工作方式,例如:
<queries>
部分),然后您可以像在早期版本的 Android 上一样查询它们intent.setClassName(...)
执行此操作,我们仅对在构建时不知道包名称的包使用此解决方案(否则我们可以将它们放入清单中并避免这种复杂性)