1 /* 2 * Copyright (C) 2020 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.android.systemui.util; 18 19 import android.content.Context; 20 import android.content.res.Resources; 21 import android.view.View; 22 import android.view.View.OnAttachStateChangeListener; 23 24 /** 25 * Utility class that handles view lifecycle events for View Controllers. 26 * 27 * Implementations should handle setup and teardown related activities inside of 28 * {@link #onViewAttached()} and {@link #onViewDetached()}. Be sure to call {@link #init()} on 29 * any child controllers that this uses. This can be done in {@link #onInit()} if the 30 * controllers are injected, or right after creation time of the child controller. 31 * 32 * Tip: View "attachment" happens top down - parents are notified that they are attached before 33 * any children. That means that if you call a method on a child controller in 34 * {@link #onViewAttached()}, the child controller may not have had its onViewAttach method 35 * called, so it may not be fully set up. 36 * 37 * As such, make sure that methods on your controller are safe to call _before_ its {@link #init()} 38 * and {@link #onViewAttached()} methods are called. Specifically, if your controller must call 39 * {@link View#findViewById(int)} on its root view to setup member variables, do so in its 40 * constructor. Save {@link #onViewAttached()} for things that can happen post-construction - adding 41 * listeners, dynamically changing content, or other runtime decisions. 42 * 43 * @param <T> View class that this ViewController is for. 44 */ 45 public abstract class ViewController<T extends View> { 46 protected final T mView; 47 private boolean mInited; 48 49 private OnAttachStateChangeListener mOnAttachStateListener = new OnAttachStateChangeListener() { 50 @Override 51 public void onViewAttachedToWindow(View v) { 52 ViewController.this.onViewAttached(); 53 } 54 55 @Override 56 public void onViewDetachedFromWindow(View v) { 57 ViewController.this.onViewDetached(); 58 } 59 }; 60 ViewController(T view)61 protected ViewController(T view) { 62 mView = view; 63 } 64 65 /** 66 * Call immediately after constructing Controller in order to handle view lifecycle events. 67 * 68 * Generally speaking, you don't want to override this method. Instead, override 69 * {@link #onInit()} as a way to have an run-once idempotent method that you can use for 70 * setup of your ViewController. 71 */ init()72 public void init() { 73 if (mInited) { 74 return; 75 } 76 onInit(); 77 mInited = true; 78 79 if (isAttachedToWindow()) { 80 mOnAttachStateListener.onViewAttachedToWindow(mView); 81 } 82 addOnAttachStateChangeListener(mOnAttachStateListener); 83 } 84 85 /** 86 * Run once when {@link #init()} is called. 87 * 88 * Override this to perform idempotent, one-time setup that your controller needs. It will 89 * be called before {@link #onViewAttached()}. 90 */ onInit()91 protected void onInit() {} 92 getContext()93 protected Context getContext() { 94 return mView.getContext(); 95 } 96 getResources()97 protected Resources getResources() { 98 return mView.getResources(); 99 } 100 isAttachedToWindow()101 public boolean isAttachedToWindow() { 102 return mView != null && mView.isAttachedToWindow(); 103 } 104 105 /** Add an OnAttachStateListener to the view. Does nothing if the view is null. */ addOnAttachStateChangeListener(View.OnAttachStateChangeListener listener)106 public void addOnAttachStateChangeListener(View.OnAttachStateChangeListener listener) { 107 if (mView != null) { 108 mView.addOnAttachStateChangeListener(listener); 109 } 110 } 111 112 /** 113 * Called when the view is attached and a call to {@link #init()} has been made in either order. 114 */ onViewAttached()115 protected abstract void onViewAttached(); 116 117 /** 118 * Called when the view is detached. 119 */ onViewDetached()120 protected abstract void onViewDetached(); 121 122 } 123