Coverage Summary for Class: ActivityLifecycleCallbackExtKt (dev.shreyaspatil.permissionFlow.utils)

Class Method, % Branch, % Line, % Instruction, %
ActivityLifecycleCallbackExtKt 100% (1/1) 100% (1/1) 100% (7/7)
ActivityLifecycleCallbackExtKt$activityForegroundEventFlow$1 100% (1/1) 100% (3/3) 100% (27/27)
ActivityLifecycleCallbackExtKt$activityForegroundEventFlow$1$1 100% (1/1) 100% (1/1) 100% (6/6)
ActivityLifecycleCallbackExtKt$activityForegroundEventFlow$1$callback$1 66.7% (6/9) 87.5% (14/16) 82.4% (14/17) 100% (74/74)
Total 75% (9/12) 87.5% (14/16) 86.4% (19/22) 100% (114/114)


1 /** 2  * Copyright 2022 Shreyas Patil 3  * 4  * Licensed under the Apache License, Version 2.0 (the "License"); 5  * you may not use this file except in compliance with the License. 6  * You may obtain a copy of the License at 7  * 8  * http://www.apache.org/licenses/LICENSE-2.0 9  * 10  * Unless required by applicable law or agreed to in writing, software 11  * distributed under the License is distributed on an "AS IS" BASIS, 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13  * See the License for the specific language governing permissions and 14  * limitations under the License. 15  */ 16 package dev.shreyaspatil.permissionFlow.utils 17  18 import android.app.Activity 19 import android.app.Application 20 import android.os.Build 21 import android.os.Bundle 22 import androidx.annotation.RequiresApi 23 import kotlinx.coroutines.channels.awaitClose 24 import kotlinx.coroutines.flow.callbackFlow 25  26 /** 27  * A flow which gives callback whenever any activity is started withing application (without 28  * configuration change) or any activity is resumed after being in multi-window or 29  * picture-in-picture mode. 30  */ 31 internal val Application.activityForegroundEventFlow 32  get() = callbackFlow { 33  val callback = object : Application.ActivityLifecycleCallbacks { 34  private var isActivityChangingConfigurations: Boolean? = null 35  private var wasInMultiWindowMode: Boolean? = null 36  private var wasInPictureInPictureMode: Boolean? = null 37  38  /** 39  * Whenever activity receives onStart() lifecycle callback, emit foreground event only 40  * when activity hasn't changed configurations. 41  */ 42  override fun onActivityStarted(activity: Activity) { 43  if (isActivityChangingConfigurations == false) { 44  trySend(Unit) 45  } 46  } 47  48  override fun onActivityStopped(activity: Activity) { 49  isActivityChangingConfigurations = activity.isChangingConfigurations 50  } 51  52  override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} 53  54  /** 55  * Whenever application is resized after being in in PiP or multi-window mode, or exits 56  * from these modes, onResumed() lifecycle callback is triggered. 57  * 58  * Here we assume that user has changed permission from app settings after being in 59  * PiP or multi-window mode. So whenever these modes are exited, emit foreground event. 60  */ 61  override fun onActivityResumed(activity: Activity) { 62  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 63  if (isActivityResumedAfterMultiWindowOrPiPMode(activity)) { 64  trySend(Unit) 65  } 66  wasInMultiWindowMode = activity.isInMultiWindowMode 67  wasInPictureInPictureMode = activity.isInPictureInPictureMode 68  } 69  } 70  71  /** 72  * Whenever application is launched in PiP or multi-window mode, onPaused() lifecycle 73  * callback is triggered. 74  */ 75  override fun onActivityPaused(activity: Activity) { 76  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 77  wasInMultiWindowMode = activity.isInMultiWindowMode 78  wasInPictureInPictureMode = activity.isInPictureInPictureMode 79  } 80  } 81  82  override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} 83  override fun onActivityDestroyed(activity: Activity) {} 84  85  /** 86  * Returns whether [activity] was previously in multi-window mode or PiP mode. 87  */ 88  @RequiresApi(Build.VERSION_CODES.N) 89  private fun isActivityResumedAfterMultiWindowOrPiPMode(activity: Activity) = 90  (wasInMultiWindowMode == true && !activity.isInMultiWindowMode) || 91  (wasInPictureInPictureMode == true && !activity.isInPictureInPictureMode) 92  } 93  94  registerActivityLifecycleCallbacks(callback) 95  96  awaitClose { 97  // Cleanup 98  unregisterActivityLifecycleCallbacks(callback) 99  } 100  }