问题
I need my app to return automatically to my application once the user grants permission of usage stats access through toggle/switch button in Settings Screen. This is possible in Google FILES app. PSB FLOW from Google File App: Google Files APP Flow
How do I achieve this in my Kotlin app. I have tried multiple ways, but could not achieve it. Please advise. Thank You.
MAIN ACTIVITY
class MainActivity : AppCompatActivity() {
private lateinit var usage: UsageStatsManager
private lateinit var impStats: MutableList<UsageStats>
private lateinit var ops: AppOpsManager
private var opsChanged = false
private val opsListener = object : AppOpsManager.OnOpChangedListener {
override fun onOpChanged(op: String?, packageName: String?) {
//Stop Watching
ops.stopWatchingMode(this)
if (!opsChanged) {
opsChanged = true
if (usagePermission()) {
val intent = Intent(this@MainActivity, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
}
startActivity(intent)
}
}
}
}
private fun usagePermission(): Boolean {
val mode = ops.checkOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
packageName
) ?: AppOpsManager.MODE_ALLOWED
return mode == AppOpsManager.MODE_ALLOWED
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
ops = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
ops.startWatchingMode(AppOpsManager.OPSTR_GET_USAGE_STATS, packageName, opsListener)
binding.settings.setOnClickListener {
openSettings()
}
}
override fun onDestroy() {
ops.stopWatchingMode(opsListener)
super.onDestroy()
}
fun openSettings() {
val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
if (intent.resolveActivity(packageManager) != null)
startActivity(intent)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
val fragmentTransaction = fragmentManager.beginTransaction()
val fragment = FragmentA()
fragmentTransaction.add(R.id.main_activity, fragment)
fragmentTransaction.commit()
}
} }
FRAGMENT A
class TitleFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding = DataBindingUtil.inflate<FragmentTitleBinding>(inflater, R.layout.fragmentA, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
}
****MANIFEST FILE***
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sample">
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
FRAGMENT A XML
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navGraph="@navigation/navigation">
<TextView
android:id="@+id/View"
android:layout_width="120dp"
android:layout_height="43dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="@string/welcome"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
NAVIGATION XML
SINCE I AM USING NAVIGATION GRAPH
<?xml version="1.0" encoding="utf-8"?>
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/navigation"
app:startDestination="@+id/fragmentA">
<fragment
android:id="@+id/fragmentA"
android:name="com.sample.FragmentA"
android:label="FragmentA"
tools:layout="@layout/fragmentA"/>
</navigation>
**ACTIVITY MAN XML**
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:id="@+id/main_activity"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment
android:id="@+id/myNavHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost ="true"
app:navGraph="@navigation/navigation"/>
</LinearLayout>
</layout>
回答1:
From AppOpsManager android documentation.
startWatchingMode
public void startWatchingMode (String op, String packageName, AppOpsManager.OnOpChangedListener callback)Monitor for changes to the operating mode for the given op in the given app package. You can watch op changes only for your UID.
Solution: You can follow the following example to navigate back to your app after users grant usage access data permission.
class MainActivity : AppCompatActivity() {
private var mAppOpsManager: AppOpsManager? = null
private var mOpChanged = false
private val mOnOpChangedListener = object : AppOpsManager.OnOpChangedListener {
override fun onOpChanged(op: String?, packageName: String?) {
// Stop watch op changed
mAppOpsManager!!.stopWatchingMode(this)
if (!mOpChanged) {
mOpChanged = true
if (hasUsageDataAccessPermission()) {
// Start your designed activity here
val intent = Intent(this@MainActivity, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
// [NOTE] You can put some data into this intent.
// This intent will be received in onNewIntent method.
}
startActivity(intent)
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mAppOpsManager = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager?
mAppOpsManager?.startWatchingMode(AppOpsManager.OPSTR_GET_USAGE_STATS,
packageName,
mOnOpChangedListener)
if (!hasUsageDataAccessPermission()) {
val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}
}
}
override fun onDestroy() {
mAppOpsManager?.stopWatchingMode(mOnOpChangedListener)
super.onDestroy()
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
// [IMPORTANT] After users grant usage data access permission, this activity will display
// and this method will be called instead of onCreate.
// You should handle your logic here.
}
private fun hasUsageDataAccessPermission(): Boolean {
val mode = mAppOpsManager?.checkOpNoThrow(
AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(),
packageName
) ?: AppOpsManager.MODE_ALLOWED
return mode == AppOpsManager.MODE_ALLOWED
}
}
来源:https://stackoverflow.com/questions/54987476/how-do-i-navigate-back-to-my-app-after-user-grants-usage-access-permission-in-ko