How do I navigate back to my app after user grants

2019-07-21 06:52发布

问题:

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
    }
}