Loading src/com/android/settings/spa/SettingsSpaEnvironment.kt +2 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider import com.android.settings.spa.app.specialaccess.UseFullScreenIntentAppListProvider import com.android.settings.spa.core.instrumentation.SpaLogProvider import com.android.settings.spa.development.UsageStatsPageProvider import com.android.settings.spa.home.HomePageProvider import com.android.settings.spa.network.NetworkAndInternetPageProvider Loading Loading @@ -87,4 +88,5 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) { ), ) } override val logger = SpaLogProvider } src/com/android/settings/spa/SpaActivity.kt +21 −0 Original line number Diff line number Diff line Loading @@ -16,18 +16,29 @@ package com.android.settings.spa import android.app.ActivityManager import android.content.Context import android.content.Intent import android.os.RemoteException import android.os.UserHandle import android.util.Log import com.android.settingslib.spa.framework.BrowseActivity import com.android.settingslib.spa.framework.util.SESSION_BROWSE import com.android.settingslib.spa.framework.util.SESSION_EXTERNAL import com.android.settingslib.spa.framework.util.appendSpaParams class SpaActivity : BrowseActivity() { companion object { private const val TAG = "SpaActivity" @JvmStatic fun Context.startSpaActivity(destination: String) { val intent = Intent(this, SpaActivity::class.java) .appendSpaParams(destination = destination) if (isLaunchedFromInternal()) { intent.appendSpaParams(sessionName = SESSION_BROWSE) } else { intent.appendSpaParams(sessionName = SESSION_EXTERNAL) } startActivity(intent) } Loading @@ -37,5 +48,15 @@ class SpaActivity : BrowseActivity() { startSpaActivity("$destinationPrefix/$packageName/${UserHandle.myUserId()}") return true } fun Context.isLaunchedFromInternal(): Boolean { var pkg: String? = null try { pkg = ActivityManager.getService().getLaunchedFromPackage(getActivityToken()) } catch (e: RemoteException) { Log.v(TAG, "Could not talk to activity manager.", e) } return applicationContext.packageName == pkg } } } src/com/android/settings/spa/core/instrumentation/MetricsDataModel.kt 0 → 100644 +41 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 com.android.settings.spa.core.instrumentation import androidx.annotation.VisibleForTesting /** * This class stores some metrics temporary data. Such as the timestamp of the page enter for * calculating the duration time on page. */ class MetricsDataModel { @VisibleForTesting val pageTimeStampList = mutableListOf<PageTimeStamp>() fun addTimeStamp(dataItem: PageTimeStamp){ pageTimeStampList.add(dataItem) } fun getPageDuration(pageId: String, removed: Boolean = true): String { val lastItem = pageTimeStampList.findLast { it.pageId == pageId } if (removed && lastItem != null) { pageTimeStampList.remove(lastItem) } return if (lastItem == null) "0" else (System.currentTimeMillis() - lastItem.timeStamp).toString() } } src/com/android/settings/spa/core/instrumentation/SpaLogProvider.kt 0 → 100644 +132 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 com.android.settings.spa.core.instrumentation import android.app.settings.SettingsEnums import android.os.Bundle import androidx.annotation.VisibleForTesting import com.android.settings.core.instrumentation.ElapsedTimeUtils import com.android.settings.core.instrumentation.SettingsStatsLog import com.android.settingslib.spa.framework.common.LOG_DATA_SESSION_NAME import com.android.settingslib.spa.framework.common.LogCategory import com.android.settingslib.spa.framework.common.LogEvent import com.android.settingslib.spa.framework.common.SpaLogger import com.android.settingslib.spa.framework.util.SESSION_BROWSE import com.android.settingslib.spa.framework.util.SESSION_EXTERNAL import com.android.settingslib.spa.framework.util.SESSION_SEARCH import com.android.settingslib.spa.framework.util.SESSION_SLICE import com.android.settingslib.spa.framework.util.SESSION_UNKNOWN /** * To receive the events from spa framework and logging the these events. */ object SpaLogProvider : SpaLogger { private val dataModel = MetricsDataModel() override fun event(id: String, event: LogEvent, category: LogCategory, extraData: Bundle) { when(event) { LogEvent.PAGE_ENTER, LogEvent.PAGE_LEAVE -> write(SpaLogData(id, event, extraData, dataModel)) else -> return //TODO(b/253979024): Will be implemented in subsequent CLs. } } private fun write(data: SpaLogData) { with(data) { SettingsStatsLog.write( SettingsStatsLog.SETTINGS_SPA_REPORTED /* atomName */, getSessionType(), getPageId(), getTarget(), getAction(), getKey(), getValue(), getPreValue(), getElapsedTime() ) } } } @VisibleForTesting class SpaLogData(val id: String, val event: LogEvent, val extraData: Bundle, val dataModel: MetricsDataModel) { fun getSessionType(): Int { if (!extraData.containsKey(LOG_DATA_SESSION_NAME)) { return SettingsEnums.SESSION_UNKNOWN } val sessionSource = extraData.getString(LOG_DATA_SESSION_NAME) return when(sessionSource) { SESSION_BROWSE -> SettingsEnums.BROWSE SESSION_SEARCH -> SettingsEnums.SEARCH SESSION_SLICE -> SettingsEnums.SLICE_TYPE SESSION_EXTERNAL -> SettingsEnums.EXTERNAL else -> SettingsEnums.SESSION_UNKNOWN } } fun getPageId(): String { return when(event) { LogEvent.PAGE_ENTER, LogEvent.PAGE_LEAVE -> id else -> getPageIdByEntryId(id) } } //TODO(b/253979024): Will be implemented in subsequent CLs. fun getTarget(): String? { return null } fun getAction(): Int { return event.action } //TODO(b/253979024): Will be implemented in subsequent CLs. fun getKey(): String? { return null } fun getValue(): String? { when(event) { LogEvent.PAGE_ENTER -> dataModel.addTimeStamp( PageTimeStamp(id, System.currentTimeMillis())) LogEvent.PAGE_LEAVE -> return dataModel.getPageDuration(id) else -> {} //TODO(b/253979024): Will be implemented in subsequent CLs. } return null } //TODO(b/253979024): Will be implemented in subsequent CLs. fun getPreValue(): String? { return null } fun getElapsedTime(): Long { return ElapsedTimeUtils.getElapsedTime(System.currentTimeMillis()) } //TODO(b/253979024): Will be implemented in subsequent CLs. private fun getPageIdByEntryId(id: String): String { return "" } } /** * The buffer is keeping the time stamp while spa page entering. */ data class PageTimeStamp(val pageId: String, val timeStamp: Long) tests/spa_unit/src/com/android/settings/spa/SpaActivityTest.kt +9 −1 Original line number Diff line number Diff line Loading @@ -25,12 +25,15 @@ import com.android.settings.spa.SpaActivity.Companion.startSpaActivity import com.android.settings.spa.SpaActivity.Companion.startSpaActivityForApp import com.android.settingslib.spa.framework.util.KEY_DESTINATION import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Answers import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule Loading @@ -39,9 +42,14 @@ class SpaActivityTest { @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() @Mock @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var context: Context @Before fun setUp() { `when`(context.applicationContext.packageName).thenReturn("com.android.settings") } @Test fun startSpaActivity() { context.startSpaActivity(DESTINATION) Loading Loading
src/com/android/settings/spa/SettingsSpaEnvironment.kt +2 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider import com.android.settings.spa.app.specialaccess.UseFullScreenIntentAppListProvider import com.android.settings.spa.core.instrumentation.SpaLogProvider import com.android.settings.spa.development.UsageStatsPageProvider import com.android.settings.spa.home.HomePageProvider import com.android.settings.spa.network.NetworkAndInternetPageProvider Loading Loading @@ -87,4 +88,5 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) { ), ) } override val logger = SpaLogProvider }
src/com/android/settings/spa/SpaActivity.kt +21 −0 Original line number Diff line number Diff line Loading @@ -16,18 +16,29 @@ package com.android.settings.spa import android.app.ActivityManager import android.content.Context import android.content.Intent import android.os.RemoteException import android.os.UserHandle import android.util.Log import com.android.settingslib.spa.framework.BrowseActivity import com.android.settingslib.spa.framework.util.SESSION_BROWSE import com.android.settingslib.spa.framework.util.SESSION_EXTERNAL import com.android.settingslib.spa.framework.util.appendSpaParams class SpaActivity : BrowseActivity() { companion object { private const val TAG = "SpaActivity" @JvmStatic fun Context.startSpaActivity(destination: String) { val intent = Intent(this, SpaActivity::class.java) .appendSpaParams(destination = destination) if (isLaunchedFromInternal()) { intent.appendSpaParams(sessionName = SESSION_BROWSE) } else { intent.appendSpaParams(sessionName = SESSION_EXTERNAL) } startActivity(intent) } Loading @@ -37,5 +48,15 @@ class SpaActivity : BrowseActivity() { startSpaActivity("$destinationPrefix/$packageName/${UserHandle.myUserId()}") return true } fun Context.isLaunchedFromInternal(): Boolean { var pkg: String? = null try { pkg = ActivityManager.getService().getLaunchedFromPackage(getActivityToken()) } catch (e: RemoteException) { Log.v(TAG, "Could not talk to activity manager.", e) } return applicationContext.packageName == pkg } } }
src/com/android/settings/spa/core/instrumentation/MetricsDataModel.kt 0 → 100644 +41 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 com.android.settings.spa.core.instrumentation import androidx.annotation.VisibleForTesting /** * This class stores some metrics temporary data. Such as the timestamp of the page enter for * calculating the duration time on page. */ class MetricsDataModel { @VisibleForTesting val pageTimeStampList = mutableListOf<PageTimeStamp>() fun addTimeStamp(dataItem: PageTimeStamp){ pageTimeStampList.add(dataItem) } fun getPageDuration(pageId: String, removed: Boolean = true): String { val lastItem = pageTimeStampList.findLast { it.pageId == pageId } if (removed && lastItem != null) { pageTimeStampList.remove(lastItem) } return if (lastItem == null) "0" else (System.currentTimeMillis() - lastItem.timeStamp).toString() } }
src/com/android/settings/spa/core/instrumentation/SpaLogProvider.kt 0 → 100644 +132 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 com.android.settings.spa.core.instrumentation import android.app.settings.SettingsEnums import android.os.Bundle import androidx.annotation.VisibleForTesting import com.android.settings.core.instrumentation.ElapsedTimeUtils import com.android.settings.core.instrumentation.SettingsStatsLog import com.android.settingslib.spa.framework.common.LOG_DATA_SESSION_NAME import com.android.settingslib.spa.framework.common.LogCategory import com.android.settingslib.spa.framework.common.LogEvent import com.android.settingslib.spa.framework.common.SpaLogger import com.android.settingslib.spa.framework.util.SESSION_BROWSE import com.android.settingslib.spa.framework.util.SESSION_EXTERNAL import com.android.settingslib.spa.framework.util.SESSION_SEARCH import com.android.settingslib.spa.framework.util.SESSION_SLICE import com.android.settingslib.spa.framework.util.SESSION_UNKNOWN /** * To receive the events from spa framework and logging the these events. */ object SpaLogProvider : SpaLogger { private val dataModel = MetricsDataModel() override fun event(id: String, event: LogEvent, category: LogCategory, extraData: Bundle) { when(event) { LogEvent.PAGE_ENTER, LogEvent.PAGE_LEAVE -> write(SpaLogData(id, event, extraData, dataModel)) else -> return //TODO(b/253979024): Will be implemented in subsequent CLs. } } private fun write(data: SpaLogData) { with(data) { SettingsStatsLog.write( SettingsStatsLog.SETTINGS_SPA_REPORTED /* atomName */, getSessionType(), getPageId(), getTarget(), getAction(), getKey(), getValue(), getPreValue(), getElapsedTime() ) } } } @VisibleForTesting class SpaLogData(val id: String, val event: LogEvent, val extraData: Bundle, val dataModel: MetricsDataModel) { fun getSessionType(): Int { if (!extraData.containsKey(LOG_DATA_SESSION_NAME)) { return SettingsEnums.SESSION_UNKNOWN } val sessionSource = extraData.getString(LOG_DATA_SESSION_NAME) return when(sessionSource) { SESSION_BROWSE -> SettingsEnums.BROWSE SESSION_SEARCH -> SettingsEnums.SEARCH SESSION_SLICE -> SettingsEnums.SLICE_TYPE SESSION_EXTERNAL -> SettingsEnums.EXTERNAL else -> SettingsEnums.SESSION_UNKNOWN } } fun getPageId(): String { return when(event) { LogEvent.PAGE_ENTER, LogEvent.PAGE_LEAVE -> id else -> getPageIdByEntryId(id) } } //TODO(b/253979024): Will be implemented in subsequent CLs. fun getTarget(): String? { return null } fun getAction(): Int { return event.action } //TODO(b/253979024): Will be implemented in subsequent CLs. fun getKey(): String? { return null } fun getValue(): String? { when(event) { LogEvent.PAGE_ENTER -> dataModel.addTimeStamp( PageTimeStamp(id, System.currentTimeMillis())) LogEvent.PAGE_LEAVE -> return dataModel.getPageDuration(id) else -> {} //TODO(b/253979024): Will be implemented in subsequent CLs. } return null } //TODO(b/253979024): Will be implemented in subsequent CLs. fun getPreValue(): String? { return null } fun getElapsedTime(): Long { return ElapsedTimeUtils.getElapsedTime(System.currentTimeMillis()) } //TODO(b/253979024): Will be implemented in subsequent CLs. private fun getPageIdByEntryId(id: String): String { return "" } } /** * The buffer is keeping the time stamp while spa page entering. */ data class PageTimeStamp(val pageId: String, val timeStamp: Long)
tests/spa_unit/src/com/android/settings/spa/SpaActivityTest.kt +9 −1 Original line number Diff line number Diff line Loading @@ -25,12 +25,15 @@ import com.android.settings.spa.SpaActivity.Companion.startSpaActivity import com.android.settings.spa.SpaActivity.Companion.startSpaActivityForApp import com.android.settingslib.spa.framework.util.KEY_DESTINATION import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Answers import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule Loading @@ -39,9 +42,14 @@ class SpaActivityTest { @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() @Mock @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var context: Context @Before fun setUp() { `when`(context.applicationContext.packageName).thenReturn("com.android.settings") } @Test fun startSpaActivity() { context.startSpaActivity(DESTINATION) Loading