/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.fragment.app
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.IntentSender
import android.content.IntentSender.SendIntentException
import android.os.Bundle
import android.os.Handler
import android.view.LayoutInflater
import android.view.View
import androidx.annotation.RestrictTo
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import java.io.FileDescriptor
import java.io.PrintWriter
/**
* Integration points with the Fragment host.
*
* Fragments may be hosted by any object; such as an [Activity]. In order to
* host fragments, implement [FragmentHostCallback], overriding the methods
* applicable to the host.
*
* FragmentManager changes its behavior based on what optional interfaces your
* FragmentHostCallback implements. This includes the following:
*
* - **[androidx.activity.result.ActivityResultRegistryOwner]**: Removes the need to
* override [.onStartIntentSenderFromFragment] or
* [.onRequestPermissionsFromFragment].
* - **[FragmentOnAttachListener]**: Removes the need to
* manually call [FragmentManager.addFragmentOnAttachListener] from your
* host in order to receive [FragmentOnAttachListener.onAttachFragment] callbacks
* for the [FragmentController.getSupportFragmentManager].
* - **[androidx.activity.OnBackPressedDispatcherOwner]**: Removes
* the need to manually call
* [FragmentManager.popBackStackImmediate] when handling the system
* back button.
* - **[androidx.lifecycle.ViewModelStoreOwner]**: Removes the need
* for your [FragmentController] to call
* [FragmentController.retainNestedNonConfig] or
* [FragmentController.restoreAllState].
*
* @param H the type of object that's currently hosting the fragments. An instance of this
* class must be returned by [onGetHost].
*/
@Suppress("deprecation")
abstract class FragmentHostCallback<H> internal constructor(
@get:RestrictTo(RestrictTo.Scope.LIBRARY)
val activity: Activity?,
@get:RestrictTo(RestrictTo.Scope.LIBRARY)
val context: Context,
@get:RestrictTo(RestrictTo.Scope.LIBRARY)
val handler: Handler,
private val windowAnimations: Int
) : FragmentContainer() {
@get:RestrictTo(RestrictTo.Scope.LIBRARY)
val fragmentManager: FragmentManager = FragmentManagerImpl()
@Suppress("unused")
constructor(
context: Context,
handler: Handler,
windowAnimations: Int
) : this(
if (context is Activity) context else null,
context,
handler,
windowAnimations
)
@Suppress("deprecation")
internal constructor(activity: FragmentActivity) : this(
activity,
context = activity,
Handler(),
windowAnimations = 0
)
/**
* Print internal state into the given stream.
*
* @param prefix Desired prefix to prepend at each line of output.
* @param fd The raw file descriptor that the dump is being sent to.
* @param writer The PrintWriter to which you should dump your state. This will be closed
* for you after you return.
* @param args additional arguments to the dump request.
*/
open fun onDump(
prefix: String,
fd: FileDescriptor?,
writer: PrintWriter,
args: Array<String>?
) {
}
/**
* Return `true` if the fragment's state needs to be saved.
*/
open fun onShouldSaveFragmentState(fragment: Fragment): Boolean {
return true
}
/**
* Return a [LayoutInflater].
* See [Activity.getLayoutInflater].
*/
open fun onGetLayoutInflater(): LayoutInflater {
return LayoutInflater.from(context)
}
/**
* Return the object that's currently hosting the fragment. If a [Fragment]
* is hosted by a [FragmentActivity], the object returned here should be
* the same object returned from [Fragment.getActivity].
*/
abstract fun onGetHost(): H
/**
* Invalidates the activity's options menu.
* See [FragmentActivity.supportInvalidateOptionsMenu]
*/
open fun onSupportInvalidateOptionsMenu() {}
/**
* Starts a new [Activity] from the given fragment.
* See [FragmentActivity.startActivityForResult].
*/
open fun onStartActivityFromFragment(
fragment: Fragment,
intent: Intent,
requestCode: Int
) {
onStartActivityFromFragment(fragment, intent, requestCode, null)
}
/**
* Starts a new [Activity] from the given fragment.
* See [FragmentActivity.startActivityForResult].
*/
open fun onStartActivityFromFragment(
fragment: Fragment,
intent: Intent,
requestCode: Int,
options: Bundle?
) {
check(requestCode == -1) {
"Starting activity with a requestCode requires a FragmentActivity host"
}
ContextCompat.startActivity(context, intent, options)
}
/**
* Starts a new [IntentSender] from the given fragment.
* See [Activity.startIntentSender].
*/
@Deprecated(
"""Have your FragmentHostCallback implement {@link ActivityResultRegistryOwner}
to allow Fragments to use
{@link Fragment#registerForActivityResult(ActivityResultContract, ActivityResultCallback)}
with {@link StartIntentSenderForResult}. This method will still be called when Fragments
call the deprecated <code>startIntentSenderForResult()</code> method."""
)
@Throws(SendIntentException::class)
open fun onStartIntentSenderFromFragment(
fragment: Fragment,
intent: IntentSender,
requestCode: Int,
fillInIntent: Intent?,
flagsMask: Int,
flagsValues: Int,
extraFlags: Int,
options: Bundle?
) {
check(requestCode == -1) {
"Starting intent sender with a requestCode requires a FragmentActivity host"
}
val activity = checkNotNull(activity) {
"Starting intent sender with a requestCode requires a FragmentActivity host"
}
ActivityCompat.startIntentSenderForResult(
activity, intent, requestCode, fillInIntent,
flagsMask, flagsValues, extraFlags, options
)
}
/**
* Requests permissions from the given fragment.
* See [FragmentActivity.requestPermissions]
*/
@Deprecated(
"""Have your FragmentHostCallback implement {@link ActivityResultRegistryOwner}
to allow Fragments to use
{@link Fragment#registerForActivityResult(ActivityResultContract, ActivityResultCallback)}
with {@link RequestMultiplePermissions}. This method will still be called when Fragments
call the deprecated <code>requestPermissions()</code> method."""
)
open fun onRequestPermissionsFromFragment(
fragment: Fragment,
permissions: Array<String>,
requestCode: Int
) {
}
/**
* Checks whether to show permission rationale UI from a fragment.
* See [FragmentActivity.shouldShowRequestPermissionRationale]
*/
open fun onShouldShowRequestPermissionRationale(permission: String): Boolean {
return false
}
/**
* Return `true` if there are window animations.
*/
open fun onHasWindowAnimations(): Boolean {
return true
}
/**
* Return the window animations.
*/
open fun onGetWindowAnimations(): Int {
return windowAnimations
}
override fun onFindViewById(id: Int): View? {
return null
}
override fun onHasView(): Boolean {
return true
}
}