• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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