1 /* 2 * Copyright (C) 2018 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 17 package com.google.android.setupcompat.internal; 18 19 import static java.util.concurrent.TimeUnit.NANOSECONDS; 20 21 import android.app.Activity; 22 import android.app.Fragment; 23 import android.app.FragmentManager; 24 import android.content.Context; 25 import android.os.Build.VERSION; 26 import android.os.Build.VERSION_CODES; 27 import android.os.PersistableBundle; 28 import android.util.Log; 29 import com.google.android.setupcompat.logging.CustomEvent; 30 import com.google.android.setupcompat.logging.MetricKey; 31 import com.google.android.setupcompat.logging.SetupMetricsLogger; 32 import com.google.android.setupcompat.util.WizardManagerHelper; 33 34 /** Fragment used to detect lifecycle of an activity for metrics logging. */ 35 public class LifecycleFragment extends Fragment { 36 private static final String LOG_TAG = LifecycleFragment.class.getSimpleName(); 37 private static final String FRAGMENT_ID = "lifecycle_monitor"; 38 39 private MetricKey metricKey; 40 private long startInNanos; 41 private long durationInNanos = 0; 42 LifecycleFragment()43 public LifecycleFragment() { 44 setRetainInstance(true); 45 } 46 47 /** 48 * Attaches the lifecycle fragment if it is not attached yet. 49 * 50 * @param activity the activity to detect lifecycle for. 51 * @return fragment to monitor life cycle. 52 */ attachNow(Activity activity)53 public static LifecycleFragment attachNow(Activity activity) { 54 if (WizardManagerHelper.isAnySetupWizard(activity.getIntent())) { 55 SetupCompatServiceInvoker.get(activity.getApplicationContext()) 56 .bindBack( 57 LayoutBindBackHelper.getScreenName(activity), 58 LayoutBindBackHelper.getExtraBundle(activity)); 59 60 if (VERSION.SDK_INT > VERSION_CODES.M) { 61 FragmentManager fragmentManager = activity.getFragmentManager(); 62 if (fragmentManager != null && !fragmentManager.isDestroyed()) { 63 Fragment fragment = fragmentManager.findFragmentByTag(FRAGMENT_ID); 64 if (fragment == null) { 65 LifecycleFragment lifeCycleFragment = new LifecycleFragment(); 66 try { 67 fragmentManager.beginTransaction().add(lifeCycleFragment, FRAGMENT_ID).commitNow(); 68 fragment = lifeCycleFragment; 69 } catch (IllegalStateException e) { 70 Log.e( 71 LOG_TAG, 72 "Error occurred when attach to Activity:" + activity.getComponentName(), 73 e); 74 } 75 } else if (!(fragment instanceof LifecycleFragment)) { 76 Log.wtf( 77 LOG_TAG, 78 activity.getClass().getSimpleName() + " Incorrect instance on lifecycle fragment."); 79 return null; 80 } 81 return (LifecycleFragment) fragment; 82 } 83 } 84 } 85 86 return null; 87 } 88 89 @Override onAttach(Context context)90 public void onAttach(Context context) { 91 super.onAttach(context); 92 metricKey = MetricKey.get("ScreenDuration", getActivity()); 93 } 94 95 @Override onDetach()96 public void onDetach() { 97 super.onDetach(); 98 SetupMetricsLogger.logDuration(getActivity(), metricKey, NANOSECONDS.toMillis(durationInNanos)); 99 } 100 101 @Override onResume()102 public void onResume() { 103 super.onResume(); 104 startInNanos = ClockProvider.timeInNanos(); 105 logScreenResume(); 106 } 107 108 @Override onPause()109 public void onPause() { 110 super.onPause(); 111 durationInNanos += (ClockProvider.timeInNanos() - startInNanos); 112 } 113 logScreenResume()114 private void logScreenResume() { 115 if (VERSION.SDK_INT >= VERSION_CODES.Q) { 116 PersistableBundle bundle = new PersistableBundle(); 117 bundle.putLong("onScreenResume", System.nanoTime()); 118 SetupMetricsLogger.logCustomEvent( 119 getActivity(), 120 CustomEvent.create(MetricKey.get("ScreenActivity", getActivity()), bundle)); 121 } 122 } 123 } 124