1 /* 2 * Copyright (C) 2022 The Android Open Source Project 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 com.android.settingslib.core.instrumentation 17 18 import android.view.View 19 import androidx.annotation.VisibleForTesting 20 import androidx.preference.PreferenceGroupAdapter 21 import androidx.preference.SwitchPreference 22 import androidx.recyclerview.widget.RecyclerView 23 import com.android.internal.jank.InteractionJankMonitor 24 import java.util.concurrent.Executors 25 import java.util.concurrent.TimeUnit 26 27 /** 28 * Helper class for Settings library to trace jank. 29 */ 30 object SettingsJankMonitor { 31 private val jankMonitor = InteractionJankMonitor.getInstance() 32 private val scheduledExecutorService = Executors.newSingleThreadScheduledExecutor() 33 34 // Switch toggle animation duration is 250ms, and there is also a ripple effect animation when 35 // clicks, which duration is variable. Use 300ms here to cover. 36 @VisibleForTesting 37 const val MONITORED_ANIMATION_DURATION_MS = 300L 38 39 /** 40 * Detects the jank when click on a SwitchPreference. 41 * 42 * @param recyclerView the recyclerView contains the preference 43 * @param preference the clicked preference 44 */ 45 @JvmStatic 46 fun detectSwitchPreferenceClickJank(recyclerView: RecyclerView, preference: SwitchPreference) { 47 val adapter = recyclerView.adapter as? PreferenceGroupAdapter ?: return 48 val adapterPosition = adapter.getPreferenceAdapterPosition(preference) 49 val viewHolder = recyclerView.findViewHolderForAdapterPosition(adapterPosition) ?: return 50 detectToggleJank(preference.key, viewHolder.itemView) 51 } 52 53 /** 54 * Detects the animation jank on the given view. 55 * 56 * @param tag the tag for jank monitor 57 * @param view the instrumented view 58 */ 59 @JvmStatic 60 fun detectToggleJank(tag: String?, view: View) { 61 val builder = InteractionJankMonitor.Configuration.Builder.withView( 62 InteractionJankMonitor.CUJ_SETTINGS_TOGGLE, 63 view 64 ) 65 if (tag != null) { 66 builder.setTag(tag) 67 } 68 if (jankMonitor.begin(builder)) { 69 scheduledExecutorService.schedule({ 70 jankMonitor.end(InteractionJankMonitor.CUJ_SETTINGS_TOGGLE) 71 }, MONITORED_ANIMATION_DURATION_MS, TimeUnit.MILLISECONDS) 72 } 73 } 74 }